Attestation
Cryptographic attestation of every response
Deterministic JSON serialization per RFC 8785 (JCS), then Ed25519 per RFC 8032.
Why attestation
Every Stratalize MCP and x402 JSON response that passes the signing path carries an Ed25519 signature over a JCS-canonicalized payload, allowing offline verification that bytes originated from Stratalize and were not altered after signing. The mechanism is standards-based (RFC 8785 + RFC 8032), not a proprietary MAC string.
The `_stratalize` envelope
The attestation object is attached at the top level as _stratalize alongside tool-specific fields (for example flat tool fields, or a data wrapper on HTTP x402 routes — always verify over the exact JSON object returned).
// Tool response (other tool-specific top-level fields omitted).
interface StratalizeAttestedResponse {
data?: unknown;
_stratalize: {
signed_at: string;
signature: string;
public_key_url: string;
canonicalization: "JCS-RFC8785";
version: "1";
unsigned?: true;
/**
* Per-agent signature over an agent-scoped payload (nested attestation).
* Q3 2026 — not yet emitted by production; do not rely on this field today.
*/
_agent?: {
agent_id: string;
signed_at: string;
signature: string;
public_key_url?: string;
canonicalization: "JCS-RFC8785";
version: "1";
};
};
}Implementation reference: lib/mcp/response-signer.ts. Signing strips _stratalize before canonicalization, then signs the UTF-8 bytes of the canonical string.
Verification algorithm
- Parse the response JSON. Read
_stratalize.signature,signed_at, andpublic_key_url. - If
unsigned: true, treat the response as not cryptographically attested. - Build a shallow copy of the response object and delete
_stratalize. The signed material is the remaining object only (RFC 8785 JCS via the same canonicalizer Stratalize uses in production). - Compute
canonical = canonicalize(payloadWithoutEnvelope). If canonicalization fails, verification fails. - Fetch the SPKI public key:
GET public_key_urlreturns JSON with apublic_keyfield (base64-encoded SubjectPublicKeyInfo DER for Ed25519). Cache aggressively; honorCache-Controlon that endpoint. - Verify Ed25519:
VERIFY( publicKey, UTF-8(canonical), base64url_decode(signature) )per RFC 8032. Use your crypto library’s Ed25519 verify primitive (Node:crypto.verifywith SPKI). - If verification fails, treat the payload as untrusted for compliance or billing evidence.
TypeScript verifier
Node 20+. Uses the same canonicalize package and SPKI + base64url wire format as lib/mcp/response-signer.ts. Install: npm install canonicalize.
import canonicalize from "canonicalize";
import { createPublicKey, verify } from "node:crypto";
export interface VerificationResult {
valid: boolean;
signedAt?: string;
reason?: string;
}
export async function verifyStratalizeResponse(
response: Record<string, unknown>,
publicKeySpkiBase64?: string,
): Promise<VerificationResult> {
const env = response["_stratalize"];
if (env === null || typeof env !== "object" || Array.isArray(env)) {
return { valid: false, reason: "missing _stratalize envelope" };
}
const att = env as Record<string, unknown>;
if (att["unsigned"] === true) {
return { valid: false, reason: "envelope marked unsigned" };
}
const sig = att["signature"];
if (typeof sig !== "string" || !sig) {
return { valid: false, reason: "missing signature" };
}
const signedAt = typeof att["signed_at"] === "string" ? att["signed_at"] : undefined;
let spki = publicKeySpkiBase64;
if (!spki) {
const url = att["public_key_url"];
if (typeof url !== "string" || !url) return { valid: false, reason: "missing public_key_url" };
const res = await fetch(url);
if (!res.ok) return { valid: false, reason: `fetch signing key HTTP ${res.status}` };
const doc = (await res.json()) as { public_key?: string };
if (typeof doc.public_key !== "string")
return { valid: false, reason: "signing-key JSON missing public_key (SPKI base64)" };
spki = doc.public_key;
}
const body: Record<string, unknown> = { ...response };
delete body["_stratalize"];
const canonical = canonicalize(body);
if (canonical === undefined) return { valid: false, reason: "JCS canonicalize returned undefined" };
try {
const publicKey = createPublicKey({
key: Buffer.from(spki, "base64"),
format: "der",
type: "spki",
});
const ok = verify(null, Buffer.from(canonical, "utf8"), publicKey, Buffer.from(sig, "base64url"));
return ok ? { valid: true, signedAt } : { valid: false, signedAt, reason: "Ed25519 verify failed" };
} catch (e) {
return { valid: false, reason: e instanceof Error ? e.message : "verify threw" };
}
}Public key distribution
The active verification key is published at https://www.stratalize.com/api/trust/signing-key as JSON: public_key (SPKI DER, base64), algorithm: "Ed25519", key_version, and usage. This is not a JWKS document; consumers must import SPKI as shown above.
Rotation. Keys rotate on a quarterly cadence for operational hygiene. After rotation, prior keys remain valid for verifying historically signed responses for one year. The envelope’s public_key_url always points at the current fetch path; callers should key cache entries by key_version when present in the signing-key JSON.
What signatures prove (and do not)
Ed25519 signatures prove
- Provenance — the canonicalized payload was processed by Stratalize code holding the signing key at
signed_at(subject to your trust model for key custody). - Integrity — any bit flip in the signed JSON object after signing breaks verification.
- Non-repudiation (operational) — Stratalize cannot plausibly deny having emitted this exact canonical byte sequence without compromising the private key.
They do not prove
- Upstream factual accuracy — the signature covers Stratalize’s output, not independent truth of every numeric field against the physical world or third-party systems.
- Data freshness as-of external sources —
signed_atis when the response was signed, not a universal timestamp for every upstream feed’s last refresh. - Future replay or stability — the same logical question at a later time may yield a different signed object as inputs change; signatures are not a commitment to repeatability.
- Client or transport safety — TLS still protects bytes in flight; attestation binds the JSON object Stratalize intended to return, not your local storage after you transform it.
Audit lineage
Each signed production response is suitable for inclusion in Stratalize’s audit records (response fingerprint, signing time, tool name, org tenancy where applicable). Synthesis and governance lineage views for authenticated organizations are exposed through org MCP tools documented on /docs/mcp; this page does not specify internal hash algorithms beyond “cryptographic” or database schemas for lineage tables.
Related: x402, Tool catalog, Docs home.