@agenticprimitives/verifiable-credentials 0.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/canonical.d.ts +34 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +146 -0
- package/dist/canonical.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/proof.d.ts +90 -0
- package/dist/proof.d.ts.map +1 -0
- package/dist/proof.js +127 -0
- package/dist/proof.js.map +1 -0
- package/dist/schema.d.ts +35 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +61 -0
- package/dist/schema.js.map +1 -0
- package/dist/situation.d.ts +48 -0
- package/dist/situation.d.ts.map +1 -0
- package/dist/situation.js +36 -0
- package/dist/situation.js.map +1 -0
- package/dist/types.d.ts +149 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +39 -0
- package/dist/types.js.map +1 -0
- package/dist/verifier.d.ts +90 -0
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +207 -0
- package/dist/verifier.js.map +1 -0
- package/package.json +58 -0
- package/spec.md +5 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema registration helper — produces the `did:shape:<name>:<version>` ↔
|
|
3
|
+
* on-chain `ShapeRegistry.defineShape(...)` round-trip per PD-12.
|
|
4
|
+
*
|
|
5
|
+
* In W1 this module ships pure helpers (no on-chain call). The caller wires
|
|
6
|
+
* the actual `defineShape` call against their viem clients; we provide:
|
|
7
|
+
* - the canonical schema URI form (`did:shape:<name>:<version>`)
|
|
8
|
+
* - the canonical hash bytes (`keccak256` of the canonical SHACL string)
|
|
9
|
+
* - a parser to round-trip schema URIs
|
|
10
|
+
*/
|
|
11
|
+
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
12
|
+
import { utf8ToBytes } from '@noble/hashes/utils.js';
|
|
13
|
+
export const SHAPE_DID_PREFIX = 'did:shape:';
|
|
14
|
+
/**
|
|
15
|
+
* Build the canonical `did:shape:<name>:<version>` URI.
|
|
16
|
+
*
|
|
17
|
+
* @throws if name or version contains characters outside `[A-Za-z0-9-_.]`.
|
|
18
|
+
*/
|
|
19
|
+
export function buildShapeUri(name, version) {
|
|
20
|
+
if (!/^[A-Za-z0-9._-]+$/.test(name)) {
|
|
21
|
+
throw new Error(`[verifiable-credentials/schema] invalid shape name: ${name}`);
|
|
22
|
+
}
|
|
23
|
+
if (!/^[A-Za-z0-9._-]+$/.test(version)) {
|
|
24
|
+
throw new Error(`[verifiable-credentials/schema] invalid shape version: ${version}`);
|
|
25
|
+
}
|
|
26
|
+
return `${SHAPE_DID_PREFIX}${name}:${version}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse a `did:shape:<name>:<version>` URI.
|
|
30
|
+
*/
|
|
31
|
+
export function parseShapeUri(uri) {
|
|
32
|
+
if (!uri.startsWith(SHAPE_DID_PREFIX))
|
|
33
|
+
return null;
|
|
34
|
+
const body = uri.slice(SHAPE_DID_PREFIX.length);
|
|
35
|
+
const colonIdx = body.lastIndexOf(':');
|
|
36
|
+
if (colonIdx === -1)
|
|
37
|
+
return null;
|
|
38
|
+
const name = body.slice(0, colonIdx);
|
|
39
|
+
const version = body.slice(colonIdx + 1);
|
|
40
|
+
if (!name || !version)
|
|
41
|
+
return null;
|
|
42
|
+
return { name, version };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Canonicalise SHACL bytes for the on-chain hash. The canonical form is:
|
|
46
|
+
* - UTF-8 normalised (callers are responsible for upstream NFC if needed)
|
|
47
|
+
* - keccak256 of the raw bytes
|
|
48
|
+
*
|
|
49
|
+
* This matches the on-chain `ShapeRegistry.defineShape(shapeHash)` expectation
|
|
50
|
+
* — both sides hash the exact same SHACL string.
|
|
51
|
+
*/
|
|
52
|
+
export function shapeHash(shaclBytes) {
|
|
53
|
+
const bytes = typeof shaclBytes === 'string' ? utf8ToBytes(shaclBytes) : shaclBytes;
|
|
54
|
+
const digest = keccak_256(bytes);
|
|
55
|
+
let hex = '0x';
|
|
56
|
+
for (const v of digest) {
|
|
57
|
+
hex += v.toString(16).padStart(2, '0');
|
|
58
|
+
}
|
|
59
|
+
return hex;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAIrD,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAqB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAe;IACzD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,uDAAuD,IAAI,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,0DAA0D,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,GAAG,gBAAgB,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,QAAQ,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,UAA+B;IACvD,MAAM,KAAK,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACpF,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,GAAY,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOLCE+DnS Situation pattern — the substrate's typed base for every credential
|
|
3
|
+
* subject. Per spec 242 §5: substrate credentials describe a `Situation` with
|
|
4
|
+
* a `Description`, `Roles`, and `Participants`.
|
|
5
|
+
*
|
|
6
|
+
* Situation — the thing the credential is about (e.g. "this agreement",
|
|
7
|
+
* "this validation result", "this evidence anchor")
|
|
8
|
+
* Description — the typed shape spec the situation conforms to (SHACL IRI)
|
|
9
|
+
* Roles — typed slots filled by participants (e.g. issuer, holder,
|
|
10
|
+
* beneficiary, validator)
|
|
11
|
+
* Participants — the SAs filling each role
|
|
12
|
+
*
|
|
13
|
+
* Owning vocabularies define their specific Situation subclasses; this module
|
|
14
|
+
* exports the substrate base types every consumer composes against.
|
|
15
|
+
*/
|
|
16
|
+
import type { Address } from '@agenticprimitives/types';
|
|
17
|
+
/** Ontology IRI of the Description shape (e.g. `apagr:AgreementCredential`). */
|
|
18
|
+
export type DescriptionRef = `${string}:${string}`;
|
|
19
|
+
/** Role identifier — local name within a Description. */
|
|
20
|
+
export type RoleName = string;
|
|
21
|
+
/** A typed Situation as a credential subject. The `[k: string]: unknown`
|
|
22
|
+
* index signature lets it satisfy `VerifiableCredential`'s `TSubject` constraint
|
|
23
|
+
* while keeping the typed `description / roles / body` fields tight. */
|
|
24
|
+
export interface Situation<TBody extends Record<string, unknown> = Record<string, unknown>> {
|
|
25
|
+
/** The Description shape the situation conforms to. */
|
|
26
|
+
description: DescriptionRef;
|
|
27
|
+
/** Typed slots — `{ roleName → AgentSA }`. */
|
|
28
|
+
roles: Record<RoleName, Address>;
|
|
29
|
+
/** Optional non-Agent participants (e.g. resource IRIs, document hashes). */
|
|
30
|
+
participants?: Record<string, string>;
|
|
31
|
+
/** The situation-specific payload. */
|
|
32
|
+
body: TBody;
|
|
33
|
+
/** Index signature so Situation satisfies VC's TSubject constraint. */
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate that a Situation declares the roles its Description requires.
|
|
38
|
+
* Used by SDK helpers before signing; production validation runs SHACL.
|
|
39
|
+
*/
|
|
40
|
+
export declare function assertSituationRolesPresent(s: Situation, requiredRoles: RoleName[]): void;
|
|
41
|
+
/** Convenience constructor — builds the situation envelope from typed bits. */
|
|
42
|
+
export declare function buildSituation<TBody extends Record<string, unknown>>(args: {
|
|
43
|
+
description: DescriptionRef;
|
|
44
|
+
roles: Record<RoleName, Address>;
|
|
45
|
+
body: TBody;
|
|
46
|
+
participants?: Record<string, string>;
|
|
47
|
+
}): Situation<TBody>;
|
|
48
|
+
//# sourceMappingURL=situation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"situation.d.ts","sourceRoot":"","sources":["../src/situation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAExD,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;AAEnD,yDAAyD;AACzD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B;;yEAEyE;AACzE,MAAM,WAAW,SAAS,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxF,uDAAuD;IACvD,WAAW,EAAE,cAAc,CAAC;IAC5B,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,sCAAsC;IACtC,IAAI,EAAE,KAAK,CAAC;IACZ,uEAAuE;IACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,IAAI,CAQzF;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE;IAC1E,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,KAAK,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,GAAG,SAAS,CAAC,KAAK,CAAC,CAOnB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOLCE+DnS Situation pattern — the substrate's typed base for every credential
|
|
3
|
+
* subject. Per spec 242 §5: substrate credentials describe a `Situation` with
|
|
4
|
+
* a `Description`, `Roles`, and `Participants`.
|
|
5
|
+
*
|
|
6
|
+
* Situation — the thing the credential is about (e.g. "this agreement",
|
|
7
|
+
* "this validation result", "this evidence anchor")
|
|
8
|
+
* Description — the typed shape spec the situation conforms to (SHACL IRI)
|
|
9
|
+
* Roles — typed slots filled by participants (e.g. issuer, holder,
|
|
10
|
+
* beneficiary, validator)
|
|
11
|
+
* Participants — the SAs filling each role
|
|
12
|
+
*
|
|
13
|
+
* Owning vocabularies define their specific Situation subclasses; this module
|
|
14
|
+
* exports the substrate base types every consumer composes against.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Validate that a Situation declares the roles its Description requires.
|
|
18
|
+
* Used by SDK helpers before signing; production validation runs SHACL.
|
|
19
|
+
*/
|
|
20
|
+
export function assertSituationRolesPresent(s, requiredRoles) {
|
|
21
|
+
const have = new Set(Object.keys(s.roles));
|
|
22
|
+
const missing = requiredRoles.filter((r) => !have.has(r));
|
|
23
|
+
if (missing.length > 0) {
|
|
24
|
+
throw new Error(`[verifiable-credentials] Situation '${s.description}' is missing required roles: ${missing.join(', ')}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Convenience constructor — builds the situation envelope from typed bits. */
|
|
28
|
+
export function buildSituation(args) {
|
|
29
|
+
return {
|
|
30
|
+
description: args.description,
|
|
31
|
+
roles: { ...args.roles },
|
|
32
|
+
participants: args.participants ? { ...args.participants } : undefined,
|
|
33
|
+
body: args.body,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=situation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"situation.js","sourceRoot":"","sources":["../src/situation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA0BH;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,CAAY,EAAE,aAAyB;IACjF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,uCAAuC,CAAC,CAAC,WAAW,gCAAgC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAwC,IAKrE;IACC,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE;QACxB,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,SAAS;QACtE,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3C Verifiable Credentials 2.0 envelope types + `Eip712Signature2026` proof
|
|
3
|
+
* type. Substrate for all spine credential classes (Association, Evidence,
|
|
4
|
+
* Outcome, Validation, TrustUpdate, AgreementCredential, PaymentReceipt, ...).
|
|
5
|
+
*
|
|
6
|
+
* Authoritative spec: specs/242-trust-credentials-and-public-assertions.md §4.
|
|
7
|
+
* Architecture-of-record: ADR-0023 + ADR-0024 (composability table).
|
|
8
|
+
*/
|
|
9
|
+
import type { Address, Hex } from '@agenticprimitives/types';
|
|
10
|
+
export type Hex32 = `0x${string}`;
|
|
11
|
+
export type ISODate = string;
|
|
12
|
+
/** W3C VC 2.0 default context. */
|
|
13
|
+
export declare const VC_CONTEXT_V2: "https://www.w3.org/ns/credentials/v2";
|
|
14
|
+
/** Substrate's Eip712Signature2026 context (proof-type alongside W3C VC). */
|
|
15
|
+
export declare const EIP712_SIG_2026_CONTEXT: "https://agenticprimitives.dev/contexts/eip712-signature-2026/v1";
|
|
16
|
+
/**
|
|
17
|
+
* Discriminator for the proof type. Primary in W1; BBS+ / SD-JWT reserved per
|
|
18
|
+
* PD-28 (privacy doc §2.3).
|
|
19
|
+
*/
|
|
20
|
+
export type ProofType = 'Eip712Signature2026' | 'DataIntegrityProof' | 'SdJwtProof';
|
|
21
|
+
/**
|
|
22
|
+
* The primary W1 proof shape — EIP-712 typed-data signature verified via
|
|
23
|
+
* ERC-1271 against the issuer SA.
|
|
24
|
+
*
|
|
25
|
+
* Per ADR-0023:
|
|
26
|
+
* - `verificationMethod` SHOULD be a CAIP-10 reference to the issuer's SA.
|
|
27
|
+
* - `proofPurpose` is `assertionMethod` for issuer-attested credentials,
|
|
28
|
+
* `authentication` for holder-asserted ones.
|
|
29
|
+
*/
|
|
30
|
+
export interface Eip712Signature2026Proof {
|
|
31
|
+
type: 'Eip712Signature2026';
|
|
32
|
+
created: ISODate;
|
|
33
|
+
verificationMethod: string;
|
|
34
|
+
proofPurpose: 'assertionMethod' | 'authentication' | 'capabilityInvocation';
|
|
35
|
+
/** Hex-encoded ERC-1271-verifiable signature. */
|
|
36
|
+
proofValue: Hex;
|
|
37
|
+
/** Optional EIP-712 domain hash for cross-stack reconciliation. */
|
|
38
|
+
eip712Domain?: {
|
|
39
|
+
name: string;
|
|
40
|
+
version: string;
|
|
41
|
+
chainId: number;
|
|
42
|
+
verifyingContract: Address;
|
|
43
|
+
};
|
|
44
|
+
/** Optional canonical-hash receipt of the credential body (RFC 8785 JCS). */
|
|
45
|
+
credentialHash?: Hex32;
|
|
46
|
+
/** Present when an issuer-AUTHORIZED operational key (e.g. Cloud KMS) signed this credential instead of
|
|
47
|
+
* the issuer's own custodian. `proofValue` is then the delegate key's signature; trust still roots in
|
|
48
|
+
* the issuer SA (`delegatorIssuer` == the credential's issuer). Lives in the proof (stripped from the
|
|
49
|
+
* credential hash), so attaching it does NOT change the signed digest. The verifier checks the leaf. */
|
|
50
|
+
delegatingSigner?: DelegatingSignerProof;
|
|
51
|
+
}
|
|
52
|
+
/** Issuer authorization of an operational signing key (mirrors content-primitives' DelegatingSigner). */
|
|
53
|
+
export interface DelegatingSignerProof {
|
|
54
|
+
/** The issuer SA the credential's trust roots in (== the credential issuer). */
|
|
55
|
+
delegatorIssuer: Address;
|
|
56
|
+
/** The operational key that actually signed (authorized by `delegatorIssuer`). */
|
|
57
|
+
delegateKey: Address;
|
|
58
|
+
/** The signed delegation binding `delegateKey` → `delegatorIssuer`. OPAQUE; the app validates it. */
|
|
59
|
+
delegationLeaf: unknown;
|
|
60
|
+
}
|
|
61
|
+
/** Discriminated union for all supported proof types (W1: Eip712Signature2026 only). */
|
|
62
|
+
export type Proof = Eip712Signature2026Proof;
|
|
63
|
+
/** W3C VC StatusList2021 envelope (per `credentialStatus`). */
|
|
64
|
+
export interface CredentialStatus2021 {
|
|
65
|
+
id: string;
|
|
66
|
+
type: 'StatusList2021Entry' | 'RevocationList2020Status';
|
|
67
|
+
statusPurpose?: 'revocation' | 'suspension';
|
|
68
|
+
statusListIndex?: number;
|
|
69
|
+
statusListCredential?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Per privacy doc D-42: every field MAY carry a per-field VisibilityTier
|
|
73
|
+
* override; consumers MAY render a partial VC by tier.
|
|
74
|
+
*/
|
|
75
|
+
export type VisibilityTier = 'Public' | 'PublicCoarse' | 'PrivateCommitment' | 'PrivateZK' | 'OffchainOnly';
|
|
76
|
+
export interface DisclosurePolicy {
|
|
77
|
+
/** JSON-encoded { fieldPath → tier }. */
|
|
78
|
+
fieldDisclosure?: Record<string, VisibilityTier>;
|
|
79
|
+
/** Default tier applied to every unmapped field. */
|
|
80
|
+
defaultTier: VisibilityTier;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The canonical W3C VC 2.0 envelope, parameterised by the credential-subject
|
|
84
|
+
* shape. Implementations of substrate credential types (Association, Evidence,
|
|
85
|
+
* Outcome, Validation, TrustUpdate, AgreementCredential, PaymentReceipt) live
|
|
86
|
+
* in their owning packages; this envelope is substrate-wide.
|
|
87
|
+
*/
|
|
88
|
+
export interface VerifiableCredential<TSubject extends Record<string, unknown> = Record<string, unknown>> {
|
|
89
|
+
'@context': readonly string[];
|
|
90
|
+
/** First element MUST be `'VerifiableCredential'`; subsequent are class names. */
|
|
91
|
+
type: readonly [...string[]];
|
|
92
|
+
/** CAIP-10 reference to the issuer SA (preferred), or a DID. */
|
|
93
|
+
issuer: string;
|
|
94
|
+
/** Issuance time. */
|
|
95
|
+
validFrom: ISODate;
|
|
96
|
+
/** Optional expiry. */
|
|
97
|
+
validUntil?: ISODate;
|
|
98
|
+
/** Credential subject — discriminated by class. */
|
|
99
|
+
credentialSubject: TSubject;
|
|
100
|
+
/** Optional revocation envelope (StatusList2021). */
|
|
101
|
+
credentialStatus?: CredentialStatus2021;
|
|
102
|
+
/** Optional schema pointer — `did:shape:<name>:<version>` (per PD-12 round-trip). */
|
|
103
|
+
credentialSchema?: {
|
|
104
|
+
id: string;
|
|
105
|
+
type: 'JsonSchema' | 'ShaclShape';
|
|
106
|
+
};
|
|
107
|
+
/** Substrate per-field DisclosurePolicy (D-42). */
|
|
108
|
+
disclosurePolicy?: DisclosurePolicy;
|
|
109
|
+
/** The proof (W1: Eip712Signature2026). */
|
|
110
|
+
proof?: Proof;
|
|
111
|
+
}
|
|
112
|
+
/** Convenience alias for a VC without a proof attached (pre-signing). */
|
|
113
|
+
export type UnsignedCredential<TSubject extends Record<string, unknown> = Record<string, unknown>> = Omit<VerifiableCredential<TSubject>, 'proof'>;
|
|
114
|
+
/**
|
|
115
|
+
* Standard EIP-712 domain for the substrate's `Eip712Signature2026` proof.
|
|
116
|
+
*
|
|
117
|
+
* Cross-stack typehash equality: any Solidity verifier MUST compute the same
|
|
118
|
+
* `typeHash` from the same primary type string + same field types. The
|
|
119
|
+
* canonical primary type is `VerifiableCredentialAttestation`:
|
|
120
|
+
*
|
|
121
|
+
* VerifiableCredentialAttestation(
|
|
122
|
+
* bytes32 credentialHash,
|
|
123
|
+
* string issuer,
|
|
124
|
+
* uint64 validFrom,
|
|
125
|
+
* uint64 validUntil,
|
|
126
|
+
* string proofPurpose
|
|
127
|
+
* )
|
|
128
|
+
*/
|
|
129
|
+
export declare const VC_EIP712_TYPES: {
|
|
130
|
+
readonly VerifiableCredentialAttestation: readonly [{
|
|
131
|
+
readonly name: "credentialHash";
|
|
132
|
+
readonly type: "bytes32";
|
|
133
|
+
}, {
|
|
134
|
+
readonly name: "issuer";
|
|
135
|
+
readonly type: "string";
|
|
136
|
+
}, {
|
|
137
|
+
readonly name: "validFrom";
|
|
138
|
+
readonly type: "uint64";
|
|
139
|
+
}, {
|
|
140
|
+
readonly name: "validUntil";
|
|
141
|
+
readonly type: "uint64";
|
|
142
|
+
}, {
|
|
143
|
+
readonly name: "proofPurpose";
|
|
144
|
+
readonly type: "string";
|
|
145
|
+
}];
|
|
146
|
+
};
|
|
147
|
+
export declare const VC_DOMAIN_NAME: "AgenticPrimitivesVC";
|
|
148
|
+
export declare const VC_DOMAIN_VERSION: "1";
|
|
149
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3C Verifiable Credentials 2.0 envelope types + `Eip712Signature2026` proof
|
|
3
|
+
* type. Substrate for all spine credential classes (Association, Evidence,
|
|
4
|
+
* Outcome, Validation, TrustUpdate, AgreementCredential, PaymentReceipt, ...).
|
|
5
|
+
*
|
|
6
|
+
* Authoritative spec: specs/242-trust-credentials-and-public-assertions.md §4.
|
|
7
|
+
* Architecture-of-record: ADR-0023 + ADR-0024 (composability table).
|
|
8
|
+
*/
|
|
9
|
+
/** W3C VC 2.0 default context. */
|
|
10
|
+
export const VC_CONTEXT_V2 = 'https://www.w3.org/ns/credentials/v2';
|
|
11
|
+
/** Substrate's Eip712Signature2026 context (proof-type alongside W3C VC). */
|
|
12
|
+
export const EIP712_SIG_2026_CONTEXT = 'https://agenticprimitives.dev/contexts/eip712-signature-2026/v1';
|
|
13
|
+
/**
|
|
14
|
+
* Standard EIP-712 domain for the substrate's `Eip712Signature2026` proof.
|
|
15
|
+
*
|
|
16
|
+
* Cross-stack typehash equality: any Solidity verifier MUST compute the same
|
|
17
|
+
* `typeHash` from the same primary type string + same field types. The
|
|
18
|
+
* canonical primary type is `VerifiableCredentialAttestation`:
|
|
19
|
+
*
|
|
20
|
+
* VerifiableCredentialAttestation(
|
|
21
|
+
* bytes32 credentialHash,
|
|
22
|
+
* string issuer,
|
|
23
|
+
* uint64 validFrom,
|
|
24
|
+
* uint64 validUntil,
|
|
25
|
+
* string proofPurpose
|
|
26
|
+
* )
|
|
27
|
+
*/
|
|
28
|
+
export const VC_EIP712_TYPES = {
|
|
29
|
+
VerifiableCredentialAttestation: [
|
|
30
|
+
{ name: 'credentialHash', type: 'bytes32' },
|
|
31
|
+
{ name: 'issuer', type: 'string' },
|
|
32
|
+
{ name: 'validFrom', type: 'uint64' },
|
|
33
|
+
{ name: 'validUntil', type: 'uint64' },
|
|
34
|
+
{ name: 'proofPurpose', type: 'string' },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
export const VC_DOMAIN_NAME = 'AgenticPrimitivesVC';
|
|
38
|
+
export const VC_DOMAIN_VERSION = '1';
|
|
39
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,sCAA+C,CAAC;AAE7E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,uBAAuB,GAClC,iEAA0E,CAAC;AAwH7E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,+BAA+B,EAAE;QAC/B,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE;QAC3C,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE;QAClC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE;QACrC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE;QACtC,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE;KACzC;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,cAAc,GAAG,qBAA8B,CAAC;AAC7D,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAY,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifier — checks that a VC's proof is valid (findings VC-1 / VC-2, closed).
|
|
3
|
+
*
|
|
4
|
+
* `verifyCredential(vc, publicClient)` performs the ERC-1271 round-trip against
|
|
5
|
+
* the issuer SA on chain and FAILS CLOSED — `credentialHash` is mandatory, and
|
|
6
|
+
* the EIP-712 digest pins `verifyingContract` to the issuer SA + binds `chainId`,
|
|
7
|
+
* so an attacker can't substitute a different verifying domain. The structural
|
|
8
|
+
* helpers remain for callers that only need shape validation, but the authority
|
|
9
|
+
* check is `verifyCredential`; there is no fallback path (ADR-0013).
|
|
10
|
+
*
|
|
11
|
+
* Status-list resolution (StatusList2021): the verifier records whether a status
|
|
12
|
+
* entry was present + which list to fetch; the consumer fetches it (no silent
|
|
13
|
+
* fallback, ADR-0013).
|
|
14
|
+
*/
|
|
15
|
+
import type { CredentialStatus2021, Hex32, VerifiableCredential } from './types.js';
|
|
16
|
+
/** Parsed `eip155:<chainId>:<address>` CAIP-10 reference. */
|
|
17
|
+
export interface Caip10Eip155 {
|
|
18
|
+
chainId: number;
|
|
19
|
+
address: `0x${string}`;
|
|
20
|
+
}
|
|
21
|
+
/** Parse an `eip155:<chainId>:0x…40` CAIP-10 account id, or null. Both `vc.issuer` and the
|
|
22
|
+
* `verificationMethod` prefix use this form — the VC-2 domain-binding source of truth. */
|
|
23
|
+
export declare function parseEip155Caip10(ref: string): Caip10Eip155 | null;
|
|
24
|
+
export interface VerificationResult {
|
|
25
|
+
/** Structural validity (envelope shape + canonical hash agreement). */
|
|
26
|
+
structural: boolean;
|
|
27
|
+
/** The digest the issuer SA's ERC-1271 path MUST validate. */
|
|
28
|
+
expectedDigest: Hex32 | null;
|
|
29
|
+
/** The proof value the consumer presents to `isValidSignatureNow`. */
|
|
30
|
+
proofValue: `0x${string}` | null;
|
|
31
|
+
/** The CAIP-10 issuer reference extracted from the verification method. */
|
|
32
|
+
issuerCaip10: string | null;
|
|
33
|
+
/** Status-list entry (if present); consumer fetches the list itself. */
|
|
34
|
+
statusListId: string | null;
|
|
35
|
+
/** Issues found — empty array if everything passes structural + canonical-hash checks. */
|
|
36
|
+
issues: string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Structural verification — does NOT make a network call. Returns the
|
|
40
|
+
* expected digest + proof value the caller MUST forward to the issuer SA's
|
|
41
|
+
* `isValidSignatureNow` (or other ERC-1271-aware path).
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyCredentialStructural(vc: VerifiableCredential): VerificationResult;
|
|
44
|
+
/** Minimal ERC-1271 verifier seam — satisfied structurally by a viem `PublicClient`
|
|
45
|
+
* (`client.verifyHash({ address, hash, signature })`). Kept structural so the package stays a
|
|
46
|
+
* graph leaf and isn't pinned to a single viem version. */
|
|
47
|
+
export interface Erc1271Verifier {
|
|
48
|
+
verifyHash(args: {
|
|
49
|
+
address: `0x${string}`;
|
|
50
|
+
hash: `0x${string}`;
|
|
51
|
+
signature: `0x${string}`;
|
|
52
|
+
}): Promise<boolean>;
|
|
53
|
+
}
|
|
54
|
+
export interface VerifyCredentialResult {
|
|
55
|
+
/** True ONLY when the structural checks pass AND the issuer SA's ERC-1271 path validates the
|
|
56
|
+
* signature over the expected digest. Fail-closed: any error/anomaly → false. */
|
|
57
|
+
valid: boolean;
|
|
58
|
+
/** The structural sub-result (digest, proof value, issuer ref, issues). */
|
|
59
|
+
structuralResult: VerificationResult;
|
|
60
|
+
/** All issues — structural plus signature/verification failures. */
|
|
61
|
+
issues: string[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Full credential verification (VC-1): structural checks PLUS the on-chain ERC-1271 round-trip
|
|
65
|
+
* against the issuer SA resolved from `vc.issuer`. This is the function consumers (content
|
|
66
|
+
* entitlements, agent-skills, geo-features, related-agents) MUST gate on — `verifyCredentialStructural`
|
|
67
|
+
* alone proves nothing about the signature.
|
|
68
|
+
*
|
|
69
|
+
* Fail-closed per ADR-0013: a structural failure, an unresolvable issuer, a verification-call error,
|
|
70
|
+
* or a non-validating signature all return `valid: false` — never a silent accept.
|
|
71
|
+
*/
|
|
72
|
+
/** Resolve + check a credential's revocation/suspension status (NEW-VC-1). Returns `revoked: true` to
|
|
73
|
+
* fail the verification. Injected so the package stays a leaf (the StatusList2021 fetch is the
|
|
74
|
+
* consumer's effect). */
|
|
75
|
+
export interface CredentialStatusResolver {
|
|
76
|
+
(status: CredentialStatus2021): Promise<{
|
|
77
|
+
revoked: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
}
|
|
80
|
+
export interface VerifyCredentialOpts {
|
|
81
|
+
/** NEW-VC-1: when the VC carries a `credentialStatus`, this resolver checks revocation/suspension.
|
|
82
|
+
* REQUIRED for any status-bearing credential unless `allowUnresolvedStatus` is set — otherwise a
|
|
83
|
+
* REVOKED credential would verify valid (the verifier can confirm the signature but not the status). */
|
|
84
|
+
verifyStatus?: CredentialStatusResolver;
|
|
85
|
+
/** NEW-VC-1 opt-out: accept a status-bearing credential WITHOUT resolving its status. Use ONLY where
|
|
86
|
+
* revocation genuinely doesn't matter; otherwise omitting the resolver fails closed (ADR-0013). */
|
|
87
|
+
allowUnresolvedStatus?: boolean;
|
|
88
|
+
}
|
|
89
|
+
export declare function verifyCredential(vc: VerifiableCredential, client: Erc1271Verifier, opts?: VerifyCredentialOpts): Promise<VerifyCredentialResult>;
|
|
90
|
+
//# sourceMappingURL=verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../src/verifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EACV,oBAAoB,EAEpB,KAAK,EAEL,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAEpB,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;CACxB;AAED;2FAC2F;AAC3F,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAIlE;AAED,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,UAAU,EAAE,OAAO,CAAC;IACpB,8DAA8D;IAC9D,cAAc,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,UAAU,EAAE,KAAK,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,2EAA2E;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wEAAwE;IACxE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0FAA0F;IAC1F,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,oBAAoB,GAAG,kBAAkB,CA+HvF;AAYD;;4DAE4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;QACvB,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;QACpB,SAAS,EAAE,KAAK,MAAM,EAAE,CAAC;KAC1B,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC;sFACkF;IAClF,KAAK,EAAE,OAAO,CAAC;IACf,2EAA2E;IAC3E,gBAAgB,EAAE,kBAAkB,CAAC;IACrC,oEAAoE;IACpE,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH;;0BAE0B;AAC1B,MAAM,WAAW,wBAAwB;IACvC,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,oBAAoB;IACnC;;6GAEyG;IACzG,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC;wGACoG;IACpG,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,oBAAoB,EACxB,MAAM,EAAE,eAAe,EACvB,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,sBAAsB,CAAC,CAmDjC"}
|