Skip to content

Hosted verifier

Sill runs a hosted, in-browser audit-bundle verifier at sill.so/verify. Drag an exported bundle onto the page and it confirms the bundle was signed by Sill and has not been altered since. The verifier is entirely client-side: the uploaded file never leaves your browser, and the only network request the page makes is a GET for Sill’s audit-envelope public key.

The hosted verifier runs the exact same recipe documented in Verify a signature — ed25519 over an RFC 8785 (JCS) canonical form, SHA-256 for chain and Merkle inputs — implemented with pure-JS libraries. It is a convenience layer over the open standard, not a substitute for it. Anyone who prefers to verify independently, or who wants verification in CI, should use the manual recipe directly.

  1. Drop the bundle. The exported audit bundle is an HTML file (the .html variant of GET /v1/audit/{record_id}/bundle, or the same file downloaded from the dashboard’s Export JSON action’s HTML variant). Drop it onto the page. The verifier extracts the canonical signed JSON from the <script type="application/sill+json" id="sill-bundle"> block embedded in the HTML.
  2. The verifier fetches Sill’s public key. A single GET https://sill.so/.well-known/sill-audit-envelope-v1.pub retrieves the current and any rotated/historical audit-envelope public keys. This is the only outbound network request; the bundle itself is never uploaded.
  3. The verifier checks the four invariants described below and reports the result. A successful bundle shows every record verified and the chain intact.

For each dropped bundle the verifier confirms, in order:

  1. Key match. The bundle’s signing_key_id resolves to a public key from sill.so/.well-known/sill-audit-envelope-v1.pub — either the currently-published audit-envelope key or a rotated/historical one. A bundle signed by a key Sill has never published is rejected.
  2. Envelope signature. The bundle envelope’s ed25519 signature is recomputed from the canonical body (SHA-256(utf8("sill-audit-bundle-v1") || 0x00 || JCS(body))) and verified against the matched public key. See Audit envelope for the full signing recipe.
  3. Per-record signatures. Each record’s envelope_signature is verified against the same key over its own JCS-canonical form (with envelope_signature and merkle_root stripped, per the record recipe).
  4. Chain linkage. Each record’s prev_record_hash is recomputed as SHA-256(JCS(previous_record_with_pending_merkle_root)) and compared to the value the record carries. A single flipped byte anywhere in a record or its predecessor breaks this link.

If every step passes, the verifier reports the bundle as genuine and unaltered. The mechanical invariants above prove the bundle has not been altered; the semantic scope of what each record’s signature attests — the verified intent projection, the rail-neutral settlement outcome, the pseudonymous human-overseer identity on HITL records — is documented under Audit envelope: What the signed record binds.

The bundle file is read and processed inside your browser tab. It is never uploaded to Sill, never posted to any endpoint, and never leaves your machine as network traffic. The single network request the verifier issues is a GET for the public key at https://sill.so/.well-known/sill-audit-envelope-v1.pub, which is non-secret and served with a permissive Access-Control-Allow-Origin: * header.

This is the load-bearing trust property. A reviewer — auditor, counsel, counterparty — does not have to trust Sill’s word to confirm that a Sill-produced bundle is genuine. They fetch the public key Sill publishes, run open crypto against the bytes in the file, and see the answer themselves. Sill sees nothing.

A Sill audit bundle is a per-(site, decision-class, UTC-day) slice of an append-only chain that predates and continues past it. Except on the very first day of a chain’s life, the first record in a bundle links to a record in the previous batch — one that is not in this file.

The hosted verifier handles this correctly. When it walks the chain and reaches the first record’s prev_record_hash, it recognizes that the predecessor is expected to live in the previous batch and reports the bundle as verified with a note that the first record “links to the batch before it, which isn’t in this file. That’s expected.” This is not a verification failure. The per-record signature on the first record still verifies against the JWKS key, and the chain link is a well-formed hash — it just cannot be reconstructed from the bytes in this bundle alone.

Full cross-batch continuity — every prev_record_hash verified back to the chain’s genesis — requires the complete chain. Sill can provide the adjacent batches on request. For most reviewer workflows (a single bundle attached to an incident or a request) the per-bundle result is what the reviewer needs.

The hosted verifier is the quickest path to a yes/no on a single bundle. Reach for the manual recipe when you want:

  • CI verification. Run the recipe as part of a scheduled job against archived bundles.
  • Independence from Sill’s site. The recipe uses only the public key at sill.so/.well-known/sill-audit-envelope-v1.pub (or a locally-pinned copy) and off-the-shelf ed25519 / SHA-256 / JCS libraries. Nothing else on sill.so is in the trust path.
  • Cross-batch verification. A verifier that ingests every consecutive batch can walk prev_record_hash back to the chain’s genesis and confirm continuity end-to-end.
  • Language of choice. The recipe is language-agnostic; any reputable ed25519 library and any RFC 8785 canonicalizer will do.

The hosted verifier just runs the same recipe for you.

  • Verify a signature — the manual recipe the hosted verifier implements.
  • Audit envelope — the record shape, chain, and Merkle batch the verifier walks.
  • Public JWKS — the edge JWKS used to verify the per-site agent card and ARD trust manifest; the audit-envelope public key lives separately at sill.so/.well-known/sill-audit-envelope-v1.pub.
  • Audit log and export — how to produce the bundle the verifier consumes.