{
  "markdown": "# seal\n\n> **Public register:** the [Retry-Safety Index](https://aurumflux.co/retry-safety/) lists which agent-payment implementations pay once when the answer is lost — verified safe, found & fixed (with time-to-fix), and how to get verified. Every row links to its proof.\n\n**Your agents earn the right to spend without you.**\n\n### Seal is not Coherence\n\nSeal and [EffectFence](https://github.com/aurumflux20/effectfence) stop an irreversible action from firing twice **while it happens** — runtime enforcement on money movement. [Coherence](https://github.com/aurumflux20/coherence) never touches your runtime; it reads the record **afterwards** and grades what an agent *claimed* against what it *proved*. **Prevention versus proof.** Different problems, different code, no overlap.\n\n> **Free:** submit any client, facilitator, SDK or toolkit that moves money — yours or someone else's — and we read it and publish a verdict on the [Retry-Safety Index](https://aurumflux.co/retry-safety/) at no cost. Findings come back with the mechanism, the file and line, and a failing test. You are counted, never named, until you ship a fix. **[Submit for grading →](https://github.com/aurumflux20/hostile-facilitator/issues/new?template=submit.yml)**\n\n\n**Seal is an MCP server** (`seal-mcp`, stdio, JSON-RPC 2.0) — and a Python\nlibrary. It gives an MCP host **12 tools** for exactly-once execution of\nirreversible actions: `seal_propose`, `seal_execute`, `seal_paths` (gateway\nmode — the agent holds a single-use ticket, never the provider key), plus\n`seal_admit`, `seal_commit`, `seal_abort`, `seal_heartbeat`, `seal_get`,\n`seal_verify`, `seal_incident_receipt`, `seal_expect`, `seal_obligations`.\n\n```bash\ndocker run -i ghcr.io/aurumflux20/seal          # or: python -m seal.mcp_server\n```\n\nIt starts in **introspection-only mode with no environment** — `initialize` and\n`tools/list` answer with no database, so a host or registry probe can connect\nimmediately. Set `SEAL_DSN` to a Postgres DSN to actually admit actions, and\n`SEAL_EXECUTORS=your.module` for gateway mode.\n\n```jsonc\n// claude_desktop_config.json\n{ \"mcpServers\": { \"seal\": { \"command\": \"python\", \"args\": [\"-m\", \"seal.mcp_server\"],\n                            \"env\": { \"SEAL_DSN\": \"postgres://...\" } } } }\n```\n\n> Not an engineer? Read [docs/PLAIN-ENGLISH.md](docs/PLAIN-ENGLISH.md) instead —\n> the same thing with no jargon, including what we can't do.\n\nEveryone else ships a *lock*: a spend cap you set once and forget. The cap never\nlearns, so an agent that has settled ten thousand clean payments is trusted\nexactly as little as the one you installed this morning — and you keep clicking\nApprove.\n\nSeal ships the *unlock*. It reads what a payment path has actually proven —\nsettlements the provider confirmed, sweeps showing nothing moved behind its back —\nand computes the autonomy that path has earned. **L0 OBSERVED → L5 AUTONOMOUS.\nNobody types the level.**\n\n```\n████████············  L2 ASSISTED      50 proven · 100% confirmed   [human required]\n     fifty settlements — but volume alone is not trust.\n████████████········  L3 DELEGATED     50 proven · 100% confirmed   [unattended]\n     one clean sweep later: the human stops clicking Approve.\n····················  L0 OBSERVED      50 proven · 100% confirmed   [SUSPENDED]\n     one charge the gateway never admitted. fifty clean ones don't outweigh it.\n```\n\n```bash\nSEAL_DSN=\"...\" python3 license_demo.py     # watch a path earn L3 and lose it\n```\n\n**Since 0.4.0 the licence drives the wheel, not just the dashboard.** Turn on\nearned autonomy — `Gateway(seal, earned_autonomy=True)`, or\n`SEAL_EARNED_AUTONOMY=1` for the MCP server — and the gateway lets a path move\nmoney unattended only to the extent its own record has earned (L3+), inside\nthe operator's ceilings, never above them. Three things hand the wheel back to\na human instantly: a path that hasn't earned it yet, a suspension (money moved\nbehind the gateway's back), and a **hold** — an execution reached the provider\nand its outcome is unknown, so the path pulls over until `settle()` has asked\nthe provider what happened. The hold lifts by itself once the world answers.\nA human can still approve any single action through the same maker-checker\ndoor (`tier=LICENCE`). Off by default: nothing changes until you switch it on.\n\n> **Can you prove your agents won't double-charge a customer?** Three rungs, one\n> ladder, written-only: a **$300 founding conformance run** — your implementation\n> through the battery, result published on the Index (first three only;\n> [book](https://buy.stripe.com/14A6oHb9K1UI69A21ldIA0o)) · a **$1,200 attestation\n> run** — your live endpoint against every ambiguous outcome, signed result, findings\n> within five business days, a clean run signed within 24 h\n> ([book](https://buy.stripe.com/28E7sL91C9naapQbBVdIA0l)) · a **$12,000 fixed-scope\n> [money-path review](SUPPORT.md)** — one production money path read, tested and\n> attested in 7–10 days, **no invoice if no real double-fire is shown on a path you\n> run**. For a free self-check first,\n> [hostile-facilitator](https://github.com/aurumflux20/hostile-facilitator) tells\n> you in 60 seconds.\n\nSlow to earn, instant to lose — the only shape that makes a track record mean\nanything. The full level definitions: [docs/AUTONOMY-LEVELS.md](docs/AUTONOMY-LEVELS.md).\n\nSeal's ambiguous-outcome doctrine — *\"could not determine\" is terminal, never\nabsent* — is now §4.3 of the draft [MCP retry-safety proposal](https://github.com/YoadElkayam/mcp-fuse/tree/main/sep),\nco-authored by us, with our [conformance battery](https://github.com/aurumflux20/hostile-facilitator)\nas its test suite.\n\n## Underneath: exactly-once admission\n\nTwo different agents, on two different machines, both decide to charge order 123\nat the same instant. In-process idempotency can't help — the guard has to live in\na store both agents talk to, and the winner has to be decided *atomically there*.\n\nSeal is that layer. One Postgres, one row per intent, one winner:\n\n```\nINSERT ... ON CONFLICT DO NOTHING     -- one row, one winner, no check-then-act window\n```\n\nEvery admitted action ends in a **certificate**: a content-addressed hash over\nintent + args digest + result digest + the previous cert's hash. Editing,\ndeleting or reordering any cert breaks every hash after it — and anyone with the\nDSN can check, with no network and no trust in us:\n\n```bash\nSEAL_DSN=\"...\" python3 -m seal verify\n# chain VERIFIED — 41 cert(s), every link intact   (exit 0; broken chain → exit 1)\n```\n\n## The proof\n\nThe claim is tested the hostile way: **1,000 real threads released by one\nbarrier against one shared Postgres**, where the \"charge\" increments a measured\ncounter — if two callers run, the counter says 2 and the test fails loudly.\n\nResult, four consecutive runs: **ACTUAL_EXECUTIONS = 1.** Every loser either\nreplayed the sealed cert, stood down mid-flight, or failed safe when the store\nwas unreachable. A 50-caller post-seal wave: all replayed, none re-ran. Full\nnumbers, including the honest limits: [STORM-PROOF.md](STORM-PROOF.md).\n\nRun it yourself:\n\n```bash\npip install seal-kernel\n\nexport SEAL_DSN=\"host=... dbname=seal\"\npython3 -m seal verify          # chain check, no network, no trust in us\n```\n\nTo run the 1,000-thread storm proof yourself, clone the repo (the harness\nships with the source, not the wheel):\n\n```bash\n# Needs Python 3.10+. macOS ships 3.9 with pip 21, which fails an editable\n# install with a misleading \"setup.py not found\" error — use a venv rather\n# than debugging that.\ngit clone https://github.com/aurumflux20/seal && cd seal\npython3 -m venv .venv && source .venv/bin/activate\npython3 -m pip install -U pip && python3 -m pip install -e .\n\nexport SEAL_DSN=\"host=... dbname=seal\"\npython3 storm.py --n 1000\n```\n\n## Test YOUR server, not just ours\n\nThe exact harness above, generalized into a standalone file with zero\ndependency on this repo — copy it, point it at your own write-bearing tool,\nand find out for yourself:\n\n```bash\npython3 range_safety_test.py --n 1000\n```\n\nIt demonstrates itself against a known-unsafe target and a known-safe one\nbefore you ever run it for real, so a pass means something. Full writeup,\nincluding the three ways an early version of this test lied to us before it\nwas fixed: [docs/RANGE-SAFETY-TEST.md](docs/RANGE-SAFETY-TEST.md).\n\n## Usage\n\n```python\nfrom seal import Seal\n\nseal = Seal(dsn); seal.setup()\n\nadm = seal.admit(\"charge\", {\"order_id\": \"123\", \"amount\": 4900})\nif adm.fresh:                     # you won — run the effect, then seal it\n    result = stripe_charge(...)\n    cert = seal.seal(adm.intent, adm.fence, result)\nelif adm.cert is not None:        # already done — here is the receipt\n    return adm.cert\nelse:                             # someone else is mid-flight — stand down\n    raise InFlight()\n```\n\nIf the effect fails **before anything irreversible happened**, release the claim\nso a retry is legitimate: `seal.fail(adm.intent, adm.fence, reason)`.\n\n## World confirmation — measured against live Stripe, not mocked\n\nA cert saying \"admitted once\" is a claim about us. The next question is what\nStripe (or Resend, or your bank's webhook) actually recorded — and the answer\nis allowed to disagree with us.\n\n```bash\nexport SEAL_DSN=\"host=... dbname=...\"\nexport STRIPE_TEST_KEY=\"sk_test_...\"   # your own test-mode key, Dashboard -> API keys\npython3 stripe_demo.py\n```\n\nWhat it does, against your real Stripe test account, no mocks:\n\n1. **Two agents fire the same charge at the same instant.** Seal admits one.\n   Exactly one real `PaymentIntent` is created.\n2. **The witness asks Stripe:** *\"how many charges carry this intent?\"* Stripe\n   says one → the cert upgrades to `WORLD_FINAL`.\n3. **A rogue charge is created outside the gateway** — the thing no local fence\n   can stop on its own. The witness asks again; Stripe now says two → the cert\n   becomes `WORLD_DIVERGED`, the domain freezes, and further spend on it is\n   refused automatically.\n\nTwo honest things the live run taught us, both fixed and both tested: Stripe's\nsearch index is eventually consistent (a fresh charge can take real seconds to\nappear — the witness polls to a definitive answer rather than ever recording a\n\"not indexed yet\" empty read as authoritative absence), and once the world has\ncontradicted the ledger, a later flaky re-count must never quietly downgrade\nthe cert back to `WORLD_FINAL` — divergence is sticky by design.\n\n## Pre-commit world freeze — don't act on facts that already moved\n\n`admit()` has always taken a `read_set` — the world facts a decision depends\non (a cart total, an inventory count) — and stored it on the cert. Until now\nnothing ever checked it: a caller who believed they had staleness protection\nhad none. Same defect shape as a bug fixed earlier the same day, one layer up\n— a guard present in the schema, never enforced.\n\n```python\nfrom seal.freshness import CallableChecker\n\nfresh = CallableChecker(lambda rs: current_cart_total(rs[\"order_id\"]) == rs[\"total\"])\n\nadm = seal.admit(\"charge\", {\"amount\": 5000}, key=\"order-777\",\n                 read_set={\"order_id\": \"777\", \"total\": 5000}, checker=fresh)\n# StaleWorldRead is raised BEFORE a fence is granted if the checker says no —\n# nothing runs on facts that already changed. Gateway.propose() takes the\n# same read_set/checker kwargs and passes them straight through.\n```\n\nEnforcement point is deliberate: before the fence, not after the effect ran.\nChecking afterward could only refuse to *claim* success — it can't stop money\nmoving on stale information, which is the actual failure this exists to\nprevent. Opt-in and backward-compatible, same rule as everywhere else in this\nlibrary: only engages when the caller supplies both `read_set` and `checker`.\nHonest limit, printed where it applies rather than left to be discovered: the\nchecker call itself can't be made atomic with the admission INSERT, so a\nchange landing in that narrow gap is a residual window — the same caveat\nclass as a witness's eventually-consistent provider index.\n\n## Clearance — permission that has to be earned, not declared\n\nThe fence proves an action ran once. Clearance is the layer above it that a\ncompany actually buys: which tool paths may an agent fire *unattended*, and on\nwhat evidence.\n\n```python\nfrom seal.clearance import Clearance, CLEARED\n\ncl = Clearance(seal)\ncl.set_policy(\"charge\", CLEARED)                       # an operator's intent\ncl.record_proof(\"charge\", green=True, storm_n=1000, executions=1)  # from CI\n\ncl.status(\"charge\")[\"effective\"]   # CLEARED — but only because both are true\n```\n\nThe rule that makes this more than a toggle: **CLEARED is earned, not\ndeclared.** A path only reports effectively `CLEARED` if an operator set it\n*and* a green storm proof was recorded recently enough. Let the last proof go\nred, or let it go stale, and `status()` reports `HOLD` on its own — nobody has\nto remember to downgrade it. `REVOKED` always wins, never auto-recovers, and\n`revoke_all()` is one switch that stops every known path at the choke. A\n`range_report()` exports counted events and provider-cited certs — the artifact\na security questionnaire or a CFO actually reads.\n\n## Exclusive Authority — agents get tickets, never the credential\n\nClearance is policy. Policy an agent can walk around if it still holds\n`sk_live` itself isn't a rail, it's a suggestion. Exclusive Authority removes\nthe credential from the agent entirely.\n\n```python\nfrom seal.authority import Gateway\n\ngw = Gateway(seal)\ngw.register_executor(\"charge\", lambda args: stripe_charge(args))  # secret lives HERE only\n\nprop = gw.propose(\"charge\", {\"amount\": 4900}, key=\"order-777\")\nif prop[\"status\"] == \"cleared\":\n    result = gw.execute(prop[\"ticket\"], {\"amount\": 4900})  # gateway calls Stripe, not the agent\n```\n\nAn agent calls `propose()` and gets back a **ticket** — proof an intent was\nadmitted, cleared, and budgeted — never a secret. `execute()` is the only place\nthe provider is ever called, and the ticket is bound to the exact args that\nwere cleared: it's rejected if what you hand `execute()` doesn't match what was\nproposed, single-use, and expires. (The first cut of this didn't bind args to\nthe signature and would have let a ticket cleared for \\$1 be spent on any\namount — found by attacking our own build before it shipped, not after.)\n\n**Custody model, stated plainly:** the gateway runs *inside your own\ninfrastructure*. AurumFlux never holds, sees, or transports your provider\nsecret — we ship the software that takes the key out of the agent's hands; we\ndo not become a vault ourselves. Honest limit: a process on the same host that\ncan read the gateway's own environment can still steal the secret. This raises\nthe bar to \"steal from the vault,\" not to physical impossibility.\n\n## Graduated Clearance — maker-checker for the amounts that matter\n\nBinary CLEARED is enough for a $5 API call. It is not what a finance org signs\noff on for a $50,000 payout — they sign off on segregation of duties: the\nperson who proposes a spend is never the person who approves it, on the\nrecord. Graduated Clearance adds thresholds on top of Clearance:\n\n```python\nfrom seal.graduated import GraduatedClearance, APPROVE\n\ngc = GraduatedClearance(seal)\ngc.set_thresholds(\"payout\", auto_ceiling=100, dual_ceiling=10_000, required_approvers=2)\n\n# amount 50   -> AUTO, ordinary Clearance applies\n# amount 5000 -> DUAL, needs 2 distinct human approvals before it can execute\nr = gc.request(\"payout\", 5000, maker=\"alice\", intent=intent)\ngc.add_vote(r[\"id\"], \"bob\", APPROVE)\ngc.add_vote(r[\"id\"], \"carol\", APPROVE)   # now APPROVED — a THIRD person, not alice\n```\n\nWired into the gateway: `Gateway.propose(..., amount=X)` on a path with\nthresholds configured returns `{\"status\": \"needs_approval\", \"tier\": \"DUAL\"}`\ninstead of a ticket until a satisfied `approval_id` is supplied. The maker\ncannot approve their own request — enforced in code, not policy — and one\napprover cannot be counted twice even under a genuine concurrent race, because\nit's a Postgres `UNIQUE` constraint on (approval, approver), not an\napp-level check. A single reject is terminal. An approval authorises exactly\none execution and is bound to the exact intent it was requested for. Every\ndecided approval — approved or rejected, with every vote — is appended into\nthe *same* hash chain the execution certs live in, so `seal verify` covers\ngovernance decisions the same way it covers what actually ran.\n\nBackward-compatible by design: a path nobody ran `set_thresholds()` on never\ntriggers graduated clearance, even if `propose()` is called with an amount —\nexisting budget-only integrations are unaffected.\n\nRun the whole story end to end — no payment provider needed, nothing charged:\n\n```bash\npython3 approval_demo.py\n```\n\nA $200 purchase clears on its own; $12,000 is refused until two *distinct*\nhumans approve; the requester is refused when they try to approve their own;\na duplicate vote from the same approver is refused; one reject is terminal;\n$250,000 is never automatic; and one `revoke` stops even the $200 path. It\nends on the Range Report, which states approvals in money — approved and\nrejected totals — rather than a count of event kinds.\n\n## Portable receipts — evidence that leaves the building\n\nThe dispute that matters spans three parties — the user who authorised an\nagent, the operator who ran it, and the merchant who got paid — and each holds\na database the other two cannot read. `seal verify` answers \"did this run\nexactly once, and did the world confirm it?\", but only to someone holding the\nDSN, which is to say only to the party being asked to prove its own innocence.\n\nA portable receipt is that answer as a file. Certs are hashed over\n**RFC 8785 canonical JSON** and (with a key configured) **Ed25519-signed** at\nwrite time, so a counterparty verifies them with *no database, no network, and\nnone of our code* — [`docs/verify-receipt.mjs`](docs/verify-receipt.mjs) does\nit in ~30 lines of Node:\n\n```bash\npip install 'seal-kernel[signing]'\npython -m seal keygen                     # SEAL_SIGNING_KEY= secret · public key= publish it\npython -m seal export --intent <id> > receipt.json\npython -m seal verify-receipt receipt.json --pubkey <hex>    # needs NO DSN\nnode docs/verify-receipt.mjs receipt.json <hex>              # or no Python at all\n```\n\nHonest limits, on the verdict itself: a pinned-key pass proves *these certs\nwere produced by the key holder and are unaltered* — it cannot prove\ncompleteness (whether other certs exist takes the chain check against the\nstore), and an unpinned pass proves internal consistency only, never\nauthorship. Signing is opt-in; an unsigned store keeps working exactly as\nbefore, and v1 certs keep verifying next to v2 forever.\n\n## settle() — deduplication is not settlement\n\nIdempotency keys make retrying the *same request* safe. They do not answer\nwhat happened after a timeout where the provider may already have acted. That\nintent sits `open`, and before `settle()` the only resolution was implicit — a\nfuture `admit(heal_with=…)` some caller might never make. Now it is one verb:\n\n```python\ngateway.settle(intent)      # uses the path's registered witness\n# CONFIRMED_ONE → healed to WORLD_FINAL, budget reservation settled\n# ABSENT        → claim released for a clean retry, budget returned\n# MULTIPLE      → WORLD_DIVERGED on the chain, domain frozen\n# UNKNOWN       → unresolved, loudly — the claim stands, nothing is guessed\n```\n\n## Obligations — the alarm for what an agent FAILS to do\n\nEvery guard above — and every agent-safety tool we know of — watches\n*commission*: the double-charge, the overspend, the contradiction. Nothing\nwatches *omission*. An agent that crashed, lost its key, or silently stopped\nlooks exactly like an agent with nothing to do — until payroll doesn't go\nout, or the refund that was legally due in 14 days quietly doesn't happen.\n\nThis repo already refuses that failure mode for its tests (`conftest.py`: a\nrun where everything skipped is not a pass). Obligations apply the same\nsentence to production money. It is the dual of the reconcile sweep:\n\n```\nreconcile:    provider effects − admitted intents = out-of-band  (did too much)\nobligations:  declared duties  − sealed intents   = BREACH       (did too little)\n```\n\n```python\nfrom seal.obligation import Obligations\nobs = Obligations(seal); obs.setup()\n\n# at decision time, the agent binds its future self:\nobs.expect(action=\"refund\", key=\"return-123\", due_in_sec=14*86400,\n           description=\"statutory refund window for return #123\")\n\n# the business heartbeat:\nobs.expect_recurring(action=\"renewal\", every_sec=86400, min_count=1)\n\nobs.sweep()   # or: python -m seal obligations   (exit 1 on any open breach)\n```\n\nWhat makes a miss more than a dashboard row: **the breach itself is appended\nto the tamper-evident chain** (deleting it breaks every hash after it), and\n`obligation_breached` is a **licence-suspending event** — a path that goes\nsilent on declared work loses its earned autonomy exactly like a path that\ndouble-charged. Declaring duties is open to agents (`seal_expect` over MCP);\ncancelling one is an operator act with no agent-facing tool, because an\nobligation an agent could cancel is not an obligation. A breach deliberately\ndoes *not* freeze the path — a frozen refund path cannot cure a missed\nrefund; the levers are evidence, alarm, and the licence.\n\n## What a Seal cert does and does not claim\n\nA cert proves the action was **admitted exactly once at this gateway** and that\nthe recorded result hasn't been altered since. It does **not** prove the outside\nworld settled it — every v1 cert carries `world: \"unconfirmed\"`, permanently and\non purpose. \"We admitted this once\" and \"Stripe took the money\" are different\nclaims; conflating them is exactly the bug class this tool exists to stop.\nWorld confirmation (provider adapters that flip that field against Stripe's or\nyour provider's own records) is the next layer, and the cert schema already\ncarries the field so the format won't break.\n\n## Relationship to once-kernel, effectfence, and coherence\n\n[`once-kernel`](https://github.com/aurumflux20/once-kernel-ts) proves one\n*process* didn't run an effect twice. [`effectfence`](https://github.com/aurumflux20/effectfence)\nguards one MCP server. Seal is the cross-process layer above both, for the\nmoment your agents outgrow a single machine. The free primitives stay free\n(Apache-2.0 / MIT), forever.\n\nFor **claim vs proven** on agent PRs and CI (said it ≠ showed it), see the\nseparate project [`coherence`](https://github.com/aurumflux20/coherence) —\nnot part of this repo; different package, different git history.\n\n## License\n\n[Business Source License 1.1](LICENSE): read it, run it, use it in production\ninternally (commercial included) — just don't resell it as a hosted service.\nConverts to Apache-2.0 on 2030-08-12.\n\n---\nmcp-name: io.github.aurumflux20/seal\n",
  "bytes": 22875,
  "sha": "e5876ac01600aef047cfdfe253a8e36711cb794fd992931ee8fac231bed600b4",
  "repo_slug": "aurumflux20/seal",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_aurumflux20_seal_103d1d2f/readme"
}