Proposed (STA-823, STA-827). It implements
0212 decision 3 (a KMS-signed
daily checkpoint,
stackra audit:verify checks the chain).Context
Audit checkpoints and proof bundles carried chain hashes but no signature,stackra audit:verify did not exist, and
each chain writer (access-log, OS audit) kept its own canonical JSON, so a verifier had nothing single to agree with.
Decision
- One canonical form.
@stackra/supportexportscanonicalJson(object keys sorted by UTF-16 code unit at every depth,undefinedmembers dropped, array order kept,undefineditems and non-finite numbers asnull, dates as ISO 8601 UTC) andcanonicalHash(its SHA-256, 64 lowercase hex). The form is the one access-log already chained over, so stored chains still verify, and it never changes. - A Signer port.
@stackra/contractshasSigner(sign(bytes),publicKey()) with one algorithm, ES256 (ECDSA P-256, SHA-256). ASignatureis{ keyId, algorithm, value }with the 64-byter || svalue in base64url; aSignerPublicKeyis{ keyId, algorithm, spki }. Drivers: memory (Web Crypto) andaws-kms, which signs the SHA-256 digest inside KMS so the private key never leaves it.verifySignaturechecks offline and never throws on bad input. - What is signed. A checkpoint is
{ sequence, hash, takenAt }(takenAtas ISO 8601); its signature is overcanonicalJson(checkpoint)as UTF-8. A link’s hash iscanonicalHash(link)where the link holdssequence,previousHashand the record fields; the first link’spreviousHashis 64 zeros. - The proof bundle and
stackra audit:verify. A bundle is JSON Lines of{"key": SignerPublicKey},{"link": {...}, "hash": "…"}and{"checkpoint": {...}, "signature": {...}}.stackra audit:verify <bundle> [--key <keys.json>]rehashes every link, checks each follows the one before (the first from the genesis hash or a signed checkpoint just before it), checks each checkpoint’s signature and that it matches its link, and exits 1 at the first broken sequence. With--keyonly the caller’s keys count; without, it says the check used the bundle’s own keys.
Consequences
- OS audit signs its daily checkpoint through
Signerand writes bundles in this form; access-log and audit hash withcanonicalHash. @stackra/clidepends on@stackra/support(the lockfile gains that link).
Enforcement
packages/foundation/support/src/core/functions/__tests__/canonical-json.function.test.ts,
packages/foundation/contracts/src/core/drivers/__tests__/aws-kms-signer.driver.test.ts,
packages/developer/cli/src/node/functions/__tests__/verify-audit-proof.function.test.ts.
Supersedes and sources
Supersedes: none. Sources: OSservices/audit/SPEC.md gaps 4 and 8; STA-823; STA-827.