@zanii/core 0.2.0 → 0.4.0
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/README.md +9 -0
- package/dist/confirmation.d.ts +28 -0
- package/dist/confirmation.d.ts.map +1 -0
- package/dist/confirmation.js +32 -0
- package/dist/confirmation.js.map +1 -0
- package/dist/cosign.d.ts +37 -0
- package/dist/cosign.d.ts.map +1 -0
- package/dist/cosign.js +38 -0
- package/dist/cosign.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/receipt.d.ts +28 -0
- package/dist/receipt.d.ts.map +1 -1
- package/dist/receipt.js +31 -1
- package/dist/receipt.js.map +1 -1
- package/package.json +1 -1
- package/src/confirmation.ts +53 -0
- package/src/cosign.ts +63 -0
- package/src/index.ts +12 -0
- package/src/receipt.ts +49 -1
package/README.md
CHANGED
|
@@ -59,6 +59,15 @@ revocations, and on-chain anchor consistency.
|
|
|
59
59
|
- **Docs & concepts** — https://ledger.zanii.agency/docs
|
|
60
60
|
- **Live transparency log** — https://ledger.zanii.agency
|
|
61
61
|
|
|
62
|
+
## Changelog
|
|
63
|
+
|
|
64
|
+
- **0.4.0** — optional signature-covered `subject_tag` receipt field (per-subject auditability, `@zanii/subject`).
|
|
65
|
+
- **0.3.0** — `saltedPayloadHash`/`verifyPayload`, `createConfirmation`/`verifyConfirmation`, `createCoSignature`/`verifyCoSignature`, and optional receipt provenance fields (`runtime_hash`/`model_id`/`manifest_hash`).
|
|
66
|
+
- **0.2.0** — version bump in lockstep with the package family (no API changes).
|
|
67
|
+
- **0.1.0** — initial release: `did:key` identity, scoped/expiring delegation, signed
|
|
68
|
+
hash-chained receipts, RFC 6962 Merkle inclusion + consistency proofs, signed tree
|
|
69
|
+
heads, audit-bundle verification, and cross-org (A2A) receipts.
|
|
70
|
+
|
|
62
71
|
## License
|
|
63
72
|
|
|
64
73
|
Apache-2.0.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner confirmation of an irreversible action (SPEC §13 / Article VI).
|
|
3
|
+
*
|
|
4
|
+
* The human owner signs the exact action hash with their `did:key`. This is the
|
|
5
|
+
* cryptographic evidence a person confirmed — replacing an in-memory "yes" that
|
|
6
|
+
* proves nothing. Recorded as its own `action_confirmation` receipt so a dispute
|
|
7
|
+
* ("did the client actually approve this payment?") has a verifiable answer.
|
|
8
|
+
*/
|
|
9
|
+
export interface ActionConfirmation {
|
|
10
|
+
v: 1;
|
|
11
|
+
type: 'action_confirmation';
|
|
12
|
+
/** The owner's `did:key`. */
|
|
13
|
+
owner_id: string;
|
|
14
|
+
/** `sha256:…` hash of the exact action `{tool, args}` being confirmed. */
|
|
15
|
+
action_hash: string;
|
|
16
|
+
confirmed_at: string;
|
|
17
|
+
/** Ed25519 by the owner over JCS of the confirmation without `sig`. */
|
|
18
|
+
sig: string;
|
|
19
|
+
}
|
|
20
|
+
/** Owner signs an action hash. Only the owner's key can produce this. */
|
|
21
|
+
export declare function createConfirmation(fields: {
|
|
22
|
+
ownerId: string;
|
|
23
|
+
actionHash: string;
|
|
24
|
+
confirmedAt: string;
|
|
25
|
+
}, ownerPrivateKey: Uint8Array): ActionConfirmation;
|
|
26
|
+
/** Verify a confirmation's signature against the owner DID it names. */
|
|
27
|
+
export declare function verifyConfirmation(c: ActionConfirmation): boolean;
|
|
28
|
+
//# sourceMappingURL=confirmation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmation.d.ts","sourceRoot":"","sources":["../src/confirmation.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,CAAC,EAAE,CAAC,CAAC;IACL,IAAI,EAAE,qBAAqB,CAAC;IAC5B,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAChC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EACpE,eAAe,EAAE,UAAU,GAC1B,kBAAkB,CAUpB;AAED,wEAAwE;AACxE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAWjE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519';
|
|
2
|
+
import { canonicalBytes, decodeSig, encodeSig } from './encoding.js';
|
|
3
|
+
import { publicKeyFromDid } from './keys.js';
|
|
4
|
+
/** Owner signs an action hash. Only the owner's key can produce this. */
|
|
5
|
+
export function createConfirmation(fields, ownerPrivateKey) {
|
|
6
|
+
const unsigned = {
|
|
7
|
+
v: 1,
|
|
8
|
+
type: 'action_confirmation',
|
|
9
|
+
owner_id: fields.ownerId,
|
|
10
|
+
action_hash: fields.actionHash,
|
|
11
|
+
confirmed_at: fields.confirmedAt,
|
|
12
|
+
};
|
|
13
|
+
const sig = ed25519.sign(canonicalBytes(unsigned), ownerPrivateKey);
|
|
14
|
+
return { ...unsigned, sig: encodeSig(sig) };
|
|
15
|
+
}
|
|
16
|
+
/** Verify a confirmation's signature against the owner DID it names. */
|
|
17
|
+
export function verifyConfirmation(c) {
|
|
18
|
+
if (c?.v !== 1 || c.type !== 'action_confirmation')
|
|
19
|
+
return false;
|
|
20
|
+
const pub = publicKeyFromDid(c.owner_id);
|
|
21
|
+
const sig = decodeSig(c.sig);
|
|
22
|
+
if (!pub || !sig)
|
|
23
|
+
return false;
|
|
24
|
+
const { sig: _omitted, ...unsigned } = c;
|
|
25
|
+
try {
|
|
26
|
+
return ed25519.verify(sig, canonicalBytes(unsigned), pub);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=confirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmation.js","sourceRoot":"","sources":["../src/confirmation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAsB7C,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CAChC,MAAoE,EACpE,eAA2B;IAE3B,MAAM,QAAQ,GAAG;QACf,CAAC,EAAE,CAAU;QACb,IAAI,EAAE,qBAA8B;QACpC,QAAQ,EAAE,MAAM,CAAC,OAAO;QACxB,WAAW,EAAE,MAAM,CAAC,UAAU;QAC9B,YAAY,EAAE,MAAM,CAAC,WAAW;KACjC,CAAC;IACF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;IACpE,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,kBAAkB,CAAC,CAAqB;IACtD,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,qBAAqB;QAAE,OAAO,KAAK,CAAC;IACjE,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/cosign.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure co-signature over an agent's receipt (SPEC §14 — segregation of duties).
|
|
3
|
+
*
|
|
4
|
+
* Gordon's Q1: the agent that does the task cannot be the sole party reporting it.
|
|
5
|
+
* "Connect" — the deterministic rails that are the only door to the outside world —
|
|
6
|
+
* holds its own `did:key` and co-signs every receipt. A second, independent signature
|
|
7
|
+
* from a party the agent can't control (and, later, that holds the provider
|
|
8
|
+
* credentials) makes acting-without-recording structurally catchable. Reconciliation
|
|
9
|
+
* (`reconcile`) covers anything that still slips around the rails.
|
|
10
|
+
*
|
|
11
|
+
* This is a signed artifact carried in a normal receipt — no server or schema change.
|
|
12
|
+
*/
|
|
13
|
+
export interface ReceiptCoSignature {
|
|
14
|
+
v: 1;
|
|
15
|
+
type: 'receipt_cosign';
|
|
16
|
+
/** The co-signer's `did:key` (Connect / the rails) — MUST differ from `agent_id`. */
|
|
17
|
+
cosigner_id: string;
|
|
18
|
+
/** Hash of the agent receipt being co-signed. */
|
|
19
|
+
receipt_hash: string;
|
|
20
|
+
/** The agent (the doer) whose receipt this is. */
|
|
21
|
+
agent_id: string;
|
|
22
|
+
cosigned_at: string;
|
|
23
|
+
sig: string;
|
|
24
|
+
}
|
|
25
|
+
/** Connect co-signs an agent's receipt. Only the co-signer's key can produce this. */
|
|
26
|
+
export declare function createCoSignature(fields: {
|
|
27
|
+
cosignerId: string;
|
|
28
|
+
receiptHash: string;
|
|
29
|
+
agentId: string;
|
|
30
|
+
cosignedAt: string;
|
|
31
|
+
}, cosignerPrivateKey: Uint8Array): ReceiptCoSignature;
|
|
32
|
+
/**
|
|
33
|
+
* Verify a co-signature. Requires a valid signature by `cosigner_id` AND that the
|
|
34
|
+
* co-signer is a **distinct** party from the acting agent (segregation of duties).
|
|
35
|
+
*/
|
|
36
|
+
export declare function verifyCoSignature(c: ReceiptCoSignature): boolean;
|
|
37
|
+
//# sourceMappingURL=cosign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cosign.d.ts","sourceRoot":"","sources":["../src/cosign.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,CAAC,EAAE,CAAC,CAAC;IACL,IAAI,EAAE,gBAAgB,CAAC;IACvB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EACxF,kBAAkB,EAAE,UAAU,GAC7B,kBAAkB,CAWpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAYhE"}
|
package/dist/cosign.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519';
|
|
2
|
+
import { canonicalBytes, decodeSig, encodeSig } from './encoding.js';
|
|
3
|
+
import { publicKeyFromDid } from './keys.js';
|
|
4
|
+
/** Connect co-signs an agent's receipt. Only the co-signer's key can produce this. */
|
|
5
|
+
export function createCoSignature(fields, cosignerPrivateKey) {
|
|
6
|
+
const unsigned = {
|
|
7
|
+
v: 1,
|
|
8
|
+
type: 'receipt_cosign',
|
|
9
|
+
cosigner_id: fields.cosignerId,
|
|
10
|
+
receipt_hash: fields.receiptHash,
|
|
11
|
+
agent_id: fields.agentId,
|
|
12
|
+
cosigned_at: fields.cosignedAt,
|
|
13
|
+
};
|
|
14
|
+
const sig = ed25519.sign(canonicalBytes(unsigned), cosignerPrivateKey);
|
|
15
|
+
return { ...unsigned, sig: encodeSig(sig) };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Verify a co-signature. Requires a valid signature by `cosigner_id` AND that the
|
|
19
|
+
* co-signer is a **distinct** party from the acting agent (segregation of duties).
|
|
20
|
+
*/
|
|
21
|
+
export function verifyCoSignature(c) {
|
|
22
|
+
if (c?.v !== 1 || c.type !== 'receipt_cosign')
|
|
23
|
+
return false;
|
|
24
|
+
if (c.cosigner_id === c.agent_id)
|
|
25
|
+
return false; // doer must not co-sign itself
|
|
26
|
+
const pub = publicKeyFromDid(c.cosigner_id);
|
|
27
|
+
const sig = decodeSig(c.sig);
|
|
28
|
+
if (!pub || !sig)
|
|
29
|
+
return false;
|
|
30
|
+
const { sig: _omitted, ...unsigned } = c;
|
|
31
|
+
try {
|
|
32
|
+
return ed25519.verify(sig, canonicalBytes(unsigned), pub);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=cosign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cosign.js","sourceRoot":"","sources":["../src/cosign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AA2B7C,sFAAsF;AACtF,MAAM,UAAU,iBAAiB,CAC/B,MAAwF,EACxF,kBAA8B;IAE9B,MAAM,QAAQ,GAAG;QACf,CAAC,EAAE,CAAU;QACb,IAAI,EAAE,gBAAyB;QAC/B,WAAW,EAAE,MAAM,CAAC,UAAU;QAC9B,YAAY,EAAE,MAAM,CAAC,WAAW;QAChC,QAAQ,EAAE,MAAM,CAAC,OAAO;QACxB,WAAW,EAAE,MAAM,CAAC,UAAU;KAC/B,CAAC;IACF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAC,CAAC;IACvE,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAqB;IACrD,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,KAAK,CAAC;IAC5D,IAAI,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC,CAAC,+BAA+B;IAC/E,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,7 @@ export { leafHash, merkleRoot, inclusionProof, verifyInclusion, consistencyProof
|
|
|
5
5
|
export { createSTH, verifySTH, type SignedTreeHead } from './sth.js';
|
|
6
6
|
export { buildA2ABody, signA2ABody, assembleA2AReceipt, a2aHash, bodyOf, verifyA2AReceipt, participantsOf, prevFor, type A2AReceipt, type A2ABody, type A2AParty, } from './a2a.js';
|
|
7
7
|
export { verifyAuditBundle, type AuditBundle, type BundledReceipt, type BundledRevocation, type BundledAnchor, type BundleReport, type BundleCheck, } from './bundle.js';
|
|
8
|
-
export { createReceipt, receiptHash, hashPayload, verifyReceipt, verifyReceiptSignature, verifyReceiptChain, type Receipt, type VerifyResult, } from './receipt.js';
|
|
8
|
+
export { createReceipt, receiptHash, hashPayload, saltedPayloadHash, verifyPayload, verifyReceipt, verifyReceiptSignature, verifyReceiptChain, type Receipt, type VerifyResult, } from './receipt.js';
|
|
9
|
+
export { createConfirmation, verifyConfirmation, type ActionConfirmation, } from './confirmation.js';
|
|
10
|
+
export { createCoSignature, verifyCoSignature, type ReceiptCoSignature, } from './cosign.js';
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,QAAQ,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,QAAQ,GACd,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,5 +5,7 @@ export { leafHash, merkleRoot, inclusionProof, verifyInclusion, consistencyProof
|
|
|
5
5
|
export { createSTH, verifySTH } from './sth.js';
|
|
6
6
|
export { buildA2ABody, signA2ABody, assembleA2AReceipt, a2aHash, bodyOf, verifyA2AReceipt, participantsOf, prevFor, } from './a2a.js';
|
|
7
7
|
export { verifyAuditBundle, } from './bundle.js';
|
|
8
|
-
export { createReceipt, receiptHash, hashPayload, verifyReceipt, verifyReceiptSignature, verifyReceiptChain, } from './receipt.js';
|
|
8
|
+
export { createReceipt, receiptHash, hashPayload, saltedPayloadHash, verifyPayload, verifyReceipt, verifyReceiptSignature, verifyReceiptChain, } from './receipt.js';
|
|
9
|
+
export { createConfirmation, verifyConfirmation, } from './confirmation.js';
|
|
10
|
+
export { createCoSignature, verifyCoSignature, } from './cosign.js';
|
|
9
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAgB,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,WAAW,GAIZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAuB,MAAM,UAAU,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,OAAO,GAIR,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,iBAAiB,GAOlB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,kBAAkB,GAGnB,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAgB,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,WAAW,GAIZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAuB,MAAM,UAAU,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,OAAO,EACP,MAAM,EACN,gBAAgB,EAChB,cAAc,EACd,OAAO,GAIR,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,iBAAiB,GAOlB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,kBAAkB,GAGnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,aAAa,CAAC"}
|
package/dist/receipt.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ export interface Receipt {
|
|
|
8
8
|
payload_hash: string;
|
|
9
9
|
ts: string;
|
|
10
10
|
prev: string | null;
|
|
11
|
+
/** Optional provenance (SPEC §3). Present when set; covered by the signature. */
|
|
12
|
+
runtime_hash?: string;
|
|
13
|
+
model_id?: string;
|
|
14
|
+
/** Hash of the product's tool/scope manifest that governed this action. */
|
|
15
|
+
manifest_hash?: string;
|
|
16
|
+
/** Optional pseudonymous data-subject tag (SPEC §3) — lets the end user the action was
|
|
17
|
+
* about find + audit their own slice (`@zanii/subject`). Covered by the signature. */
|
|
18
|
+
subject_tag?: string;
|
|
11
19
|
sig: string;
|
|
12
20
|
}
|
|
13
21
|
export interface VerifyResult {
|
|
@@ -15,6 +23,20 @@ export interface VerifyResult {
|
|
|
15
23
|
error?: string;
|
|
16
24
|
}
|
|
17
25
|
export declare function hashPayload(payload: Uint8Array | string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Privacy-preserving payload commitment: `sha256(nonce ‖ payload)`. A bare
|
|
28
|
+
* `hashPayload` of a low-entropy payload ("phone X, amount 500") is dictionary-
|
|
29
|
+
* attackable from the public anchored log, and a linkable hash can itself be
|
|
30
|
+
* personal data (PDPL). The per-receipt `nonce` is kept operator-side (NOT in the
|
|
31
|
+
* receipt), so the on-chain value reveals nothing; keep it to later prove the
|
|
32
|
+
* payload with `verifyPayload`. If `nonce` is omitted a fresh 16-byte one is drawn.
|
|
33
|
+
*/
|
|
34
|
+
export declare function saltedPayloadHash(payload: Uint8Array | string, nonce?: string): {
|
|
35
|
+
payload_hash: string;
|
|
36
|
+
nonce: string;
|
|
37
|
+
};
|
|
38
|
+
/** Prove a payload matches a salted `payload_hash`, given the nonce kept at record time. */
|
|
39
|
+
export declare function verifyPayload(payload: Uint8Array | string, nonce: string, payloadHash: string): boolean;
|
|
18
40
|
export declare function createReceipt(fields: {
|
|
19
41
|
agentId: string;
|
|
20
42
|
delegation: DelegationCert[];
|
|
@@ -23,6 +45,12 @@ export declare function createReceipt(fields: {
|
|
|
23
45
|
payloadHash: string;
|
|
24
46
|
ts: string;
|
|
25
47
|
prev: string | null;
|
|
48
|
+
/** Optional provenance — included in the signed receipt only when provided. */
|
|
49
|
+
runtimeHash?: string;
|
|
50
|
+
modelId?: string;
|
|
51
|
+
manifestHash?: string;
|
|
52
|
+
/** Optional pseudonymous data-subject tag — per-subject auditability (`@zanii/subject`). */
|
|
53
|
+
subjectTag?: string;
|
|
26
54
|
}, agentPrivateKey: Uint8Array): Receipt;
|
|
27
55
|
/** Hash of the full receipt (incl. sig) — what the next receipt's `prev` points to. */
|
|
28
56
|
export declare function receiptHash(receipt: Receipt): string;
|
package/dist/receipt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEhF,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,CAAC,CAAC;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE;IACN,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,EACD,eAAe,EAAE,UAAU,GAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEhF,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,CAAC,CAAC;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;0FACsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAGhE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,UAAU,GAAG,MAAM,EAC5B,KAAK,CAAC,EAAE,MAAM,GACb;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQzC;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAMvG;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE;IACN,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,EACD,eAAe,EAAE,UAAU,GAC1B,OAAO,CAkBT;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpD;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAUhE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GACzF,YAAY,CAwBd;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAO,GAC3D,YAAY,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CASzC"}
|
package/dist/receipt.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ed25519 } from '@noble/curves/ed25519';
|
|
2
2
|
import { sha256 } from '@noble/hashes/sha256';
|
|
3
|
-
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
|
+
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils';
|
|
4
4
|
import { canonicalBytes, decodeSig, encodeSig, jcsHash } from './encoding.js';
|
|
5
5
|
import { publicKeyFromDid } from './keys.js';
|
|
6
6
|
import { scopesAllow, verifyChain } from './delegation.js';
|
|
@@ -8,6 +8,32 @@ export function hashPayload(payload) {
|
|
|
8
8
|
const bytes = typeof payload === 'string' ? new TextEncoder().encode(payload) : payload;
|
|
9
9
|
return `sha256:${bytesToHex(sha256(bytes))}`;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Privacy-preserving payload commitment: `sha256(nonce ‖ payload)`. A bare
|
|
13
|
+
* `hashPayload` of a low-entropy payload ("phone X, amount 500") is dictionary-
|
|
14
|
+
* attackable from the public anchored log, and a linkable hash can itself be
|
|
15
|
+
* personal data (PDPL). The per-receipt `nonce` is kept operator-side (NOT in the
|
|
16
|
+
* receipt), so the on-chain value reveals nothing; keep it to later prove the
|
|
17
|
+
* payload with `verifyPayload`. If `nonce` is omitted a fresh 16-byte one is drawn.
|
|
18
|
+
*/
|
|
19
|
+
export function saltedPayloadHash(payload, nonce) {
|
|
20
|
+
const nonceHex = nonce ?? bytesToHex(randomBytes(16));
|
|
21
|
+
const nonceBytes = hexToBytes(nonceHex);
|
|
22
|
+
const payloadBytes = typeof payload === 'string' ? new TextEncoder().encode(payload) : payload;
|
|
23
|
+
const combined = new Uint8Array(nonceBytes.length + payloadBytes.length);
|
|
24
|
+
combined.set(nonceBytes, 0);
|
|
25
|
+
combined.set(payloadBytes, nonceBytes.length);
|
|
26
|
+
return { payload_hash: `sha256:${bytesToHex(sha256(combined))}`, nonce: nonceHex };
|
|
27
|
+
}
|
|
28
|
+
/** Prove a payload matches a salted `payload_hash`, given the nonce kept at record time. */
|
|
29
|
+
export function verifyPayload(payload, nonce, payloadHash) {
|
|
30
|
+
try {
|
|
31
|
+
return saltedPayloadHash(payload, nonce).payload_hash === payloadHash;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
11
37
|
export function createReceipt(fields, agentPrivateKey) {
|
|
12
38
|
if (Number.isNaN(Date.parse(fields.ts)))
|
|
13
39
|
throw new Error(`invalid ts timestamp: ${fields.ts}`);
|
|
@@ -20,6 +46,10 @@ export function createReceipt(fields, agentPrivateKey) {
|
|
|
20
46
|
payload_hash: fields.payloadHash,
|
|
21
47
|
ts: fields.ts,
|
|
22
48
|
prev: fields.prev,
|
|
49
|
+
...(fields.runtimeHash ? { runtime_hash: fields.runtimeHash } : {}),
|
|
50
|
+
...(fields.modelId ? { model_id: fields.modelId } : {}),
|
|
51
|
+
...(fields.manifestHash ? { manifest_hash: fields.manifestHash } : {}),
|
|
52
|
+
...(fields.subjectTag ? { subject_tag: fields.subjectTag } : {}),
|
|
23
53
|
};
|
|
24
54
|
const sig = ed25519.sign(canonicalBytes(unsigned), agentPrivateKey);
|
|
25
55
|
return { ...unsigned, sig: encodeSig(sig) };
|
package/dist/receipt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zanii/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Zanii protocol: verifiable identity, signed action receipts, Merkle transparency-log proofs. Pure, no I/O.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ledger.zanii.agency",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519';
|
|
2
|
+
import { canonicalBytes, decodeSig, encodeSig } from './encoding.js';
|
|
3
|
+
import { publicKeyFromDid } from './keys.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Owner confirmation of an irreversible action (SPEC §13 / Article VI).
|
|
7
|
+
*
|
|
8
|
+
* The human owner signs the exact action hash with their `did:key`. This is the
|
|
9
|
+
* cryptographic evidence a person confirmed — replacing an in-memory "yes" that
|
|
10
|
+
* proves nothing. Recorded as its own `action_confirmation` receipt so a dispute
|
|
11
|
+
* ("did the client actually approve this payment?") has a verifiable answer.
|
|
12
|
+
*/
|
|
13
|
+
export interface ActionConfirmation {
|
|
14
|
+
v: 1;
|
|
15
|
+
type: 'action_confirmation';
|
|
16
|
+
/** The owner's `did:key`. */
|
|
17
|
+
owner_id: string;
|
|
18
|
+
/** `sha256:…` hash of the exact action `{tool, args}` being confirmed. */
|
|
19
|
+
action_hash: string;
|
|
20
|
+
confirmed_at: string;
|
|
21
|
+
/** Ed25519 by the owner over JCS of the confirmation without `sig`. */
|
|
22
|
+
sig: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Owner signs an action hash. Only the owner's key can produce this. */
|
|
26
|
+
export function createConfirmation(
|
|
27
|
+
fields: { ownerId: string; actionHash: string; confirmedAt: string },
|
|
28
|
+
ownerPrivateKey: Uint8Array,
|
|
29
|
+
): ActionConfirmation {
|
|
30
|
+
const unsigned = {
|
|
31
|
+
v: 1 as const,
|
|
32
|
+
type: 'action_confirmation' as const,
|
|
33
|
+
owner_id: fields.ownerId,
|
|
34
|
+
action_hash: fields.actionHash,
|
|
35
|
+
confirmed_at: fields.confirmedAt,
|
|
36
|
+
};
|
|
37
|
+
const sig = ed25519.sign(canonicalBytes(unsigned), ownerPrivateKey);
|
|
38
|
+
return { ...unsigned, sig: encodeSig(sig) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Verify a confirmation's signature against the owner DID it names. */
|
|
42
|
+
export function verifyConfirmation(c: ActionConfirmation): boolean {
|
|
43
|
+
if (c?.v !== 1 || c.type !== 'action_confirmation') return false;
|
|
44
|
+
const pub = publicKeyFromDid(c.owner_id);
|
|
45
|
+
const sig = decodeSig(c.sig);
|
|
46
|
+
if (!pub || !sig) return false;
|
|
47
|
+
const { sig: _omitted, ...unsigned } = c;
|
|
48
|
+
try {
|
|
49
|
+
return ed25519.verify(sig, canonicalBytes(unsigned), pub);
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/cosign.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519';
|
|
2
|
+
import { canonicalBytes, decodeSig, encodeSig } from './encoding.js';
|
|
3
|
+
import { publicKeyFromDid } from './keys.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Infrastructure co-signature over an agent's receipt (SPEC §14 — segregation of duties).
|
|
7
|
+
*
|
|
8
|
+
* Gordon's Q1: the agent that does the task cannot be the sole party reporting it.
|
|
9
|
+
* "Connect" — the deterministic rails that are the only door to the outside world —
|
|
10
|
+
* holds its own `did:key` and co-signs every receipt. A second, independent signature
|
|
11
|
+
* from a party the agent can't control (and, later, that holds the provider
|
|
12
|
+
* credentials) makes acting-without-recording structurally catchable. Reconciliation
|
|
13
|
+
* (`reconcile`) covers anything that still slips around the rails.
|
|
14
|
+
*
|
|
15
|
+
* This is a signed artifact carried in a normal receipt — no server or schema change.
|
|
16
|
+
*/
|
|
17
|
+
export interface ReceiptCoSignature {
|
|
18
|
+
v: 1;
|
|
19
|
+
type: 'receipt_cosign';
|
|
20
|
+
/** The co-signer's `did:key` (Connect / the rails) — MUST differ from `agent_id`. */
|
|
21
|
+
cosigner_id: string;
|
|
22
|
+
/** Hash of the agent receipt being co-signed. */
|
|
23
|
+
receipt_hash: string;
|
|
24
|
+
/** The agent (the doer) whose receipt this is. */
|
|
25
|
+
agent_id: string;
|
|
26
|
+
cosigned_at: string;
|
|
27
|
+
sig: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Connect co-signs an agent's receipt. Only the co-signer's key can produce this. */
|
|
31
|
+
export function createCoSignature(
|
|
32
|
+
fields: { cosignerId: string; receiptHash: string; agentId: string; cosignedAt: string },
|
|
33
|
+
cosignerPrivateKey: Uint8Array,
|
|
34
|
+
): ReceiptCoSignature {
|
|
35
|
+
const unsigned = {
|
|
36
|
+
v: 1 as const,
|
|
37
|
+
type: 'receipt_cosign' as const,
|
|
38
|
+
cosigner_id: fields.cosignerId,
|
|
39
|
+
receipt_hash: fields.receiptHash,
|
|
40
|
+
agent_id: fields.agentId,
|
|
41
|
+
cosigned_at: fields.cosignedAt,
|
|
42
|
+
};
|
|
43
|
+
const sig = ed25519.sign(canonicalBytes(unsigned), cosignerPrivateKey);
|
|
44
|
+
return { ...unsigned, sig: encodeSig(sig) };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Verify a co-signature. Requires a valid signature by `cosigner_id` AND that the
|
|
49
|
+
* co-signer is a **distinct** party from the acting agent (segregation of duties).
|
|
50
|
+
*/
|
|
51
|
+
export function verifyCoSignature(c: ReceiptCoSignature): boolean {
|
|
52
|
+
if (c?.v !== 1 || c.type !== 'receipt_cosign') return false;
|
|
53
|
+
if (c.cosigner_id === c.agent_id) return false; // doer must not co-sign itself
|
|
54
|
+
const pub = publicKeyFromDid(c.cosigner_id);
|
|
55
|
+
const sig = decodeSig(c.sig);
|
|
56
|
+
if (!pub || !sig) return false;
|
|
57
|
+
const { sig: _omitted, ...unsigned } = c;
|
|
58
|
+
try {
|
|
59
|
+
return ed25519.verify(sig, canonicalBytes(unsigned), pub);
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -48,9 +48,21 @@ export {
|
|
|
48
48
|
createReceipt,
|
|
49
49
|
receiptHash,
|
|
50
50
|
hashPayload,
|
|
51
|
+
saltedPayloadHash,
|
|
52
|
+
verifyPayload,
|
|
51
53
|
verifyReceipt,
|
|
52
54
|
verifyReceiptSignature,
|
|
53
55
|
verifyReceiptChain,
|
|
54
56
|
type Receipt,
|
|
55
57
|
type VerifyResult,
|
|
56
58
|
} from './receipt.js';
|
|
59
|
+
export {
|
|
60
|
+
createConfirmation,
|
|
61
|
+
verifyConfirmation,
|
|
62
|
+
type ActionConfirmation,
|
|
63
|
+
} from './confirmation.js';
|
|
64
|
+
export {
|
|
65
|
+
createCoSignature,
|
|
66
|
+
verifyCoSignature,
|
|
67
|
+
type ReceiptCoSignature,
|
|
68
|
+
} from './cosign.js';
|
package/src/receipt.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ed25519 } from '@noble/curves/ed25519';
|
|
2
2
|
import { sha256 } from '@noble/hashes/sha256';
|
|
3
|
-
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
|
+
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils';
|
|
4
4
|
import { canonicalBytes, decodeSig, encodeSig, jcsHash } from './encoding.js';
|
|
5
5
|
import { publicKeyFromDid } from './keys.js';
|
|
6
6
|
import { scopesAllow, verifyChain, type DelegationCert } from './delegation.js';
|
|
@@ -14,6 +14,14 @@ export interface Receipt {
|
|
|
14
14
|
payload_hash: string;
|
|
15
15
|
ts: string;
|
|
16
16
|
prev: string | null;
|
|
17
|
+
/** Optional provenance (SPEC §3). Present when set; covered by the signature. */
|
|
18
|
+
runtime_hash?: string;
|
|
19
|
+
model_id?: string;
|
|
20
|
+
/** Hash of the product's tool/scope manifest that governed this action. */
|
|
21
|
+
manifest_hash?: string;
|
|
22
|
+
/** Optional pseudonymous data-subject tag (SPEC §3) — lets the end user the action was
|
|
23
|
+
* about find + audit their own slice (`@zanii/subject`). Covered by the signature. */
|
|
24
|
+
subject_tag?: string;
|
|
17
25
|
sig: string;
|
|
18
26
|
}
|
|
19
27
|
|
|
@@ -27,6 +35,36 @@ export function hashPayload(payload: Uint8Array | string): string {
|
|
|
27
35
|
return `sha256:${bytesToHex(sha256(bytes))}`;
|
|
28
36
|
}
|
|
29
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Privacy-preserving payload commitment: `sha256(nonce ‖ payload)`. A bare
|
|
40
|
+
* `hashPayload` of a low-entropy payload ("phone X, amount 500") is dictionary-
|
|
41
|
+
* attackable from the public anchored log, and a linkable hash can itself be
|
|
42
|
+
* personal data (PDPL). The per-receipt `nonce` is kept operator-side (NOT in the
|
|
43
|
+
* receipt), so the on-chain value reveals nothing; keep it to later prove the
|
|
44
|
+
* payload with `verifyPayload`. If `nonce` is omitted a fresh 16-byte one is drawn.
|
|
45
|
+
*/
|
|
46
|
+
export function saltedPayloadHash(
|
|
47
|
+
payload: Uint8Array | string,
|
|
48
|
+
nonce?: string,
|
|
49
|
+
): { payload_hash: string; nonce: string } {
|
|
50
|
+
const nonceHex = nonce ?? bytesToHex(randomBytes(16));
|
|
51
|
+
const nonceBytes = hexToBytes(nonceHex);
|
|
52
|
+
const payloadBytes = typeof payload === 'string' ? new TextEncoder().encode(payload) : payload;
|
|
53
|
+
const combined = new Uint8Array(nonceBytes.length + payloadBytes.length);
|
|
54
|
+
combined.set(nonceBytes, 0);
|
|
55
|
+
combined.set(payloadBytes, nonceBytes.length);
|
|
56
|
+
return { payload_hash: `sha256:${bytesToHex(sha256(combined))}`, nonce: nonceHex };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Prove a payload matches a salted `payload_hash`, given the nonce kept at record time. */
|
|
60
|
+
export function verifyPayload(payload: Uint8Array | string, nonce: string, payloadHash: string): boolean {
|
|
61
|
+
try {
|
|
62
|
+
return saltedPayloadHash(payload, nonce).payload_hash === payloadHash;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
30
68
|
export function createReceipt(
|
|
31
69
|
fields: {
|
|
32
70
|
agentId: string;
|
|
@@ -36,6 +74,12 @@ export function createReceipt(
|
|
|
36
74
|
payloadHash: string;
|
|
37
75
|
ts: string;
|
|
38
76
|
prev: string | null;
|
|
77
|
+
/** Optional provenance — included in the signed receipt only when provided. */
|
|
78
|
+
runtimeHash?: string;
|
|
79
|
+
modelId?: string;
|
|
80
|
+
manifestHash?: string;
|
|
81
|
+
/** Optional pseudonymous data-subject tag — per-subject auditability (`@zanii/subject`). */
|
|
82
|
+
subjectTag?: string;
|
|
39
83
|
},
|
|
40
84
|
agentPrivateKey: Uint8Array,
|
|
41
85
|
): Receipt {
|
|
@@ -49,6 +93,10 @@ export function createReceipt(
|
|
|
49
93
|
payload_hash: fields.payloadHash,
|
|
50
94
|
ts: fields.ts,
|
|
51
95
|
prev: fields.prev,
|
|
96
|
+
...(fields.runtimeHash ? { runtime_hash: fields.runtimeHash } : {}),
|
|
97
|
+
...(fields.modelId ? { model_id: fields.modelId } : {}),
|
|
98
|
+
...(fields.manifestHash ? { manifest_hash: fields.manifestHash } : {}),
|
|
99
|
+
...(fields.subjectTag ? { subject_tag: fields.subjectTag } : {}),
|
|
52
100
|
};
|
|
53
101
|
const sig = ed25519.sign(canonicalBytes(unsigned), agentPrivateKey);
|
|
54
102
|
return { ...unsigned, sig: encodeSig(sig) };
|