diff --git a/public/ambient-verification.html b/public/ambient-verification.html
index 1473a75..e2c5e94 100644
--- a/public/ambient-verification.html
+++ b/public/ambient-verification.html
@@ -113,7 +113,7 @@
receipt structure
metadata.proof present
- canonicalization = json.sorted_keys.v1
+ canonicalization = json.sorted_keys.v1 (standard canonical JSON path) erc8211.merkle.v1 recognized at schema layer for interoperability metadata.trace optional for correlation across multi-step workflows and batch execution
hash_alg = SHA-256
hash_matches
signature_alg = Ed25519
diff --git a/public/api.html b/public/api.html
index c94bb41..e62bf95 100644
--- a/public/api.html
+++ b/public/api.html
@@ -54,7 +54,7 @@ API surfaces for verifiable agent actions.
Try Verifier
View Production Proof
- Production proof status: Runtime production is live and signs canonical Trust Verification receipts. MCP E2E against production runtime passes: STEP 1 SIGNED, STEP 2 VERIFIED, STEP 3 TAMPERED INVALID. Runtime signer is runtime.commandlayer.eth with kid=vC4WbcNoq2znSCiQ. Canonical proof is metadata.proof.canonicalization=json.sorted_keys.v1, metadata.proof.hash.alg=SHA-256, metadata.proof.signature.alg=Ed25519.
+ Production proof status: Runtime production is live and signs canonical Trust Verification receipts. MCP E2E against production runtime passes: STEP 1 SIGNED, STEP 2 VERIFIED, STEP 3 TAMPERED INVALID. Runtime signer is runtime.commandlayer.eth with kid=vC4WbcNoq2znSCiQ. Canonical proof uses metadata.proof with json.sorted_keys.v1 as the standard receipt path; erc8211.merkle.v1 is recognized at the schema layer for interoperability.
@@ -169,8 +169,8 @@ Input for verifier endpoints
}
- Canonical proof fields
- metadata.proof.canonicalizationmetadata.proof.hash.algmetadata.proof.hash.valuemetadata.proof.signature.algmetadata.proof.signature.valuemetadata.proof.signature.kidmetadata.proof.signer_id
+ Canonical proof fields metadata.proof is required. metadata.trace is optional for multi-step workflow correlation (agents, spans, solver fills, and batch execution).
metadata.proof.signature can be a single Ed25519 object or an array of role-based Ed25519 signature entries.
Allowed signature roles: user, solver, relayer, agent, runtime, verifier. Do not assume every signature in a multi-signature array is verified unless your verifier explicitly validates all of them.
+ metadata.proof.canonicalizationmetadata.proof.hash.algmetadata.proof.hash.valuemetadata.proof.signature (single object or role-based array)metadata.proof.signer_idmetadata.trace (optional)
diff --git a/public/canonical-receipts.html b/public/canonical-receipts.html
index 3bd1fed..17c031b 100644
--- a/public/canonical-receipts.html
+++ b/public/canonical-receipts.html
@@ -38,10 +38,12 @@
"value": "..."
},
"signer_id": "runtime.commandlayer.eth"
- }
+ },
+ "trace": { "trace_id": "trace_01", "span_id": "span_01" }
}
}
-Required proof fields metadata.proof.canonicalizationmetadata.proof.hash.algmetadata.proof.hash.valuemetadata.proof.signature.algmetadata.proof.signature.kidmetadata.proof.signature.valuemetadata.proof.signer_id
+Canonicalization and interoperability json.sorted_keys.v1 remains the standard canonical JSON receipt path.
erc8211.merkle.v1 is recognized at the schema layer and accepted for interoperability. Full local ERC-8211 Merkle cryptographic verification should only be claimed where implemented.
metadata.proof.signature supports a single Ed25519 signature object or a multi-signature array with roles for solver/relayer/batch workflows.
Allowed roles: user, solver, relayer, agent, runtime, verifier.
+Required proof fields metadata.proof.canonicalizationmetadata.proof.hash.algmetadata.proof.hash.valuemetadata.proof.signature (single object or array)metadata.proof.signer_idmetadata.trace (optional)
Verification lifecycle receipt received → structure checked → canonical payload rebuilt → SHA-256 hash recomputed → hash compared → Ed25519 signature verified → signer identity/kid checked → VALID or INVALID
Valid vs tampered Valid receipt verifier_status = VALIDhash_matches = truesignature_valid = true
Valid receipt accepted.
Tampered receipt verifier_status = INVALIDhash_matches = falsesignature_valid = false
Tampered receipt rejected.
Schema validation vs proof verification Schema validation checks shape. Proof verification checks truth. A schema-valid receipt can still be invalid if the hash or signature fails.
diff --git a/public/sdk-records.html b/public/sdk-records.html
index fd2d9d0..f02c730 100644
--- a/public/sdk-records.html
+++ b/public/sdk-records.html
@@ -57,7 +57,7 @@
- SDK Records
Add verifiable receipts to agent actions. @commandlayer/agent-sdk wraps agent actions and emits canonical metadata.proof receipts that can be checked by CommandLayer verifiers.
Wrap an agent action, emit a canonical receipt, and verify the proof. Runtime production is live. Runtime signs canonical Trust Verification receipts. MCP E2E against production runtime passes: STEP 1 SIGNED, STEP 2 VERIFIED, STEP 3 TAMPERED INVALID. Runtime signer: signer_id = runtime.commandlayer.eth, kid = vC4WbcNoq2znSCiQ. Canonical proof: metadata.proof.canonicalization = json.sorted_keys.v1, metadata.proof.hash.alg = SHA-256, metadata.proof.signature.alg = Ed25519.
+ SDK Records
Add verifiable receipts to agent actions. @commandlayer/agent-sdk wraps agent actions and emits canonical metadata.proof receipts that can be checked by CommandLayer verifiers.
Wrap an agent action, emit a canonical receipt, and verify the proof. Runtime production is live. Runtime signs canonical Trust Verification receipts. MCP E2E against production runtime passes: STEP 1 SIGNED, STEP 2 VERIFIED, STEP 3 TAMPERED INVALID. Runtime signer: signer_id = runtime.commandlayer.eth, kid = vC4WbcNoq2znSCiQ. Canonical proof keeps json.sorted_keys.v1 as the standard receipt path, while erc8211.merkle.v1 is accepted for interoperability at the schema layer. Signature shape supports single-signature and multi-signature proof entries for solver/relayer/batch workflows.
Install
Install the SDK package. Copy npm install @commandlayer/agent-sdk
@@ -80,13 +80,12 @@
const verified = await cl.verify(receipt);
-Receipt shape
Canonical metadata.proof fields. Copy metadata.proof.canonicalization = "json.sorted_keys.v1"
+Receipt shape
Canonical metadata.proof and metadata.trace fields. Copy metadata.proof.canonicalization = "json.sorted_keys.v1"
metadata.proof.hash.alg = "SHA-256"
metadata.proof.hash.value = "..."
-metadata.proof.signature.alg = "Ed25519"
-metadata.proof.signature.kid = "vC4WbcNoq2znSCiQ"
-metadata.proof.signature.value = "..."
-metadata.proof.signer_id = "runtime.commandlayer.eth"
+metadata.proof.signature = { alg: "Ed25519", kid: "vC4WbcNoq2znSCiQ", value: "..." } // single-signature proof
+metadata.proof.signer_id = "runtime.commandlayer.eth"
+metadata.trace = { trace_id: "trace_..." } // optional correlation metadata
What the SDK does
Wraps agent actions into consistent execution envelopes. Emits canonical receipts for each wrapped action. Normalizes canonical proof shape under metadata.proof. Calls verifier endpoints to confirm receipt validity. Helps developers integrate without rebuilding proof plumbing.
diff --git a/public/verify-badge-demo.html b/public/verify-badge-demo.html
index ec85b4a..5b62b74 100644
--- a/public/verify-badge-demo.html
+++ b/public/verify-badge-demo.html
@@ -141,7 +141,7 @@ What gets checked through the verifier
receipt structure
metadata.proof present
- json.sorted_keys.v1 canonicalization
+ json.sorted_keys.v1 canonicalization (standard JSON receipt path)erc8211.merkle.v1 canonicalization recognized at schema layer for interoperability
SHA-256 hash match
Ed25519 signature validity
signature.kid (vC4WbcNoq2znSCiQ)
@@ -149,8 +149,8 @@ What gets checked through the verifier
supported capability verb
tamper invalidation
- The canonical proof model is:
- metadata.proof.canonicalization = json.sorted_keys.v1metadata.proof.hash.alg = SHA-256metadata.proof.hash.valuemetadata.proof.signature.alg = Ed25519metadata.proof.signature.kid = vC4WbcNoq2znSCiQmetadata.proof.signature.valuemetadata.proof.signer_id = runtime.commandlayer.eth
+ Canonical proof model (single-signature example):
+ metadata.proof.canonicalization = json.sorted_keys.v1metadata.proof.hash.alg = SHA-256metadata.proof.hash.valuemetadata.proof.signature = { alg, kid, value }metadata.proof.signer_id = runtime.commandlayer.ethmetadata.trace = { trace_id, span_id? } (optional)
Multi-signature shape is also supported for solver/relayer/batch workflows via metadata.proof.signature[] entries with role values of user, solver, relayer, agent, runtime, or verifier.
Runtime endpoint: https://runtime.commandlayer.org.
diff --git a/tests/verifyReceipt-runtime.test.js b/tests/verifyReceipt-runtime.test.js
index 29c17e3..7e19569 100644
--- a/tests/verifyReceipt-runtime.test.js
+++ b/tests/verifyReceipt-runtime.test.js
@@ -54,6 +54,22 @@ async function makeRuntimeReceipt() {
return { receipt, rawPub };
}
+
+function isSingleProofSignature(signature) {
+ return Boolean(signature) && !Array.isArray(signature) && typeof signature === 'object';
+}
+
+function isMultiProofSignature(signature) {
+ return Array.isArray(signature);
+}
+
+function getPrimaryProofSignature(proof) {
+ const signature = proof?.signature;
+ if (isSingleProofSignature(signature)) return signature;
+ if (isMultiProofSignature(signature)) return signature[0] || null;
+ return null;
+}
+
function makeTextResolver(pub) {
return async (_ens, key) => ({
'cl.sig.pub': `ed25519:${pub}`,
@@ -92,14 +108,33 @@ test('wrong canonicalization rejects', async () => {
test('wrong kid rejects', async () => {
const { receipt, rawPub } = await makeRuntimeReceipt();
- receipt.metadata.proof.signature.kid = 'wrong';
+ const primarySignature = getPrimaryProofSignature(receipt.metadata.proof);
+ assert.ok(primarySignature);
+ primarySignature.kid = 'wrong';
const out = await verifyReceipt(receipt, { ens: { textResolver: makeTextResolver(rawPub) } });
assert.equal(out.status, 'INVALID');
});
test('legacy top-level proof does not verify', async () => {
const { receipt, rawPub } = await makeRuntimeReceipt();
- receipt.signature = { kid: 'vC4WbcNoq2znSCiQ', sig: receipt.metadata.proof.signature.value };
+ const primarySignature = getPrimaryProofSignature(receipt.metadata.proof);
+ assert.ok(primarySignature);
+ receipt.signature = { kid: 'vC4WbcNoq2znSCiQ', sig: primarySignature.value };
+ const out = await verifyReceipt(receipt, { ens: { textResolver: makeTextResolver(rawPub) } });
+ assert.equal(out.status, 'INVALID');
+});
+
+
+test('multi-signature proof shape does not crash runtime verifier', async () => {
+ const { receipt, rawPub } = await makeRuntimeReceipt();
+ const original = receipt.metadata.proof.signature;
+ receipt.metadata.proof.signature = [
+ { role: 'runtime', ...original },
+ { role: 'relayer', alg: 'Ed25519', kid: 'other', value: original.value },
+ ];
+ assert.equal(isMultiProofSignature(receipt.metadata.proof.signature), true);
+ const primarySignature = getPrimaryProofSignature(receipt.metadata.proof);
+ assert.ok(primarySignature);
const out = await verifyReceipt(receipt, { ens: { textResolver: makeTextResolver(rawPub) } });
assert.equal(out.status, 'INVALID');
});