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
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
}
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 /api/post/seal — body {"fingerprint":"<sha256>"}.
GET /api/verify-post?content=<sha256> — returns {"verified":true,"block_index":…,"sealed_at":…,"seal":…}, else {"verified":false}.
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.
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.
# 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.
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
}'
| Field | Type | Meaning |
|---|---|---|
| user_id | string | Your stable identifier for the acting user. Trust is learned per user_id. |
| action | string | What they're doing — payment, login, message, anything. |
| amount | number | Monetary value if relevant, else 0. Log-scaled internally. |
| country | string | ISO-style code. Country changes and off-allowlist jurisdictions raise score. |
| device_id | string | Device identifier for velocity correlation. |
| anomaly | 0–1 | Your behavioural-anomaly signal, if you have one. 0 if not. |
| device_risk | 0–1 | Your 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.
{
"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.
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/govern | A real decision through the live engine, sealed into the production chain. |
| POST /x/demo/review | Opens a review case with the verdict withheld and a server-measured clock running. |
| POST /x/demo/commit | Commits your call before the machine verdict is revealed. |
| GET /x/demo/stats | Aggregates across everyone who has tried it, including how many committed in under two seconds. |
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.
| Endpoint | Returns |
|---|---|
| GET /api/verify-chain | no 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/coverage | key Reconciliation in one call: receipts issued vs blocks sealed, complete: true/false. |
| GET /api/pulse | key Your last hour — verdict mix, recent decisions with reasons, chain tip. |
| GET /api/regulation-map | no key Versioned, hash-sealed map of engine features to legal obligations. |
| GET /api/spec | no key This API describing itself, machine-readable. |
| GET /x/stats | no key Public aggregates — chain growth, verdict mix, dwell distribution. Nothing scoped to a customer key. |
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/status | no key Proof counts, pending vs confirmed, first attempt date. |
| GET /x/ots/list | no key Every proof on file. |
| GET /x/ots/proof | no key The raw .ots bytes, base64. Verify with the standard opentimestamps client — nothing of ours required. |
| POST /x/ots/upgrade | key Re-fetches proofs from the calendars to confirm them. Also runs hourly on its own. |
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.
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/periods | Which periods are committed, and how many days after each closed. |
| GET /x/complete/root | The 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 · /verify | The rules, and a checker. |
| POST /x/complete/commit · /erase | key Only closed periods commit, and each commits once. |
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/root | Current 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/checkpoint | key Seals the current size and root into the chain so it gets anchored. |
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.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/challenge | Run 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/self | Reproduction rate across a sample. |
| GET /x/replay/fingerprint | The code fingerprint of the deployed decision function. |
| POST /x/replay/check · /attest | key |
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/declare | key Records an edge. |
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.
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 }
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_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.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"] }
| Verdict | Meaning |
|---|---|
| ALLOW | Every invariant held and the risk engine agreed. Derivable from a valid human grant. |
| CHALLENGE | Nothing 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. |
| BLOCK | An invariant failed. The response names the grant and the invariant. |
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"}'
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.
| Endpoint | Notes |
|---|---|
| GET /x/continuity/spec | no 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. |
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 · /status | Accepted peer heads, coverage strength, and the collusion limit stated openly. |
| POST /x/witnessed/submit | 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).
script_sha256 at /x/verifier/status against what you downloaded, and read it before you run it.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.
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.py | The authority verifier, as a plain file. |
| GET /x/verifier/status | Its size and SHA-256, to check what you downloaded. |
| GET /x/continuity/pubkey | The Ed25519 public key, RFC 8032, verifiable with any standard library. |
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/tip | Our current head. A peer cannot seal what it cannot read, so this is public on purpose. |
| POST /x/witness/observe | Submit 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 · /spec | What was sealed, and the protocol in full. |
| GET /x/roster/list | Every chain that has submitted, with first-seen dates and elapsed-time status: current, stale, silent. |
| GET /x/mutual/peers · /status | Who we fetch from, and how the outbound cycle is running. |
| GET /x/praxis/status | The signed lane — customer-held Ed25519 keys rather than a shared secret. |
| GET /x/signed/spec | The signed submission contract. |
current, stale and silent measure elapsed time only — nothing else.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.json | The discovery document. base_url is derived from the Host header, so the file publishes whichever domain serves it. |
| GET /x/standard/status · /hash | Module state and the document digest. |
| GET /self-check | The 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/spec | The Safe AI Registry — self-service listing, absence proofs, RFC 6962 consistency, and revocations that seal rather than delete. |
The word does three jobs, so here they are apart.
| Kind | Route | What 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. |
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 · /status | no key Browse and read every published pack, including its rules and reasons. |
| POST /x/packs/validate · /publish · /fork | no 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/run | key Executing a pack against real traffic is the metered part. |
| GET /x/pack/spec | no key What the pack contains and how each check is performed. |
| POST /x/pack/preview · /render · /issue | key issue seals the pack's own digest, so the document cannot be edited after the fact. |
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.
| GET /health · /stats · /snapshots | Local operational state. |
| POST /govern | The decision, locally. Requires a matching bearer. |
| GET /verify-chain | Rewalks and rehashes every block on your own machine. |
| GET /tip | Its head, public on purpose — a peer cannot seal what it cannot read. |
| POST /witness/observe | Open on purpose. Your on-premise chain can be witnessed by chains you choose. |
| GET /peers | Who 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.
| File | What it does |
|---|---|
| aileash.py | Drop-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.py | Zero-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.py | The 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.js | Browser widget using capture tokens rather than API keys, with server-measured dwell. |
| sebbi_tokensaver.py | The 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. |
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.
| GET /x/wallet/spec | no key |
| POST /x/wallet/charge · /quote · /simulate | key 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 · /clear | key |
| /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. |
| Status | Body | Meaning |
|---|---|---|
| 400 | valid sha-256 fingerprint required | Notary needs a 64-char hex fingerprint. Govern needs all seven fields. |
| 401 | api_key_required / invalid_api_key | Send Authorization: Bearer YOUR_KEY. Public routes need none. |
| 403 | account_inactive | Account disabled — get in touch. |
| 404 | unknown_action | Read 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. |
| 409 | fork evidence / period already committed | Consistency found a fork, or a closed period was committed twice. |
| 423 | review required | A duplicate receipt halted the key. A person clears it. |
| 429 | quota_exceeded / rate_limit_minute / rate_limit_hour | Free tier spent, or per-key limits: 60/min, 1000/hr. |
| 500 | internal | Logged 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.