One chain.
Every proof.

Every product below writes into the same tamper-evident SHA-256 chain. Most of what follows needs no account at all — the proof routes are open on purpose, because a proof you can only check with the prover's permission is not a proof. Base URL: https://sebbi.pro

The stack — drag to spin, tap a layer
auto-rotating · tap any panel to jump to its reference
ONE
CHAIN
QuickstartNotaryDecision engineThe chainCompleteness & consistencyReplay & lineageAuthorityExported proofWitness networkOrdering testPacksOn-premiseClient librariesFree toolsErrors

Quickstart — seal something in one call

No account needed. Fingerprint your content locally, send the hash, get a sealed receipt back:

curl -X POST https://sebbi.pro/api/post/seal \
  -H "Content-Type: application/json" \
  -d '{"fingerprint":"<64-char sha-256 of your content>"}'
{
  "sealed":      true,
  "seal":        "43ac8582…",   // the chain block hash
  "block_index": 1042,
  "sealed_at":   1789420000.12,
  "code":        "43ac85820f19"   // 12-char public verify code
}
Your content never leaves your machine. You hash it locally; only the 64-character fingerprint is sent. The chain proves a document with that exact fingerprint existed at that moment — it never sees the document itself.

Notary — seal a fingerprint of anything no key

Three notaries, one pattern: POST a fingerprint to seal, GET to verify. All public, all free. Re-sealing the same fingerprint returns the original receipt with already_registered: true.

Post notary — prove exact text existed

POST /api/post/seal — body {"fingerprint":"<sha256>"}.

GET /api/verify-post?content=<sha256> — returns {"verified":true,"block_index":…,"sealed_at":…,"seal":…}, else {"verified":false}.

Identity notary — prove a profile is the original

POST /api/identity/seal — body {"fingerprint":"<sha256>", "public":true, "profile":{…}}. With public set, a limited display set (name, title, bio, linkedin, facebook, org) is stored so a checker can show them; otherwise only the fingerprint is sealed.

GET /api/identity/check?code=<12+ chars> — short code or full fingerprint.

Payment notary — stop invoice fraud

POST /api/payment/seal — body {"fingerprint":"<sha256 of the real bank details>", "display":{"business":…,"sort_masked":…,"account_masked":…}}. Only masked display fields are stored; the true details never are.

GET /api/payment/check?code=<12+ chars>&fp=<optional full sha256> — returns MATCH, MISMATCH or NO_SEAL. Either way the check itself is sealed and returned as a receipt, giving provable evidence of the check under PSR reimbursement rules.

Wire it into your stack

# 1. fingerprint locally — content stays with you
import hashlib, requests
fp = hashlib.sha256(content.encode()).hexdigest()

# 2. seal the fingerprint
r = requests.post("https://sebbi.pro/api/post/seal",
                  json={"fingerprint": fp}).json()
code = r["code"]        # store this next to your record

# 3. anyone verifies later — no account
v = requests.get("https://sebbi.pro/api/verify-post",
                 params={"content": fp}).json()

Put that where your system creates the thing worth proving — a post published, an invoice issued, a profile created — and every record from then on carries tamper-evident proof automatically.

Decision engine — POST /api/govern POSTBearer key

Score an event, get a verdict, sealed before the response returns. Deterministic: identical inputs always produce identical outputs.

curl -X POST https://sebbi.pro/api/govern \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id":    "user_123",
    "action":     "payment",
    "amount":     49.99,
    "country":    "UK",
    "device_id":  "dev_abc",
    "anomaly":    0.1,
    "device_risk":0.05
  }'
FieldTypeMeaning
user_idstringYour stable identifier for the acting user. Trust is learned per user_id.
actionstringWhat they're doing — payment, login, message, anything.
amountnumberMonetary value if relevant, else 0. Log-scaled internally.
countrystringISO-style code. Country changes and off-allowlist jurisdictions raise score.
device_idstringDevice identifier for velocity correlation.
anomaly0–1Your behavioural-anomaly signal, if you have one. 0 if not.
device_risk0–1Your device-risk signal, if you have one. 0 if not.

Verdicts: score < 0.35 → ALLOW · < 0.70 → CHALLENGE · else BLOCK. Every verdict carries plain-language reasons. Trust is earned slowly on ALLOW and lost eight times faster on BLOCK, so burst attacks self-amplify.

Challenges — the exception workflow is built in

{
  "decision": "CHALLENGE",
  "challenge_url":        "https://sebbi.pro/verify-challenge?token=…",
  "challenge_status_url": "https://sebbi.pro/api/challenge/status?token=…",
  "challenge_expires_in": 900
}

Surface challenge_url to your user; they confirm or deny on the hosted page; the resolution is sealed as its own block; you poll the status URL until resolved: true. Tokens are stateless and HMAC-signed. Three lines: if CHALLENGE → surface URL → poll.

Try it with no key at all

The demo module runs the real engine with no account on any route — it is what the public Proving Ground is built on.

POST /x/demo/governA real decision through the live engine, sealed into the production chain.
POST /x/demo/reviewOpens a review case with the verdict withheld and a server-measured clock running.
POST /x/demo/commitCommits your call before the machine verdict is revealed.
GET /x/demo/statsAggregates across everyone who has tried it, including how many committed in under two seconds.

The chain — receipts, sequences, inclusion mostly no key

Every govern response includes receipt_seq: a per-key sequence issued in the same transaction as the chain write, gapless by construction. Store them. If you ever hold receipts 46 and 48 with no 47, a record has been omitted — provable by arithmetic.

The two failure modes are separate on purpose. Edited records break the chain. Missing records break the sequence. A system that only detects one of those is only half a record.
EndpointReturns
GET /api/verify-chainno key Whole-chain integrity: {"valid":true,"blocks":N,"tip":…}. Anyone can run it.
GET /api/inclusion?hash=no key Whether a full 64-char receipt hash is sealed, with block index and sequence.
GET /api/coveragekey Reconciliation in one call: receipts issued vs blocks sealed, complete: true/false.
GET /api/pulsekey Your last hour — verdict mix, recent decisions with reasons, chain tip.
GET /api/regulation-mapno key Versioned, hash-sealed map of engine features to legal obligations.
GET /api/specno key This API describing itself, machine-readable.
GET /x/statsno key Public aggregates — chain growth, verdict mix, dwell distribution. Nothing scoped to a customer key.

Anchoring — and what "anchored" actually means here

Chain tips are timestamped into Bitcoin through OpenTimestamps. A proof is pending when the calendar accepts it and confirmed only once the transaction lands and the proof is upgraded. Both states are reported as what they are, everywhere, because your own verifier will say it first.

GET /x/ots/statusno key Proof counts, pending vs confirmed, first attempt date.
GET /x/ots/listno key Every proof on file.
GET /x/ots/proofno key The raw .ots bytes, base64. Verify with the standard opentimestamps client — nothing of ours required.
POST /x/ots/upgradekey Re-fetches proofs from the calendars to confirm them. Also runs hourly on its own.

Completeness and consistency — prove what isn't there no key

Anyone can show you a log of what happened. The hard questions are whether anything is missing, and whether the log you were shown last quarter is the same log you are being shown now.

complete — inclusion, absence, completeness

A sorted Merkle tree per period, with the leaf count committed before any export is requested. Absence is proved by returning two adjacent leaves with consecutive indices: nothing can sit between them. Erasure is handled by tombstone — the payload is deleted by your system, the leaf is retained, the erasure event is sealed. That proves a record existed and was erased while holding none of its content.

GET /x/complete/periodsWhich periods are committed, and how many days after each closed.
GET /x/complete/rootThe committed root and exact leaf count for a period.
GET /x/complete/prove?period=&value=Inclusion proof, or an absence proof with the two neighbouring leaves.
GET /x/complete/spec · /verifyThe rules, and a checker.
POST /x/complete/commit · /erasekey Only closed periods commit, and each commits once.

consistency — append-only, demonstrated not asserted

An ordered RFC 6962 Merkle tree over every audit hash in write order, deliberately unmodified so existing Certificate Transparency verifiers work against it.

GET /x/consistency/rootCurrent tree size and root.
GET /x/consistency/ancestor?tip=Hold any tip we ever served and prove it is still on this chain. A fork returns 409 with the evidence.
GET /x/consistency/proof?first=&second=RFC 6962 consistency proof that the log at one size is a prefix of the log at another.
POST /x/consistency/checkpointkey Seals the current size and root into the chain so it gets anchored.
Two trees, two questions. complete is sorted — it answers "is this in, or provably absent". consistency is ordered — it answers "did this log only ever grow". The roots deliberately do not match, and anyone comparing them is comparing the wrong things.

Replay and lineage mostly no key

replay — determinism as a black box

The scoring maths is never disclosed. No route returns source, weights, thresholds or intermediate values — only a one-way SHA-256 fingerprint of the deployed decision function. Proof works by public challenge instead: POST any inputs, the run is sealed, and resubmitting identical inputs later must give an identical verdict under an unchanged fingerprint.

curl -X POST https://sebbi.pro/x/replay/challenge \
  -H "Content-Type: application/json" \
  -d '{"inputs":{"action":"payment","amount":49.99,"trust":0.5,
       "v60":1,"v5m":1,"v1h":1,"device_risk":0.05,
       "anomaly":0.1,"country":"UK","country_shift":0}}'
POST /x/replay/challengeRun any inputs. Sealed. Resubmit later and compare.
GET /x/replay/history?input_hash=Every run of those exact inputs, with verdicts and fingerprints.
GET /x/replay/selfReproduction rate across a sample.
GET /x/replay/fingerprintThe code fingerprint of the deployed decision function.
POST /x/replay/check · /attestkey

lineage — provenance across organisations

Each decision can record the receipt hashes of its inputs and which chain each came from. The graph then composes, and every hop is verifiable through routes that already exist. External hops are named with a verification plan pointing at the other party's own host — never resolved or certified by us.

GET /x/lineage/trace?receipt=Upstream: what fed this decision.
GET /x/lineage/impact?receipt=Downstream: which decisions declared a dependency on a retracted or faulty input. Article 20 corrective action.
GET /x/lineage/receipt?receipt=A portable, self-contained proof document that travels with an output.
POST /x/lineage/declarekey Records an edge.
Stated plainly: an edge is a dated, non-repudiable claim about what fed a decision. It is not proof the claim is true. The chain fixes when it was said, not whether it was right.

Authority continuity — derive it, don't look it up Bearer key

Everything above proves what your system did. This proves it was entitled to. An agent acts; it got its authority from another agent, which got it from a system, which got it from a person. A permission check answers one hop. An audit log describes the aftermath. Neither derives anything, so neither can see authority widening three delegations back.

Every grant points at a parent and terminates at a named human. Scope, limits, purpose and validity must narrow at every hop, and the whole chain is re-derived at the instant of execution rather than trusted from the instant of issue.

Issue a root grant

A root must be issued by a human, must state a purpose, and must expire. Authority with no stated purpose cannot be checked for intent drift later, so it is refused.

curl -X POST https://sebbi.pro/x/continuity/issue \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"issuer":"you@company.com","issuer_kind":"human",
       "subject":"orchestrator",
       "scope":["payments.refund","payments.read"],
       "constraints":{"max_amount":5000,"allowed_currency":["GBP"]},
       "purpose":"resolve customer refund complaints",
       "purpose_tags":["refunds","support"],
       "not_after":1786400000,
       "delegations_left":2}'
{ "grant": "g_9ec009c0a02345169788", "depth": 0,
  "risk_accepted_by": "you@company.com",
  "digest": "…", "block_index": 842 }

Delegate it onward

Same endpoint with a parent. A child may narrow and never widen; raising max_amount above the parent's is refused with the axis named.

Risk acceptance. A grant that only narrows inherits the acceptor above it. A grant that can delegate onward must name its own risk_accepted_by and cannot inherit one — handing an agent the power to hand authority on again is a new risk that did not exist when the person above signed up to it. A lineage where nobody has accepted the risk refuses to act at all.

Exercise it

curl -X POST https://sebbi.pro/x/continuity/exercise \
  -H "Authorization: Bearer YOUR_KEY" \
  -d '{"grant":"g_9ec…","action":"payments.refund",
       "params":{"amount":150,"currency":"GBP"},
       "purpose_tag":"refunds"}'
{ "verdict": "ALLOW",
  "authority_verdict": "ALLOW", "risk_verdict": "ALLOW",
  "authorised_by": "you@company.com",
  "executed_by": "refund-agent",
  "risk_accepted_by": "you@company.com",
  "delegation_depth": 1,
  "lineage": [ … every hop, root first … ],
  "lineage_digest": "…", "params_digest": "…",
  "block_index": 843 }

A refusal names where it broke rather than simply denying:

{ "verdict": "BLOCK",
  "broken_at": "g_mid…", "broken_invariant": "boundary_integrity",
  "reasons": ["amount=900 exceeds max_amount=200"] }
VerdictMeaning
ALLOWEvery invariant held and the risk engine agreed. Derivable from a valid human grant.
CHALLENGENothing provably broken, nothing provably fine — a purpose the grant does not carry, a wildcard too broad to review, or a parameter no ancestor constrains. Escalated rather than guessed.
BLOCKAn invariant failed. The response names the grant and the invariant.
Composition. The verdict is the worse of the authority verdict and the risk engine's. Either can stop an action; neither waves one through alone. If the engine cannot be reached or returns something unreadable, the result degrades to CHALLENGE — a missing risk opinion is missing, not favourable.

Bind the execution to the decision

An ALLOW is a decision about a request that may not be the request that ran. confirm re-derives the parameter digest from what actually executed, enforces the validity window, and can be spent exactly once — enforced by a unique index rather than a read followed by a write, so concurrent attempts cannot both win.

curl -X POST https://sebbi.pro/x/continuity/confirm \
  -H "Authorization: Bearer YOUR_KEY" \
  -d '{"evaluation":"e_05da…","action":"payments.refund",
       "params":{"amount":150,"currency":"GBP"},"outcome":"executed"}'

Revoke

POST /x/continuity/revoke — body {"grant":"g_…","reason":"…"}. Transitive by derivation: everything beneath stops evaluating immediately, with no descendant needing to be found. Actions already evaluated stay exactly as they were decided — revocation does not rewrite history.

EndpointNotes
GET /x/continuity/specno key The derivation rules in full, sufficient to reimplement the evaluator.
GET /x/continuity/decisions?limit=no key Real sealed evaluations. Blocks listed beside allows.
GET /x/continuity/decision?evaluation=no key One sealed decision in full.
GET /x/continuity/trace?grant=no key The whole authority path, root first, with effective constraints across it.

witnessed — the gap authority cannot close on its own

A well-formed grant that was never issued passes every internal check, because issuer, scope and approver all arrive on the request. So a grant sealed at tree size M, plus an independent peer that accepted a head at size N ≥ M at time T, proves the grant existed before T in a log we cannot write to. Back-dated grants die without a new protocol.

GET /x/witnessed/grant?grant=Whether a grant is externally witnessed, and how many minutes it sat unwitnessed.
GET /x/witnessed/heads · /statusAccepted peer heads, coverage strength, and the collusion limit stated openly.
POST /x/witnessed/submitkey

The exported proof — and a verifier that doesn't need us no key

A proof you can only check with the prover's own online tool is a reassurance. So any authority decision exports as a self-contained signed bundle, and the checker runs on your machine with the network off.

curl -sO https://sebbi.pro/verify-authority.py
curl -s "https://sebbi.pro/x/continuity/proof" | python3 verify-authority.py -

With no evaluation the proof route returns the most recent decision, so you can start knowing nothing.

{ "bundle_version": "1.0",
  "issued_by": { "algorithm": "Ed25519", "public_key": "…" },
  "decision": { "verdict": "ALLOW", "lineage_digest": "…",
                 "params_digest": "…", "broken_at": null },
  "request":  { "action": "payments.refund", "params": {…} },
  "lineage":  [ … every grant as it stood at that instant … ],
  "chain":    { "audit_hash": "…", "block_index": 843 },
  "rules":    { … how every digest and the signature are computed … },
  "signature": "…" }

The verifier does four separate things, each able to fail on its own: signature (Ed25519 over the canonical bundle), integrity (every digest recomputed from the fields in front of it), derivation (the authority path re-run from the published rules), and agreement (its verdict compared with ours — a disagreement is reported as our failure, not its).

No dependencies, no network, no telemetry. Python standard library only, including the Ed25519 implementation. It never contacts sebbi.pro and reports nothing back — a verification tool that phones home to the party being verified is not a verification tool. Check script_sha256 at /x/verifier/status against what you downloaded, and read it before you run it.

The refusal proof

When authority cannot be derived you do not get a bare BLOCK. The bundle carries the grant and the invariant that failed, and the verifier independently reproduces that failure at the same hop. An agent that can prove it was not authorised is a different object to one that was merely denied — and it is what a counterparty needs when an action does not happen.

RESULT: VERIFIED - BLOCK
This is a proof that the action was NOT authorised, and where it failed.
Checked with no network access, no dependencies, and nothing taken on
the issuer's word except the meaning of their public key.

The offline verifier for the rest of it

aileash_verify.py is the auditor's tool: one file, no dependencies, never touches the network. It checks completeness inclusion, absence, RFC 6962 ancestry and prefix proofs, and replay stability. Run --selftest and it builds trees internally and confirms that tampered proofs and a forged prefix are rejected.

GET /verify-authority.pyThe authority verifier, as a plain file.
GET /x/verifier/statusIts size and SHA-256, to check what you downloaded.
GET /x/continuity/pubkeyThe Ed25519 public key, RFC 8032, verifiable with any standard library.
Honest limits. This proves authority was derivable from a human grant — not that the human should have granted it, and not that the parameters describe something that really happened. Grants are authenticated by sealing rather than per-issuer signatures, so an outside party verifies them through the chain rather than entirely offline. The risk half of a composed verdict cannot be re-derived without the scoring engine. And decisions made before signed proofs shipped carry no lineage snapshot — reconstructing one now would describe today's authority rather than the authority the action was judged against, so the route refuses instead.

The witness network — the part we cannot control no key, ever

A chain the operator can rewrite forward is a claim. The answer is not a better promise, it is a copy held by somebody else. Chains exchange tips on an hourly cycle; once a peer holds our tip it sits in their record, not ours.

Joining is free and ungated, permanently. The protocol code never checks subscription status. There is no membership list, no seat to grant and none to revoke — the roster is a record of who submitted and when, with first-seen dates anyone can verify.

GET /x/witness/tipOur current head. A peer cannot seal what it cannot read, so this is public on purpose.
POST /x/witness/observeSubmit your tip. No account — a witnessing endpoint that needs an account is a customer list, not a witness network. Never rejects a well-formed submission.
GET /x/witness/attest · /specWhat was sealed, and the protocol in full.
GET /x/roster/listEvery chain that has submitted, with first-seen dates and elapsed-time status: current, stale, silent.
GET /x/mutual/peers · /statusWho we fetch from, and how the outbound cycle is running.
GET /x/praxis/statusThe signed lane — customer-held Ed25519 keys rather than a shared secret.
GET /x/signed/specThe signed submission contract.
Two non-rejecting checks are sealed alongside every tip. Liveness (confirmed / live / self-declared, via an SSRF-guarded fetch of a submitted URL) and name binding (first-use / bound / conflict / unbound, network-wide). Neither refuses a submission. Both are recorded so a reader can weigh it. And current, stale and silent measure elapsed time only — nothing else.

The Ordering Test no key

Sequence is the only property that cannot be retrofitted. Content can be fabricated, timestamps argued over, a log rebuilt — but a commitment made before the information existed cannot be reverse-engineered afterwards. Ten checks, published as a discovery document any vendor can serve from their own domain.

rule_binding          commit_before_reveal   completeness_proof
absence_proof         consistency_proof      reproducibility
mutual_witnessing     external_anchoring     authority_tokens
reconciliation

Each check declares supported and, separately, demonstrable_publicly — because "we built it" and "you can check it without an account" are different claims, and separating them is what stops an operator marking their own homework.

GET /.well-known/ordering-test.jsonThe discovery document. base_url is derived from the Host header, so the file publishes whichever domain serves it.
GET /x/standard/status · /hashModule state and the document digest.
GET /self-checkThe runner as a page: reads the published document and runs every check in declared order. Four outcomes, and "reachable" never counts as a pass.
GET /x/register/specThe Safe AI Registry — self-service listing, absence proofs, RFC 6962 consistency, and revocations that seal rather than delete.

Packs — three different things, one chain

The word does three jobs, so here they are apart.

KindRouteWhat it is
Cost packs/x/packs/An open library of decision rules over nine live cost signals. Free to read, write, fork and publish. Running one needs a key.
Risk packs/api/signal-pack/Weighted risk signals — bias, adverse impact, explainability gap. Keyed, private by default.
Evidence packs/x/pack/The quarterly auditor document: every block re-verified, links rewalked, receipt sequence checked, with an unbroken-since date.

Cost packs — the library

Nine signals: exposure, size, ask, depth, tools, loop, burst, grind, novelty, plus raw counts and a composite score. Rules are written in a small expression language with no function calls, attribute access or strings in the grammar — nothing reaches eval. Every rule requires a stated reason or publishing fails.

A pack can return allow, downgrade, challenge or block. It can never return serve, and it can only make the engine's verdict stricter, never weaker — capping is reported rather than silently applied.

GET /x/packs/list · /get · /spec · /statusno key Browse and read every published pack, including its rules and reasons.
POST /x/packs/validate · /publish · /forkno key Publishing seals the pack's fingerprint with the date. Forking records the parent, so lineage is visible rather than argued about.
POST /x/packs/runkey Executing a pack against real traffic is the metered part.

Evidence packs

GET /x/pack/specno key What the pack contains and how each check is performed.
POST /x/pack/preview · /render · /issuekey issue seals the pack's own digest, so the document cannot be edited after the fact.

On-premise — Sebdog runs without us

One Python file, standard library only, local SQLite with WAL, its own audit chain, daily sealed backups. Every decision, the chain and the database stay on your hardware.

No phone home. Licensing is an Ed25519 token you hold, validated locally against a published public key — we sign on our server and ship only the public half, so nothing that can mint a licence ever reaches a customer machine. The engine makes no outbound call to sebbi.pro at all, which means it keeps running whether we are up, down or gone.
GET /health · /stats · /snapshotsLocal operational state.
POST /governThe decision, locally. Requires a matching bearer.
GET /verify-chainRewalks and rehashes every block on your own machine.
GET /tipIts head, public on purpose — a peer cannot seal what it cannot read.
POST /witness/observeOpen on purpose. Your on-premise chain can be witnessed by chains you choose.
GET /peersWho has been seen witnessing this engine.
python sebdog_engine.py --token-file licence.token --port 9090 --chain your-chain-name

That last pair of routes is the point of it: the only configuration where the data never leaves your building and the record is still externally witnessed. Pair it with meshwitness.py to run the exchange.

Client libraries and the metering gate

FileWhat it does
aileash.pyDrop-in client, stdlib only. Decorator, context manager or direct check. Fails closed by default — gate unreachable means the agent stops — with an explicit on_error="allow" opt-out. Raises BudgetExhausted and HumanReviewRequired separately, because topping up does not clear a review halt.
sebbi_sdk.pyZero-dependency SDK, @witness() decorator. Canonical-JSON SHA-256 fingerprints of inputs and outputs, hash-only egress, background daemon thread, batching, disk spool on outage and replay. Never blocks the caller, never swallows the caller's exception. Adds about 0.1 ms per call.
leashproxy.pyThe chokepoint. Holds the model provider's key so the agent never sees it, charges before forwarding, and at zero balance refuses with 402 without the call ever reaching the model. Strips any provider credential an agent tries to forge.
aileash-capture.jsBrowser widget using capture tokens rather than API keys, with server-measured dwell.
sebbi_tokensaver.pyThe cost client. Change one line — base_url to http://127.0.0.1:8788. Fails open, caches locally, queues records on outage. Prompts and answers never leave your machine; --offline works with no account at all.

The wallet gate

Give an agent a budget and terminate it when the budget is gone. charge is the gate: it returns allowed true or false and seals every charge into the chain, so the spend record and the decision record are the same record. Balances are integer millipence — no floats.

Duplicate receipts halt the key. A receipt presented twice returns 423 and raises a review item — not even a subscribed device passes — until a person clears it. A machine does not get to decide whether a repeated hash was a replay or a collision.
GET /x/wallet/specno key
POST /x/wallet/charge · /quote · /simulatekey simulate is a dry run: how far does a runaway loop get on this balance, without spending anything.
POST /x/wallet/topup · /subscribe · /devices · /review · /clearkey

Free tools no key on any route

/x/identify/Connection-risk check. CLEAN / SUSPECT / HIGH with every signal named and sourced — Tor exit list, hosting and datacentre patterns, missing rDNS, caller-supplied timezone against a claimed country. No paid feeds. Every check sealed with a public verify link.
/x/watch/Password Watch. A phone reports failed unlock attempts and the SHA-256 of any photo it took — never the photo. Later, police hash the picture and check for a match. Takes no passwords, PINs or attempted values in any form.
/x/publish/Publication sealing. Fetches a URL server-side, hashes the exact bytes served with no normalisation, and seals url + hash + fetch time. Re-sealing builds an uneditable revision history.
/x/codebase/Dated authorship evidence. Hashes every file into one ordered manifest root and seals it with your authorship declaration. File contents never leave; only root, count and bytes are public.

Errors and router behaviour

StatusBodyMeaning
400valid sha-256 fingerprint requiredNotary needs a 64-char hex fingerprint. Govern needs all seven fields.
401api_key_required / invalid_api_keySend Authorization: Bearer YOUR_KEY. Public routes need none.
403account_inactiveAccount disabled — get in touch.
404unknown_actionRead this one carefully. A method mismatch returns 404 with the accepted GET and POST lists in the body, not 405. A client that treats 404 as "endpoint missing" will report false failures on POST-only routes.
409fork evidence / period already committedConsistency found a fork, or a closed period was committed twice.
423review requiredA duplicate receipt halted the key. A person clears it.
429quota_exceeded / rate_limit_minute / rate_limit_hourFree tier spent, or per-key limits: 60/min, 1000/hr.
500internalLogged server-side; detail is never leaked to callers.

Payloads are capped at 200KB. Public routes carry per-IP limits; keyed routes are metered per key.

Kick the tyres without writing code: run a live decision at the Proving Ground, browse the library at sebbi.pro/packs.html, seal a post at sebbi.pro/seal, verify it at sebbi.pro/verify, or run the whole conformance suite yourself at sebbi.pro/self-check. Same maths, same catch.