Two families of endpoints, one tamper-evident SHA-256 chain underneath. Govern scores and seals AI decisions (needs a key). Notary seals a fingerprint of anything — a post, an identity, payment details — with no key and no content ever leaving your system. Base URL: https://sebbi.pro
No account needed for the Notary. 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. The seal is permanent and tamper-evident; re-sealing the same fingerprint returns the original receipt with already_registered: true.
POST /api/post/seal — body {"fingerprint":"<sha256>"}. Seals the fingerprint of a post, article, or document.
GET /api/verify-post?content=<sha256> — returns {"verified":true,"block_index":…,"sealed_at":…,"seal":…} if that exact fingerprint is sealed, else {"verified":false}.
POST /api/identity/seal — body {"fingerprint":"<sha256>", "public":true, "profile":{…}}. If public is set, a limited set of display fields (name, title, bio, linkedin, facebook, org) is stored so a checker can show them; otherwise only the fingerprint is sealed. Returns {sealed, seal, block_index, sealed_at, code}.
GET /api/identity/check?code=<12+ chars> — accepts the short code or full fingerprint. Returns {found, fingerprint, registered_at, seal, block_index}, plus profile if it was sealed public.
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. Returns {sealed, seal, block_index, sealed_at, code}.
GET /api/payment/check?code=<12+ chars>&fp=<optional full sha256> — looks up the seal. If you also pass fp, it returns match: true/false — MATCH, MISMATCH, or NO_SEAL. Either way the verification itself is sealed and returned as a receipt, giving provable evidence of the check under PSR reimbursement rules.
Integration is a few lines wherever your system creates the thing worth proving. The shape is always the same:
# 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() # v["verified"] == True
Wire that into the moment a post is published, an invoice is issued, or a profile is created, and every record from then on carries provable, tamper-evident proof — automatically. That is the whole integration.
The decision engine: score an event, get a verdict in ~28ms, sealed before the response returns. All seven fields required. 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 moves per decision: earned slowly on ALLOW, lost 8× faster on BLOCK — burst attacks self-amplify.
Everything else here 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,
"note": "Sealed at issue. Any later edit to the stored grant changes
its digest and fails integrity." }
Same endpoint, with a parent. A child may narrow and never widen. Try raising max_amount above the parent's and it is refused with the axis named.
curl -X POST https://sebbi.pro/x/continuity/issue \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"parent":"g_9ec…","issuer":"orchestrator","issuer_kind":"agent",
"subject":"refund-agent","scope":["payments.refund"],
"constraints":{"max_amount":200,"allowed_currency":["GBP"]},
"purpose":"issue small refunds","purpose_tags":["refunds"],
"not_after":1786390000,"delegations_left":0}'
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.The whole path is walked and re-checked now, against this action, with these parameters.
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. Triggered by 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 and compares it, 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 remain exactly as they were decided — revocation does not rewrite history.
| Endpoint | Body / query | Notes |
|---|---|---|
| POST /x/continuity/issue | issuer, issuer_kind, subject, scope, constraints, purpose, purpose_tags, not_after, delegations_left, parent, risk_accepted_by | Root needs a human issuer. No expiry and no purpose are both refused. |
| POST /x/continuity/exercise | grant, action, params, purpose_tag | Re-derives the whole lineage now. Sealed whichever way it goes. |
| POST /x/continuity/confirm | evaluation, action, params, outcome | One evaluation binds to one execution. A rejected binding is sealed too. |
| POST /x/continuity/revoke | grant, reason | Transitive. Sealed. |
| 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. Each carries its own links. |
| 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 the effective constraints across it. |
A proof you can only check with the prover's own online tool is a reassurance, not a proof. 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. Pass one from /x/continuity/decisions for a specific decision.
{ "bundle_version": "1.0",
"issued_by": { "algorithm": "Ed25519", "public_key": "…" },
"decision": { "verdict": "ALLOW", "authority_verdict": "ALLOW",
"risk_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 whole authority path re-run from the published rules), and agreement (its own 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.
| Endpoint | Returns |
|---|---|
| GET /x/continuity/proof | The signed bundle. No evaluation returns the most recent; ?evaluation= returns a specific one. |
| GET /x/continuity/pubkey | The Ed25519 public key, RFC 8032, verifiable with any standard library. |
| GET /verify-authority.py | The verifier itself, as a plain file. |
| GET /x/verifier/status | The script's size and SHA-256, to check what you downloaded. |
CONTINUITY_SIGNING_SEED to 64 hex characters. Without it a key is generated into the database on first use and a warning is printed — workable, but a signing key living in the same database as the records it signs is the first thing a reviewer will ask about. Changing it rotates the public key, so every proof already handed out verifies against the old one.Every govern response includes receipt_seq: a per-key sequence issued in the same transaction as the chain write. Sequences are gapless by construction. Store them: if you ever hold receipts 46 and 48 with no 47, a record has been omitted — provable by arithmetic. Edited records break the chain. Missing records break the sequence.
When the verdict is CHALLENGE, the response includes a hosted resolution flow — you don't build exception UX:
{
"decision": "CHALLENGE",
"challenge_url": "https://sebbi.pro/verify-challenge?token=…",
"challenge_status_url": "https://sebbi.pro/api/challenge/status?token=…",
"challenge_expires_in": 900
}
Show challenge_url to your user (link, redirect, or iframe). They confirm or deny on our hosted page; the resolution is sealed into the chain as its own block; you poll challenge_status_url until resolved: true. Tokens are stateless and HMAC-signed.
| Endpoint | Returns |
|---|---|
| GET /api/verify-chain | Whole-chain integrity: {"valid":true,"blocks":N,"tip":…}. Anyone can run it — auditors, regulators, your customers. |
| GET /api/inclusion?hash= | Whether a full 64-char receipt hash is sealed, with its block index and sequence. |
| GET /api/regulation-map | Versioned, hash-sealed mapping of engine features → legal obligations (EU AI Act Arts. 9/12/13/14, OSA, Children's Code). |
| GET /api/partner/status?badge= | Partner account standing and platform status as separate fields — independently attributable. |
| GET /api/spec | This API, describing itself — machine-readable. |
GETBearer key /api/pulse — your last hour at a glance:
{ "last_hour": { "ALLOW": 412, "CHALLENGE": 9, "BLOCK": 3 },
"recent": [ { "ts":…, "action":"transfer", "decision":"BLOCK",
"score":0.87, "reasons":["velocity_spike","low_trust"],
"sealed":"8db26b04…" }, … ],
"chain_tip": "bf9257ab…" }
GETBearer key /api/coverage — reconciliation in one call: receipts issued vs blocks sealed, complete: true/false.
Real-time alerts: the moment the engine BLOCKs on your traffic, the account email receives the sealed evidence — user, action, score, reasons, audit hash. Throttled to one per hour so a burst can't flood your inbox.
The engine runs entirely inside your network — decisions, chain and database on your hardware; nothing leaves your building. Licensing is offline: HMAC-signed 365-day tokens validated with pure cryptography, no phone-home. Ask via /contact. Paid plans can mint air-gap tokens at POST /api/generate-airgap-token.
| 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 | Govern only — send Authorization: Bearer YOUR_KEY. Notary needs no key. |
| 403 | account_inactive | Account disabled — contact us. |
| 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. |