Overview
The @grcorsair/sdk package provides a typed TypeScript client for programmatic access to Corsair's signing and verification capabilities. It wraps the core engines into a clean API surface with full type exports.
Status: The SDK is currently available only inside the Corsair monorepo.
Installation
The SDK is monorepo-only for now. Clone the repo and use it as a workspace:
git clone https://github.com/grcorsair/corsair.git
cd corsair
bun install
If you need a packaged dependency today, use the CLI or API instead.
Configuration
import { CorsairClient } from "@grcorsair/sdk";
const corsair = new CorsairClient({
keyDir: "./keys", // Directory for Ed25519 signing keys
did: "did:web:acme.com", // Default issuer DID
});
Both fields are optional. If keyDir is omitted, the client uses the default key location. If did is omitted, individual operations can specify it.
| Option | Type | Description |
|---|---|---|
keyDir | string | Directory where Ed25519 signing keys are stored |
did | string | Default issuer DID (e.g., "did:web:acme.com") |
Key Generation
Generate an Ed25519 keypair for CPOE signing:
const keys = await corsair.keygen("./keys");
console.log(keys.publicKey); // PEM-encoded public key
console.log(keys.privateKey); // PEM-encoded private key
If no output directory is provided, keys are stored in the configured keyDir.
Sign Evidence
Sign structured evidence as a CPOE (JWT-VC with Ed25519):
import { readFileSync } from "fs";
const evidence = JSON.parse(readFileSync("evidence.json", "utf-8"));
const result = await corsair.sign(evidence, {
// format: "generic", // Optional: bypass mapping packs
did: "did:web:acme.com", // Override issuer DID
source: "tool", // Override provenance source
scope: "AWS Production", // Assessment scope
expiryDays: 90, // CPOE validity (default: 90)
dryRun: false, // Set true to preview without signing
sdJwt: false, // Enable SD-JWT selective disclosure
sdFields: ["summary"], // Fields to disclose when sdJwt=true
});
console.log(result.jwt); // Signed JWT-VC string
console.log(result.marqueId); // CPOE identifier
console.log(result.detectedFormat); // "mapping-pack" or "generic"
console.log(result.summary); // { controlsTested, controlsPassed, controlsFailed, overallScore }
console.log(result.provenance); // { source: "tool", sourceIdentity: "Scanner v1" }
console.log(result.warnings); // Any warnings from parsing
console.log(result.extensions); // Optional passthrough fields + mapping metadata
SignOptions
| Option | Type | Default | Description |
|---|---|---|---|
format | EvidenceFormat | auto-detect | Force generic format (bypass mapping packs) |
did | string | config default | Override issuer DID |
source | DocumentSource | from evidence | Override provenance source |
scope | string | from evidence | Override scope string |
expiryDays | number | 90 | CPOE validity in days |
dryRun | boolean | false | Parse without signing |
sdJwt | boolean | false | Enable SD-JWT selective disclosure |
sdFields | string[] | ["summary","frameworks"] | Disclosable credentialSubject fields |
Supported Formats
Call corsair.formats() to get the full list:
const formats = corsair.formats();
// ["mapping-pack", "generic"]
For tool outputs not on the built-in list, use the mapping registry on the signing host (see CORSAIR_MAPPING_DIR / CORSAIR_MAPPING_FILE or corsair mappings add). Build packs with corsair mappings pack and sign them with corsair mappings sign. Evidence-only mappings still produce a valid CPOE and surface passthrough fields under extensions. Mappings are evaluated by priority (higher wins), then filename order. Signed packs are verified when CORSAIR_MAPPING_PACK_PUBKEY is set.
Verify CPOEs
Verify a CPOE signature and structure:
const result = await corsair.verify(jwtString);
if (result.valid) {
console.log("Issuer:", result.signedBy);
console.log("Trust tier:", result.issuerTier); // "corsair-verified" | "self-signed" | "unverifiable" | "invalid"
console.log("Scope:", result.scope);
console.log("Score:", result.summary?.overallScore);
console.log("Provenance:", result.provenance?.source);
console.log("Expires:", result.expiresAt);
} else {
console.log("Invalid:", result.reason);
// "signature_invalid" | "expired" | "schema_invalid" | "evidence_mismatch"
}
VerifyResult
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the CPOE passed verification |
reason | string | Failure reason (only when invalid) |
signedBy | string | Issuer DID |
issuerTier | string | Trust tier: corsair-verified, self-signed, unverifiable, invalid |
scope | string | Assessment scope |
summary | object | Controls tested/passed/failed and overall score |
provenance | object | Source, identity, and date |
generatedAt | string | Issuance timestamp |
expiresAt | string | Expiry timestamp |
extensions | object | Optional passthrough fields and mapping metadata |
TypeScript Types
All types are exported from the package for type-safe integration:
import type {
// SDK types
CorsairClientConfig,
SignOptions,
SignResult,
VerifyResult,
// Evidence format
EvidenceFormat,
// Canonical evidence types
CanonicalControlEvidence,
CanonicalStatus, // "pass" | "fail" | "skip" | "error"
CanonicalSeverity, // "critical" | "high" | "medium" | "low" | "info"
EvidenceType, // "config" | "scan" | "test" | "observation" | "attestation" | "document"
ProvenanceSource, // "self" | "tool" | "auditor"
NormalizedEvidence,
// Verification types
MarqueVerificationResult,
} from "@grcorsair/sdk";
Complete Example
End-to-end workflow: generate keys, sign evidence, and verify the output:
import { CorsairClient } from "@grcorsair/sdk";
import { readFileSync } from "fs";
// Initialize
const corsair = new CorsairClient({ keyDir: "./keys", did: "did:web:acme.com" });
// Generate keys (first time only)
await corsair.keygen();
// Load evidence
const evidence = JSON.parse(readFileSync("tool-output.json", "utf-8"));
// Sign
const signed = await corsair.sign(evidence, { scope: "AWS Production" });
console.log(`Signed CPOE: ${signed.marqueId} (${signed.summary.overallScore}/100)`);
// Verify the signed CPOE
const verification = await corsair.verify(signed.jwt);
console.log(`Valid: ${verification.valid}, Tier: ${verification.issuerTier}`);