For Coding Agents¶
Prescriptive reference for agents. Two audiences: agents that use a monitor instance (verify evidence, call the
API; sections marked use) and agents that change the monitor in iscc/iscc-monitor (marked change).
Full Markdown of this site: llms-full.txt.
Terms¶
Use these words exactly. The codebase, the UI and these docs use them the same way.
| Term | Meaning | Do not say |
|---|---|---|
| Monitor instance | One running deployment, such as monitor.iscc.io |
"the monitor" for one host |
| Hub status | verified, unresolvable, unverified, frozen or inactive |
|
| Self-consistency violation | Two checkpoints from one hub that fail RFC 6962 consistency; freezes the hub | inconsistency, fork |
| Split view | A hub showing different histories to different audiences; found by comparison only | fork, equivocation |
| Comparison anchor | The monitor's role as a signed reference to compare a client's (size, root) against |
witness |
| Bitcoin anchoring | OpenTimestamps proof of a checkpoint root in Bitcoin; the only meaning of "anchor" | |
| Coverage | monitored_since (size and time) to now; no guarantee before it |
|
| Proof bundle | Self-contained JSON evidence a client verifies without the monitor | receipt |
| verify-for-me | /<hub>/log/verify: the monitor's own verdict, the weaker path |
|
| Declaration / Deletion | Log records with note.$schema iscc-note-0.8.0 / iscc-note-delete-0.8.0 |
|
| Redaction | A hub's local suppression; writes no record, the monitor never sees it | deletion |
Decision dispatch (use)¶
| Goal | Use |
|---|---|
| Prove an ISCC-ID is in its hub's log, trusting nobody | GET /inclusion/<iscc_id>.bundle, then verify it yourself (recipe below) |
| Show a person the evidence | GET /inclusion/<iscc_id>, the HTML certificate |
| Quick yes/no, trusting the instance | GET /<hub>/log/verify?iscc_id=<id> |
Check your own (size, root) for a split view |
Compare with GET /<hub>/log/checkpoint; prove growth with consistency |
| Read a hub's log when the hub is offline | The tlog-tiles mirror: /<hub>/log/checkpoint, /<hub>/log/tile/... |
| Get one record's bytes | GET /<hub>/log/entries?index=<n> |
| Bitcoin timestamp of a root | GET /<hub>/log/checkpoint.ots[?size=<n>], then ots verify -d <root hex> |
| Machine-readable API description | GET /openapi.json or /openapi.yaml (OpenAPI 3.1) |
| Health, build, metrics | /healthz, /version, /metrics |
All routes are GET, need no authentication and send Access-Control-Allow-Origin: *. Every page:
HTTP API.
Constraints (use)¶
| Rule |
|---|
ISCC-ID inputs accept any case, surrounding whitespace and an ISCC: or did:iscc: prefix. |
The ISCC-ID itself names its hub (12-bit hub_id); the instance resolves it through the realm's Hub-List. |
"Cannot certify" answers are 200, not 4xx/5xx: the bundle route returns {"error": ..., "iscc_id": ...}, verify-for-me returns "verified": false with a reason. Check the body. |
| A bundle exists only after the instance verified the inclusion itself; it is never partial. |
anchor in a bundle is optional; it is present only when a confirmed Bitcoin anchor covers the record. |
key.revoked (RFC 3339) is present only when the hub's did:web document marks the key revoked. |
/<hub>/log/consistency?from=<n> accepts any n up to the accepted size; a larger n is 400 (the instance has not seen that state yet). |
checkpoint.ots answers 404 while the root is not yet anchored. |
A log's origin is <hub domain>/log, for example iscc.id/log. |
| Guarantees start at coverage start; an instance cannot vouch for what happened before it watched. |
Recipe: verify a proof bundle (use)¶
The documented script does every step: Verify a proof bundle
(uv run verify_bundle.py --resolve <file>). To implement it yourself, with the members from
Proof bundle format:
| Step | Check |
|---|---|
| 1 | key.multibase is base58btc (z…); decoded, it is 0xed 0x01 followed by the 32-byte Ed25519 public key. |
| 2 | checkpoint is a signed note: body lines origin, size, base64 root; a blank line; signature lines — <name> <base64>. |
| 3 | The signature base64 decodes to a 4-byte key hash and a 64-byte signature. Key hash = SHA-256(name + "\n" + 0x01 + pubkey)[:4]; it equals key.id (hex). |
| 4 | The Ed25519 signature verifies over the note body, up to and including its last newline before the blank line. |
| 5 | Leaf hash = SHA-256(0x00 + base64decode(record)). |
| 6 | Rebuild the root from the leaf, inclusion.leafIndex, inclusion.treeSize and inclusion.inclusionProof (RFC 9162 §2.1.3.2); it equals the checkpoint root and treeSize equals the checkpoint size. |
| 7 | Fetch https://<hub.domain>/.well-known/did.json yourself; key.multibase must be a publicKeyMultibase in it. |
| 8 | If anchor is present: steps 2–6 for its checkpoint, inclusion and key; then ots verify -d <anchor root in hex> on its base64-decoded ots (needs a Bitcoin node). |
Skipping step 7 leaves the monitor in the trust path.
Architecture map (change)¶
Go 1.26, CGO_ENABLED=0, one binary, configured only by ISCC_MONITOR_* environment variables, one SQLite file per
network.
| Path | Contains |
|---|---|
cmd/iscc-monitor |
main.go: config, realm loading, hub registration, HTTP mux, OTS loop, realm refresh |
cmd/wasm |
In-browser inclusion verifier, built to internal/web/verify.wasm |
cmd/notices |
Generates THIRD_PARTY_NOTICES.txt |
internal/config |
Typed, validated startup configuration |
internal/registry |
Hub-List YAML (or legacy domains file) parsing; AtomicHubList hub_id resolver |
internal/logclient |
Pure verification chain: checkpoint, did:web key, consistency, tiles, fsck, inclusion proofs |
internal/didweb |
did:web key resolution, revocation windows |
internal/follower |
Loop and PollHub: the per-hub poll pipeline; freeze; OTS stamping (OTSTick) |
internal/store |
SQLite: hubs, checkpoints, tiles, entry_bundles, follow_state, hub_keys, iscc_index, ots, violations; schema.sql plus user_version migrations |
internal/proof/verify |
Pure VerifyInclusion, shared by server and WASM |
internal/proofserve |
/<hub>/log/inclusion, consistency, entries, verify |
internal/tilesserve |
/<hub>/log/checkpoint, tile/..., checkpoint.ots |
internal/certificate |
/inclusion/<iscc_id> and its .bundle; Trust Seal |
internal/dashboard, dossier |
/ and /<hub> pages |
internal/chrome, badge, web |
Shared masthead and footer, status badge, /_ds/ static assets |
internal/openapi, docs |
openapi.yaml + openapi.json, /docs API reference page |
internal/index |
ISCC-IDv1 decoding and Canonical |
internal/ots, otsclient |
OpenTimestamps proofs and calendar transport |
internal/metrics, metricshttp, healthz, version, corsmw |
Operations routes and the CORS middleware |
docs/, zensical.toml |
This site |
.claude/ |
PRD, ADRs and design specs; not public docs |
Runtime: run() starts the HTTP server, the OTS loop and the hourly realm refresh as goroutines; follower.Loop runs
in the foreground and polls each hub (ISCC_MONITOR_NORMAL, or ISCC_MONITOR_FROZEN for frozen hubs).
Poll pipeline (follower.PollHub): fetch checkpoint → AcceptCheckpoint verdict (signature under the did:web key)
→ only if verified: mirror the tiles → RFC 6962 consistency against the prior accepted checkpoint → on a
violation, freeze the hub, keep both signed checkpoints and alert once; otherwise record the checkpoint and coverage,
advance the follow cursor, rebuild the root from the mirror (fsck) and record the root for OpenTimestamps. A frozen
hub is re-polled for evidence only and never advances.
Change playbook (change)¶
| If you change | Also |
|---|---|
An ISCC_MONITOR_* key |
docs/operate/configuration.md; internal/config has a test that fails when the two differ |
| A machine route | internal/openapi/openapi.yaml and openapi.json (tests check they agree and match the mux); docs/reference/http-api.md |
| An HTML route | docs/reference/http-api.md; keep it out of the OpenAPI document |
| The proof bundle | docs/reference/proof-bundle.md, docs/guides/verify_bundle.py, the example docs/reference/MEIGKV5N6NTVF4AB.bundle.json (the docs workflow runs the script on it) |
internal/proof/verify or cmd/wasm |
mise run build:wasm, then web.WasmVerifyHash; a test pins the hash of the committed file |
| A metric or a log line | docs/operate/monitoring.md |
| The database schema | Append a migration step (user_version); never edit an applied one; docs/operate/data.md |
| A dependency or vendored asset | mise run notices; a new vendored asset also goes into vendoredAssets in cmd/notices |
| Operator branding | docs/operate/branding.md |
| Anything user-visible | CHANGELOG.md, written by hand for readers |
| The public API, CLI or configuration | Update this page |
Gates: mise run check (build, vet, test) and gofmt -l . must be clean; for docs, mise run docs:build and
mise run docs:check must end with 0 errors, 0 warnings.
Common mistakes¶
NEVER treat a 200 from the bundle or verify route as success. ALWAYS read error, verified and reason.
NEVER trust the key in a bundle on its own. ALWAYS check it against the hub's did:web document.
NEVER call the Comparison anchor an "anchor" alone or a "witness". ALWAYS reserve "anchor" for Bitcoin anchoring.
NEVER claim the monitor detects split views on its own. ALWAYS say it detects self-consistency violations; split views need a comparison against it.
NEVER advance accepted state for a hub that is not verified, or for a frozen hub. ALWAYS keep frozen hubs
evidence-only.
NEVER add a request to another origin to a page (CDN, fonts, analytics). ALWAYS serve assets from /_ds/.
NEVER commit to main or create tags from an automated loop. ALWAYS release through a PR from develop
with a merge commit, as in CLAUDE.md.