@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agentic Trust Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @agenticprimitives/verifiable-credentials
|
|
2
|
+
|
|
3
|
+
**A credential is only as durable as the identity it points at.** Most verifiable-credential stacks bind claims to a key — and when the key rotates, the trust graph quietly breaks. Here the issuer and subject of every credential is a Smart Agent address: a persistent ERC-4337 account whose keys are replaceable facets. Rotate a passkey, recover from a lost device, swap a signer — the address persists, and every credential issued by or about it stays verifiable.
|
|
4
|
+
|
|
5
|
+
This package is the W3C VC 2.0 envelope for that model: an `Eip712Signature2026` proof signed by a smart account and verified with an ERC-1271 round-trip against the chain, plus the RFC 8785 (JCS) canonical hash that every downstream registry stores as the on-chain anchor. It is the substrate layer — specific credential types (associations, agreements, evidence, outcomes, receipts) are composed on top by consumer packages.
|
|
6
|
+
|
|
7
|
+
> Part of [agenticprimitives](../../README.md) — the trust substrate for the agent economy: one canonical Smart Agent identity with custody, delegation, naming, credentials, and audit evidence designed as one system.
|
|
8
|
+
|
|
9
|
+
## What ships today
|
|
10
|
+
|
|
11
|
+
- **W3C VC 2.0 envelope** — `@context`, `type`, `issuer`, `validFrom`, `credentialSubject`, `proof`, `credentialStatus` (`VerifiableCredential`, `UnsignedCredential` types).
|
|
12
|
+
- **`Eip712Signature2026` proof** — `signCredential(unsigned, signer)` produces the proof; `eip712Digest(...)` matches the Solidity verifier byte-for-byte (cross-stack typehash equality, CI-gated per spec 242 §4.3).
|
|
13
|
+
- **Verifier** — `verifyCredential(vc, client)` runs structural checks, recomputes the digest, and verifies the issuer signature via ERC-1271 against the issuer's Smart Agent. One mechanism, no silent fallbacks ([ADR-0013](../../docs/architecture/decisions/0013-no-silent-fallbacks.md)). `verifyCredentialStructural(vc)` is the offline subset.
|
|
14
|
+
- **Canonical hash** — `credentialHash(vc)` / `canonicalHash(...)` / `jcsCanonicalize(...)`: the RFC 8785 JCS hash used as the credential anchor everywhere downstream (attestation registries, vault indexes, status lists).
|
|
15
|
+
- **DOLCE+DnS Situation pattern** — `buildSituation(...)` and the `Situation` / `DescriptionRef` / `RoleName` shapes that all substrate credential subjects compose against.
|
|
16
|
+
- **Schema URIs** — `buildShapeUri` / `parseShapeUri` / `shapeHash` for the `did:shape:<name>:<version>` convention that pairs with the on-chain `ShapeRegistry`.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { credentialHash, verifyCredential } from '@agenticprimitives/verifiable-credentials';
|
|
20
|
+
|
|
21
|
+
const anchor = credentialHash(vc); // RFC 8785 JCS hash — what registries store
|
|
22
|
+
const result = await verifyCredential(vc, client); // ERC-1271 round-trip against the issuer Smart Agent
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## How it's different
|
|
26
|
+
|
|
27
|
+
Veramo, Trinsic, and the broader W3C VC tooling ecosystem resolve issuers through DID methods and key registries — so credential validity is coupled to key material. Here the issuer is a CAIP-10 smart-account address and verification is ERC-1271 against the chain: **keys rotate, the address persists, credentials survive recovery** ([ADR-0011](../../docs/architecture/decisions/0011-credential-recovery-and-re-association.md)). And because the EIP-712 digest is locked to its Solidity counterpart by a CI gate, the contracts and the SDK are one artifact — the client cannot drift from the chain, which is precisely where stitched VC stacks decay.
|
|
28
|
+
|
|
29
|
+
The package is deliberately a leaf: it imports only `types`, `ontology`, `viem`, and `@noble/hashes`, and defines no specific credential types — `attestations`, `agreements`, `fulfillment`, and `payments` own those.
|
|
30
|
+
|
|
31
|
+
## Status
|
|
32
|
+
|
|
33
|
+
**W1 foundational — implemented and security-load-bearing.** The envelope, proof, canonical hash, and verifier ship today and are exercised by downstream packages; verifier findings VC-1/VC-2 are closed (see [AUDIT.md](./AUDIT.md)). Alternative proof types (BBS+ / SD-JWT / AnonCreds) have a reserved envelope slot and land in later waves per the [W1 implementation wave plan](../../docs/architecture/w1-implementation-wave-plan.md).
|
|
34
|
+
|
|
35
|
+
> Testnet/pilot-ready. Production launch is gated on the public checklist in the root README — including third-party contract audit and governance key rotation. Track every security finding live in [`docs/audits/findings.yaml`](../../docs/audits/findings.yaml).
|
|
36
|
+
|
|
37
|
+
**Authoritative spec:** [`specs/242-trust-credentials-and-public-assertions.md`](../../specs/242-trust-credentials-and-public-assertions.md) §4 — see `spec.md` for the symlink. Bounded surface: `CLAUDE.md` + `capability.manifest.json`.
|
|
38
|
+
|
|
39
|
+
## Build
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm --filter @agenticprimitives/verifiable-credentials typecheck
|
|
43
|
+
pnpm --filter @agenticprimitives/verifiable-credentials test
|
|
44
|
+
pnpm --filter @agenticprimitives/verifiable-credentials build
|
|
45
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 8785 JSON Canonicalization Scheme (JCS) — deterministic JSON serialisation
|
|
3
|
+
* for cross-stack hash equality (TS ↔ Solidity ↔ Java/Go verifiers).
|
|
4
|
+
*
|
|
5
|
+
* The substrate's `credentialHash` is `keccak256(jcsCanonicalize(vc-without-proof))`.
|
|
6
|
+
* Every package that needs to recompute the hash MUST go through this helper.
|
|
7
|
+
*
|
|
8
|
+
* RFC 8785 rules:
|
|
9
|
+
* - UTF-8 strings, NFC normalisation (browsers ship NFC already)
|
|
10
|
+
* - Object keys sorted by UTF-16 code unit (== lexicographic in BMP)
|
|
11
|
+
* - No insignificant whitespace
|
|
12
|
+
* - Numbers as the shortest IEEE 754 round-trip representation
|
|
13
|
+
* - Booleans `true` / `false` / `null` lowercase
|
|
14
|
+
* - Strings JSON-escape U+0000..U+001F, U+0022, U+005C only; emit other code points as-is
|
|
15
|
+
*
|
|
16
|
+
* This implementation is intentionally narrow: substrate VCs use a constrained
|
|
17
|
+
* subset of JSON (string / number / boolean / null / object / array). We do not
|
|
18
|
+
* handle BigInt or Date — callers must serialise those upstream.
|
|
19
|
+
*/
|
|
20
|
+
export declare class JcsError extends Error {
|
|
21
|
+
readonly path: string;
|
|
22
|
+
constructor(message: string, path: string);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Canonicalize a JSON-compatible value per RFC 8785.
|
|
26
|
+
* Returns the canonical string (UTF-8 text; callers wrap with `utf8ToBytes` to hash).
|
|
27
|
+
*/
|
|
28
|
+
export declare function jcsCanonicalize(value: unknown, path?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Compute the canonical hash of a value: `keccak256(jcsCanonicalize(value))`.
|
|
31
|
+
* Returns the 32-byte hex hash.
|
|
32
|
+
*/
|
|
33
|
+
export declare function canonicalHash(value: unknown): `0x${string}`;
|
|
34
|
+
//# sourceMappingURL=canonical.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,qBAAa,QAAS,SAAQ,KAAK;IACJ,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAtC,OAAO,EAAE,MAAM,EAAW,IAAI,EAAE,MAAM;CAGnD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAE,MAAY,GAAG,MAAM,CA2B1E;AAkED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,MAAM,EAAE,CAK3D"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 8785 JSON Canonicalization Scheme (JCS) — deterministic JSON serialisation
|
|
3
|
+
* for cross-stack hash equality (TS ↔ Solidity ↔ Java/Go verifiers).
|
|
4
|
+
*
|
|
5
|
+
* The substrate's `credentialHash` is `keccak256(jcsCanonicalize(vc-without-proof))`.
|
|
6
|
+
* Every package that needs to recompute the hash MUST go through this helper.
|
|
7
|
+
*
|
|
8
|
+
* RFC 8785 rules:
|
|
9
|
+
* - UTF-8 strings, NFC normalisation (browsers ship NFC already)
|
|
10
|
+
* - Object keys sorted by UTF-16 code unit (== lexicographic in BMP)
|
|
11
|
+
* - No insignificant whitespace
|
|
12
|
+
* - Numbers as the shortest IEEE 754 round-trip representation
|
|
13
|
+
* - Booleans `true` / `false` / `null` lowercase
|
|
14
|
+
* - Strings JSON-escape U+0000..U+001F, U+0022, U+005C only; emit other code points as-is
|
|
15
|
+
*
|
|
16
|
+
* This implementation is intentionally narrow: substrate VCs use a constrained
|
|
17
|
+
* subset of JSON (string / number / boolean / null / object / array). We do not
|
|
18
|
+
* handle BigInt or Date — callers must serialise those upstream.
|
|
19
|
+
*/
|
|
20
|
+
import { keccak_256 } from '@noble/hashes/sha3.js';
|
|
21
|
+
import { utf8ToBytes } from '@noble/hashes/utils.js';
|
|
22
|
+
export class JcsError extends Error {
|
|
23
|
+
path;
|
|
24
|
+
constructor(message, path) {
|
|
25
|
+
super(`[JCS] ${message} at ${path}`);
|
|
26
|
+
this.path = path;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Canonicalize a JSON-compatible value per RFC 8785.
|
|
31
|
+
* Returns the canonical string (UTF-8 text; callers wrap with `utf8ToBytes` to hash).
|
|
32
|
+
*/
|
|
33
|
+
export function jcsCanonicalize(value, path = '$') {
|
|
34
|
+
if (value === null)
|
|
35
|
+
return 'null';
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
throw new JcsError('undefined is not JSON', path);
|
|
38
|
+
}
|
|
39
|
+
switch (typeof value) {
|
|
40
|
+
case 'boolean':
|
|
41
|
+
return value ? 'true' : 'false';
|
|
42
|
+
case 'string':
|
|
43
|
+
return serializeString(value);
|
|
44
|
+
case 'number':
|
|
45
|
+
return serializeNumber(value, path);
|
|
46
|
+
case 'object': {
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
const items = value.map((v, i) => jcsCanonicalize(v, `${path}[${i}]`));
|
|
49
|
+
return `[${items.join(',')}]`;
|
|
50
|
+
}
|
|
51
|
+
// Plain object
|
|
52
|
+
const entries = Object.entries(value)
|
|
53
|
+
.filter(([, v]) => v !== undefined)
|
|
54
|
+
.sort(([a], [b]) => sortByCodeUnit(a, b));
|
|
55
|
+
const items = entries.map(([k, v]) => `${serializeString(k)}:${jcsCanonicalize(v, `${path}.${k}`)}`);
|
|
56
|
+
return `{${items.join(',')}}`;
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
throw new JcsError(`unsupported JSON type: ${typeof value}`, path);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** UTF-16 code unit comparison — exactly what RFC 8785 §3.2.3 requires. */
|
|
63
|
+
function sortByCodeUnit(a, b) {
|
|
64
|
+
const len = Math.min(a.length, b.length);
|
|
65
|
+
for (let i = 0; i < len; i++) {
|
|
66
|
+
const av = a.charCodeAt(i);
|
|
67
|
+
const bv = b.charCodeAt(i);
|
|
68
|
+
if (av !== bv)
|
|
69
|
+
return av - bv;
|
|
70
|
+
}
|
|
71
|
+
return a.length - b.length;
|
|
72
|
+
}
|
|
73
|
+
/** RFC 8259 string serialisation with JCS-compatible escaping (RFC 8785 §3.2.2.2). */
|
|
74
|
+
function serializeString(s) {
|
|
75
|
+
let out = '"';
|
|
76
|
+
for (let i = 0; i < s.length; i++) {
|
|
77
|
+
const code = s.charCodeAt(i);
|
|
78
|
+
if (code === 0x22) {
|
|
79
|
+
out += '\\"';
|
|
80
|
+
}
|
|
81
|
+
else if (code === 0x5c) {
|
|
82
|
+
out += '\\\\';
|
|
83
|
+
}
|
|
84
|
+
else if (code < 0x20) {
|
|
85
|
+
// Control characters
|
|
86
|
+
switch (code) {
|
|
87
|
+
case 0x08:
|
|
88
|
+
out += '\\b';
|
|
89
|
+
break;
|
|
90
|
+
case 0x09:
|
|
91
|
+
out += '\\t';
|
|
92
|
+
break;
|
|
93
|
+
case 0x0a:
|
|
94
|
+
out += '\\n';
|
|
95
|
+
break;
|
|
96
|
+
case 0x0c:
|
|
97
|
+
out += '\\f';
|
|
98
|
+
break;
|
|
99
|
+
case 0x0d:
|
|
100
|
+
out += '\\r';
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
out += `\\u${code.toString(16).padStart(4, '0')}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
out += s[i];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
out += '"';
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/** RFC 8785 §3.2.2.3 — shortest round-tripping IEEE 754 representation. */
|
|
114
|
+
function serializeNumber(n, path) {
|
|
115
|
+
if (!Number.isFinite(n)) {
|
|
116
|
+
throw new JcsError(`non-finite number (${n})`, path);
|
|
117
|
+
}
|
|
118
|
+
if (Object.is(n, -0))
|
|
119
|
+
return '0';
|
|
120
|
+
if (n === 0)
|
|
121
|
+
return '0';
|
|
122
|
+
if (Number.isInteger(n) && Math.abs(n) < 1e21) {
|
|
123
|
+
return n.toString();
|
|
124
|
+
}
|
|
125
|
+
// Browsers + Node give us the shortest round-trip via `toString()`
|
|
126
|
+
// (per ECMA-262 §6.1.6.1.13). RFC 8785 expects the ES round-trip form.
|
|
127
|
+
return n.toString();
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Compute the canonical hash of a value: `keccak256(jcsCanonicalize(value))`.
|
|
131
|
+
* Returns the 32-byte hex hash.
|
|
132
|
+
*/
|
|
133
|
+
export function canonicalHash(value) {
|
|
134
|
+
const canonical = jcsCanonicalize(value);
|
|
135
|
+
const bytes = utf8ToBytes(canonical);
|
|
136
|
+
const digest = keccak_256(bytes);
|
|
137
|
+
return `0x${bytesToHex(digest)}`;
|
|
138
|
+
}
|
|
139
|
+
function bytesToHex(b) {
|
|
140
|
+
let s = '';
|
|
141
|
+
for (const v of b) {
|
|
142
|
+
s += v.toString(16).padStart(2, '0');
|
|
143
|
+
}
|
|
144
|
+
return s;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=canonical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agenticprimitives/verifiable-credentials — W3C VC 2.0 envelope, EIP-712
|
|
3
|
+
* + ERC-1271 proof, DOLCE+DnS Situation bases, JCS canonical hash, and the
|
|
4
|
+
* schema-registration helper.
|
|
5
|
+
*
|
|
6
|
+
* Substrate for spine layers 12–15 credential classes; consumers (attestations,
|
|
7
|
+
* agreements, payments, fulfillment) compose specific credential types on top.
|
|
8
|
+
*
|
|
9
|
+
* Authoritative spec: specs/242-trust-credentials-and-public-assertions.md
|
|
10
|
+
*/
|
|
11
|
+
export declare const PACKAGE_NAME = "@agenticprimitives/verifiable-credentials";
|
|
12
|
+
export declare const PACKAGE_STATUS: "w1-foundational";
|
|
13
|
+
export declare const SPEC_REF = "specs/242-trust-credentials-and-public-assertions.md";
|
|
14
|
+
export { VC_CONTEXT_V2, EIP712_SIG_2026_CONTEXT, VC_DOMAIN_NAME, VC_DOMAIN_VERSION, VC_EIP712_TYPES, } from './types.js';
|
|
15
|
+
export type { Hex32, ISODate, ProofType, Eip712Signature2026Proof, DelegatingSignerProof, Proof, CredentialStatus2021, VisibilityTier, DisclosurePolicy, VerifiableCredential, UnsignedCredential, } from './types.js';
|
|
16
|
+
export { jcsCanonicalize, canonicalHash, JcsError } from './canonical.js';
|
|
17
|
+
export { assertSituationRolesPresent, buildSituation, } from './situation.js';
|
|
18
|
+
export type { Situation, DescriptionRef, RoleName } from './situation.js';
|
|
19
|
+
export { credentialHash, eip712Digest, isoToSeconds, signCredential, viemSignerFromWallet, kmsCredentialSigner, } from './proof.js';
|
|
20
|
+
export type { CredentialSigner, KmsSigningBackend } from './proof.js';
|
|
21
|
+
export { verifyCredential, verifyCredentialStructural, parseEip155Caip10 } from './verifier.js';
|
|
22
|
+
export type { VerificationResult, VerifyCredentialResult, VerifyCredentialOpts, CredentialStatusResolver, Erc1271Verifier, Caip10Eip155, } from './verifier.js';
|
|
23
|
+
export { SHAPE_DID_PREFIX, buildShapeUri, parseShapeUri, shapeHash } from './schema.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,YAAY,8CAA8C,CAAC;AACxE,eAAO,MAAM,cAAc,EAAG,iBAA0B,CAAC;AACzD,eAAO,MAAM,QAAQ,yDAAyD,CAAC;AAG/E,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,KAAK,EACL,OAAO,EACP,SAAS,EACT,wBAAwB,EACxB,qBAAqB,EACrB,KAAK,EACL,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1E,OAAO,EACL,2BAA2B,EAC3B,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1E,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGtE,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChG,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,YAAY,GACb,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agenticprimitives/verifiable-credentials — W3C VC 2.0 envelope, EIP-712
|
|
3
|
+
* + ERC-1271 proof, DOLCE+DnS Situation bases, JCS canonical hash, and the
|
|
4
|
+
* schema-registration helper.
|
|
5
|
+
*
|
|
6
|
+
* Substrate for spine layers 12–15 credential classes; consumers (attestations,
|
|
7
|
+
* agreements, payments, fulfillment) compose specific credential types on top.
|
|
8
|
+
*
|
|
9
|
+
* Authoritative spec: specs/242-trust-credentials-and-public-assertions.md
|
|
10
|
+
*/
|
|
11
|
+
export const PACKAGE_NAME = '@agenticprimitives/verifiable-credentials';
|
|
12
|
+
export const PACKAGE_STATUS = 'w1-foundational';
|
|
13
|
+
export const SPEC_REF = 'specs/242-trust-credentials-and-public-assertions.md';
|
|
14
|
+
// Types + constants
|
|
15
|
+
export { VC_CONTEXT_V2, EIP712_SIG_2026_CONTEXT, VC_DOMAIN_NAME, VC_DOMAIN_VERSION, VC_EIP712_TYPES, } from './types.js';
|
|
16
|
+
// Canonical hash + JCS
|
|
17
|
+
export { jcsCanonicalize, canonicalHash, JcsError } from './canonical.js';
|
|
18
|
+
// Situation pattern
|
|
19
|
+
export { assertSituationRolesPresent, buildSituation, } from './situation.js';
|
|
20
|
+
// Proof builder
|
|
21
|
+
export { credentialHash, eip712Digest, isoToSeconds, signCredential, viemSignerFromWallet, kmsCredentialSigner, } from './proof.js';
|
|
22
|
+
// Verifier
|
|
23
|
+
export { verifyCredential, verifyCredentialStructural, parseEip155Caip10 } from './verifier.js';
|
|
24
|
+
// Schema registration
|
|
25
|
+
export { SHAPE_DID_PREFIX, buildShapeUri, parseShapeUri, shapeHash } from './schema.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,2CAA2C,CAAC;AACxE,MAAM,CAAC,MAAM,cAAc,GAAG,iBAA0B,CAAC;AACzD,MAAM,CAAC,MAAM,QAAQ,GAAG,sDAAsD,CAAC;AAE/E,oBAAoB;AACpB,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,cAAc,EACd,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC;AAepB,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1E,oBAAoB;AACpB,OAAO,EACL,2BAA2B,EAC3B,cAAc,GACf,MAAM,gBAAgB,CAAC;AAGxB,gBAAgB;AAChB,OAAO,EACL,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAGpB,WAAW;AACX,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAUhG,sBAAsB;AACtB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/proof.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eip712Signature2026 proof builder — signs the canonical hash of a credential
|
|
3
|
+
* subject + envelope metadata with EIP-712 typed data, producing an
|
|
4
|
+
* ERC-1271-verifiable signature.
|
|
5
|
+
*
|
|
6
|
+
* Spec 242 §4.3 + ADR-0023 §D2.
|
|
7
|
+
*/
|
|
8
|
+
import { type Address, type Hex, type WalletClient } from 'viem';
|
|
9
|
+
import { type Eip712Signature2026Proof, type Hex32, type Proof, type UnsignedCredential, type VerifiableCredential } from './types.js';
|
|
10
|
+
/** A signer abstraction so callers can wire viem wallet client OR a custom signer. */
|
|
11
|
+
export interface CredentialSigner {
|
|
12
|
+
/** Address of the SA that will be the issuer (verified via ERC-1271). */
|
|
13
|
+
issuerAddress: Address;
|
|
14
|
+
/** EIP-712 chain id + verifying-contract anchor for the SA. */
|
|
15
|
+
chainId: number;
|
|
16
|
+
/** Must match the SA's verifying contract (typically the SA itself for ERC-1271). */
|
|
17
|
+
verifyingContract: Address;
|
|
18
|
+
/**
|
|
19
|
+
* Sign an arbitrary 32-byte digest. Implementations route through whatever
|
|
20
|
+
* the SA's signing path is (passkey, EOA wrap, KMS, multi-sig, etc.).
|
|
21
|
+
*/
|
|
22
|
+
signDigest(digest: Hex32): Promise<Hex>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Compute the canonical hash of an unsigned credential — strips `proof`,
|
|
26
|
+
* applies RFC 8785 JCS canonicalisation, returns keccak256.
|
|
27
|
+
*/
|
|
28
|
+
export declare function credentialHash(unsigned: UnsignedCredential | VerifiableCredential): Hex32;
|
|
29
|
+
/**
|
|
30
|
+
* Compute the EIP-712 typed-data digest the signer signs. Cross-stack typehash
|
|
31
|
+
* equality test (spec 242 §4.3) verifies this matches a Solidity verifier.
|
|
32
|
+
*/
|
|
33
|
+
export declare function eip712Digest(args: {
|
|
34
|
+
credentialBodyHash: Hex32;
|
|
35
|
+
issuer: string;
|
|
36
|
+
validFrom: number;
|
|
37
|
+
validUntil: number;
|
|
38
|
+
proofPurpose: Eip712Signature2026Proof['proofPurpose'];
|
|
39
|
+
chainId: number;
|
|
40
|
+
verifyingContract: Address;
|
|
41
|
+
}): Hex32;
|
|
42
|
+
/** Convert an ISO-8601 timestamp to a uint64-safe seconds value. */
|
|
43
|
+
export declare function isoToSeconds(iso: string | undefined): number;
|
|
44
|
+
/**
|
|
45
|
+
* Sign an `UnsignedCredential` and return a fully formed `VerifiableCredential`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function signCredential<TSubject extends Record<string, unknown>>(unsigned: UnsignedCredential<TSubject>, signer: CredentialSigner, opts?: {
|
|
48
|
+
proofPurpose?: Eip712Signature2026Proof['proofPurpose'];
|
|
49
|
+
delegatingSigner?: Eip712Signature2026Proof['delegatingSigner'];
|
|
50
|
+
}): Promise<VerifiableCredential<TSubject>>;
|
|
51
|
+
/** Convenience: a viem `WalletClient`-backed `CredentialSigner` (EOA-only path). */
|
|
52
|
+
export declare function viemSignerFromWallet(args: {
|
|
53
|
+
wallet: WalletClient;
|
|
54
|
+
issuerAddress: Address;
|
|
55
|
+
chainId: number;
|
|
56
|
+
verifyingContract: Address;
|
|
57
|
+
}): CredentialSigner;
|
|
58
|
+
/**
|
|
59
|
+
* Minimal structural shape of a KMS signing backend (spec 276 KCS-D5). It is
|
|
60
|
+
* declared HERE — not imported from `@agenticprimitives/key-custody` — because
|
|
61
|
+
* `verifiable-credentials` is a leaf in the dependency graph (no `@agenticprimitives/*`
|
|
62
|
+
* imports beyond types/ontology). `key-custody`'s `KmsAccountBackend` structurally
|
|
63
|
+
* satisfies this, so `kmsCredentialSigner(kmsBackend, …)` just works without the
|
|
64
|
+
* cross-package edge.
|
|
65
|
+
*/
|
|
66
|
+
export interface KmsSigningBackend {
|
|
67
|
+
/** Sign a 32-byte digest; returns a 65-byte secp256k1 `(r,s,v)` signature. */
|
|
68
|
+
signA2AAction(input: {
|
|
69
|
+
digest: Uint8Array;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
signature: Uint8Array;
|
|
72
|
+
}>;
|
|
73
|
+
/** The signing key's EVM address (the credential the issuer SA validates via ERC-1271). */
|
|
74
|
+
getSignerAddress(): Promise<Address>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Convenience: a `CredentialSigner` backed by a KMS-custodied secp256k1 key
|
|
78
|
+
* (the issuer SA's master / per-subject signer). Mirrors `viemSignerFromWallet`
|
|
79
|
+
* for the KMS path. `verifyingContract` defaults to the issuer SA itself (the
|
|
80
|
+
* ERC-1271 self-verification anchor).
|
|
81
|
+
*/
|
|
82
|
+
export declare function kmsCredentialSigner(args: {
|
|
83
|
+
backend: KmsSigningBackend;
|
|
84
|
+
issuerAddress: Address;
|
|
85
|
+
chainId: number;
|
|
86
|
+
verifyingContract?: Address;
|
|
87
|
+
}): CredentialSigner;
|
|
88
|
+
/** Re-export so callers see the Proof union from one place. */
|
|
89
|
+
export type { Proof };
|
|
90
|
+
//# sourceMappingURL=proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof.d.ts","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAyC,KAAK,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAGxG,OAAO,EAML,KAAK,wBAAwB,EAC7B,KAAK,KAAK,EACV,KAAK,KAAK,EACV,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AAEpB,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,aAAa,EAAE,OAAO,CAAC;IACvB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,iBAAiB,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,GAAG,oBAAoB,GAAG,KAAK,CAKzF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,kBAAkB,EAAE,KAAK,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAC;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,GAAG,KAAK,CAkBR;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAG5D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3E,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAC;IAAC,gBAAgB,CAAC,EAAE,wBAAwB,CAAC,kBAAkB,CAAC,CAAA;CAAO,GACtI,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAuCzC;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,YAAY,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,GAAG,gBAAgB,CAYnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,aAAa,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IACjF,2FAA2F;IAC3F,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,gBAAgB,CAUnB;AAQD,+DAA+D;AAC/D,YAAY,EAAE,KAAK,EAAE,CAAC"}
|
package/dist/proof.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eip712Signature2026 proof builder — signs the canonical hash of a credential
|
|
3
|
+
* subject + envelope metadata with EIP-712 typed data, producing an
|
|
4
|
+
* ERC-1271-verifiable signature.
|
|
5
|
+
*
|
|
6
|
+
* Spec 242 §4.3 + ADR-0023 §D2.
|
|
7
|
+
*/
|
|
8
|
+
import { hashTypedData, hexToBytes, bytesToHex } from 'viem';
|
|
9
|
+
import { canonicalHash } from './canonical.js';
|
|
10
|
+
import { EIP712_SIG_2026_CONTEXT, VC_CONTEXT_V2, VC_DOMAIN_NAME, VC_DOMAIN_VERSION, VC_EIP712_TYPES, } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Compute the canonical hash of an unsigned credential — strips `proof`,
|
|
13
|
+
* applies RFC 8785 JCS canonicalisation, returns keccak256.
|
|
14
|
+
*/
|
|
15
|
+
export function credentialHash(unsigned) {
|
|
16
|
+
// Defensive: strip proof if a signed VC was passed in.
|
|
17
|
+
const { proof: _ignored, ...withoutProof } = unsigned;
|
|
18
|
+
void _ignored;
|
|
19
|
+
return canonicalHash(withoutProof);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compute the EIP-712 typed-data digest the signer signs. Cross-stack typehash
|
|
23
|
+
* equality test (spec 242 §4.3) verifies this matches a Solidity verifier.
|
|
24
|
+
*/
|
|
25
|
+
export function eip712Digest(args) {
|
|
26
|
+
return hashTypedData({
|
|
27
|
+
domain: {
|
|
28
|
+
name: VC_DOMAIN_NAME,
|
|
29
|
+
version: VC_DOMAIN_VERSION,
|
|
30
|
+
chainId: args.chainId,
|
|
31
|
+
verifyingContract: args.verifyingContract,
|
|
32
|
+
},
|
|
33
|
+
types: VC_EIP712_TYPES,
|
|
34
|
+
primaryType: 'VerifiableCredentialAttestation',
|
|
35
|
+
message: {
|
|
36
|
+
credentialHash: args.credentialBodyHash,
|
|
37
|
+
issuer: args.issuer,
|
|
38
|
+
validFrom: BigInt(args.validFrom),
|
|
39
|
+
validUntil: BigInt(args.validUntil),
|
|
40
|
+
proofPurpose: args.proofPurpose,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** Convert an ISO-8601 timestamp to a uint64-safe seconds value. */
|
|
45
|
+
export function isoToSeconds(iso) {
|
|
46
|
+
if (!iso)
|
|
47
|
+
return 0;
|
|
48
|
+
return Math.floor(new Date(iso).getTime() / 1000);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sign an `UnsignedCredential` and return a fully formed `VerifiableCredential`.
|
|
52
|
+
*/
|
|
53
|
+
export async function signCredential(unsigned, signer, opts = {}) {
|
|
54
|
+
const proofPurpose = opts.proofPurpose ?? 'assertionMethod';
|
|
55
|
+
// Expand `@context` BEFORE hashing so the emitted `credentialHash` matches the
|
|
56
|
+
// canonical hash of the emitted body. (Hashing the pre-expansion body and then
|
|
57
|
+
// adding the EIP-712 context to the output produced a VC whose stored hash never
|
|
58
|
+
// reconciled with its own body — structural verification would reject it.)
|
|
59
|
+
const contexted = { ...unsigned, '@context': ensureContexts(unsigned['@context']) };
|
|
60
|
+
const bodyHash = credentialHash(contexted);
|
|
61
|
+
const digest = eip712Digest({
|
|
62
|
+
credentialBodyHash: bodyHash,
|
|
63
|
+
issuer: contexted.issuer,
|
|
64
|
+
validFrom: isoToSeconds(contexted.validFrom),
|
|
65
|
+
validUntil: isoToSeconds(contexted.validUntil),
|
|
66
|
+
proofPurpose,
|
|
67
|
+
chainId: signer.chainId,
|
|
68
|
+
verifyingContract: signer.verifyingContract,
|
|
69
|
+
});
|
|
70
|
+
const proofValue = await signer.signDigest(digest);
|
|
71
|
+
const proof = {
|
|
72
|
+
type: 'Eip712Signature2026',
|
|
73
|
+
created: new Date().toISOString(),
|
|
74
|
+
verificationMethod: `eip155:${signer.chainId}:${signer.verifyingContract}#assertion-key-1`,
|
|
75
|
+
proofPurpose,
|
|
76
|
+
proofValue,
|
|
77
|
+
eip712Domain: {
|
|
78
|
+
name: VC_DOMAIN_NAME,
|
|
79
|
+
version: VC_DOMAIN_VERSION,
|
|
80
|
+
chainId: signer.chainId,
|
|
81
|
+
verifyingContract: signer.verifyingContract,
|
|
82
|
+
},
|
|
83
|
+
credentialHash: bodyHash,
|
|
84
|
+
...(opts.delegatingSigner ? { delegatingSigner: opts.delegatingSigner } : {}),
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
...contexted,
|
|
88
|
+
proof,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Convenience: a viem `WalletClient`-backed `CredentialSigner` (EOA-only path). */
|
|
92
|
+
export function viemSignerFromWallet(args) {
|
|
93
|
+
return {
|
|
94
|
+
issuerAddress: args.issuerAddress,
|
|
95
|
+
chainId: args.chainId,
|
|
96
|
+
verifyingContract: args.verifyingContract,
|
|
97
|
+
async signDigest(digest) {
|
|
98
|
+
return args.wallet.request({
|
|
99
|
+
method: 'eth_sign',
|
|
100
|
+
params: [args.issuerAddress, digest],
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Convenience: a `CredentialSigner` backed by a KMS-custodied secp256k1 key
|
|
107
|
+
* (the issuer SA's master / per-subject signer). Mirrors `viemSignerFromWallet`
|
|
108
|
+
* for the KMS path. `verifyingContract` defaults to the issuer SA itself (the
|
|
109
|
+
* ERC-1271 self-verification anchor).
|
|
110
|
+
*/
|
|
111
|
+
export function kmsCredentialSigner(args) {
|
|
112
|
+
return {
|
|
113
|
+
issuerAddress: args.issuerAddress,
|
|
114
|
+
chainId: args.chainId,
|
|
115
|
+
verifyingContract: args.verifyingContract ?? args.issuerAddress,
|
|
116
|
+
async signDigest(digest) {
|
|
117
|
+
const { signature } = await args.backend.signA2AAction({ digest: hexToBytes(digest) });
|
|
118
|
+
return bytesToHex(signature);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function ensureContexts(existing) {
|
|
123
|
+
const needed = [VC_CONTEXT_V2, EIP712_SIG_2026_CONTEXT];
|
|
124
|
+
const have = new Set(existing);
|
|
125
|
+
return [...existing, ...needed.filter((c) => !have.has(c))];
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=proof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proof.js","sourceRoot":"","sources":["../src/proof.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
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 type { Hex32 } from './types.js';
|
|
12
|
+
export declare const SHAPE_DID_PREFIX: "did:shape:";
|
|
13
|
+
/**
|
|
14
|
+
* Build the canonical `did:shape:<name>:<version>` URI.
|
|
15
|
+
*
|
|
16
|
+
* @throws if name or version contains characters outside `[A-Za-z0-9-_.]`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildShapeUri(name: string, version: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Parse a `did:shape:<name>:<version>` URI.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseShapeUri(uri: string): {
|
|
23
|
+
name: string;
|
|
24
|
+
version: string;
|
|
25
|
+
} | null;
|
|
26
|
+
/**
|
|
27
|
+
* Canonicalise SHACL bytes for the on-chain hash. The canonical form is:
|
|
28
|
+
* - UTF-8 normalised (callers are responsible for upstream NFC if needed)
|
|
29
|
+
* - keccak256 of the raw bytes
|
|
30
|
+
*
|
|
31
|
+
* This matches the on-chain `ShapeRegistry.defineShape(shapeHash)` expectation
|
|
32
|
+
* — both sides hash the exact same SHACL string.
|
|
33
|
+
*/
|
|
34
|
+
export declare function shapeHash(shaclBytes: string | Uint8Array): Hex32;
|
|
35
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,eAAO,MAAM,gBAAgB,EAAG,YAAqB,CAAC;AAEtD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQnE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASnF;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,CAQhE"}
|