{
 "version": "https://jsonfeed.org/version/1.1",
 "title": "Mandare Journal",
 "home_page_url": "https://mandarelabs.com/journal",
 "feed_url": "https://mandarelabs.com/journal/feed.json",
 "description": "Field notes on giving AI agents identity, limits, and a tamper-evident record.",
 "language": "en",
 "authors": [
  {
   "name": "Mandare Labs",
   "url": "https://mandarelabs.com/"
  }
 ],
 "items": [
  {
   "id": "https://mandarelabs.com/journal/rfc-9421-http-message-signatures-ai-agents",
   "url": "https://mandarelabs.com/journal/rfc-9421-http-message-signatures-ai-agents",
   "title": "RFC 9421 HTTP message signatures for AI agents",
   "summary": "What RFC 9421 signs, what it leaves to you (body, replay, coverage), and how a gateway can check an agent's signed request. With the RFC's own example.",
   "content_html": "<h2 id=\"a-bearer-api-key-proves-nothing-about-who-sent-the-request\">A bearer API key proves nothing about who sent the request</h2>\n<p><a href=\"https://www.rfc-editor.org/rfc/rfc6750\">RFC 6750</a> defines the property that makes a stolen header dangerous. A bearer token is one \"with the property that any party in possession of the token (a \"bearer\") can use the token in any way that any other party in possession of it can.\"</p>\n<p>An LLM provider key is that kind of credential. Anthropic's <a href=\"https://platform.claude.com/docs/en/api/overview\">API overview</a>, read on 3 October 2026, lists <code>Authorization: Bearer &lt;token&gt;</code> where the token is \"your API key or a short-lived access token\", with <code>x-api-key</code> as a legacy fallback. If an agent's environment leaks, the provider cannot tell the thief from the agent.</p>\n<p>A signed request changes what the server checks. The agent keeps a private key. Each request carries a signature that only that key could produce over that request. Copying the headers off the wire does not give the thief a second valid request.</p>\n<p>RFC 9421 is the standard way to do this over HTTP. Its abstract says it describes \"a mechanism for creating, encoding, and verifying digital signatures or message authentication codes over components of an HTTP message.\" The rest of this entry is about the parts the RFC leaves to the application, because those are where agent deployments go wrong.</p>\n<h2 id=\"what-the-rfc-signs-components-you-choose-plus-parameters\">What the RFC signs: components you choose, plus parameters</h2>\n<p>A signature covers a list of named components. Some are headers, such as <code>content-type</code>. Some are derived values, written with an <code>@</code>, such as <code>@method</code>, <code>@path</code> and <code>@authority</code>. The signer builds a text block called the signature base, one line per component, and ends it with an <code>@signature-params</code> line that repeats the component list and the parameters. It signs those bytes.</p>\n<p>The RFC's own Ed25519 example, from <a href=\"https://www.rfc-editor.org/rfc/rfc9421\">appendix B.2.6</a>, shows the shape. Its line wrapping follows RFC 8792.</p>\n<pre class=\"code\"><code>\"date\": Tue, 20 Apr 2021 02:07:55 GMT\n\"@method\": POST\n\"@path\": /foo\n\"@authority\": example.com\n\"content-type\": application/json\n\"content-length\": 18\n\"@signature-params\": (\"date\" \"@method\" \"@path\" \"@authority\" \\\n  \"content-type\" \"content-length\");created=1618884473\\\n  ;keyid=\"test-key-ed25519\"</code></pre>\n<p>The result travels in two headers. <code>Signature-Input</code> repeats the component list and parameters. <code>Signature</code> carries the signature itself:</p>\n<pre class=\"code\"><code>Signature-Input: sig-b26=(\"date\" \"@method\" \"@path\" \"@authority\" \\\n  \"content-type\" \"content-length\");created=1618884473\\\n  ;keyid=\"test-key-ed25519\"</code></pre>\n<p>Section 2.3 defines the parameters a signer can attach. Four matter for agents:</p>\n<div class=\"table-wrap\"><table><thead><tr><th>Parameter</th><th>RFC 9421 wording</th><th>Use for an agent</th></tr></thead><tbody><tr><td><code>created</code></td><td>\"Creation time as a UNIX timestamp value of type Integer\"</td><td>When the agent signed</td></tr><tr><td><code>expires</code></td><td>\"Expiration time as a UNIX timestamp value of type Integer\"</td><td>A short life for a captured request</td></tr><tr><td><code>nonce</code></td><td>\"A random unique value generated for this signature as a String value\"</td><td>Detecting a repeat</td></tr><tr><td><code>keyid</code></td><td>\"The identifier for the key material as a String value\"</td><td>Which key to verify with</td></tr></tbody></table></div>\n<p>Verification (section 3.2) rebuilds the signature base from the received message and the declared components, then checks the signature against the key. The list of covered components comes from the sender's <code>Signature-Input</code> header, so the verifier has to decide whether that list is enough. That point comes back below.</p>\n<h2 id=\"the-rfc-does-not-sign-the-body-so-add-a-digest\">The RFC does not sign the body, so add a digest</h2>\n<p>Look at the example again. It covers <code>content-type</code> and <code>content-length</code>, but nothing in it commits to the JSON that follows. For an agent that matters: the body holds the model name, the token limit and the prompt, which is where the cost is decided.</p>\n<p>Section 7.2.8 is direct about it. The specification \"does not provide coverage for the content of an HTTP message under the signature, in either a request or a response.\" It points to the <code>Content-Digest</code> field from <a href=\"https://www.rfc-editor.org/rfc/rfc9530\">RFC 9530</a>, which \"can be used in requests and responses to communicate digests that are calculated using a hashing algorithm applied to the actual message content.\" Once the field exists, you list <code>content-digest</code> as a covered component like any header.</p>\n<p>The digest covers exact bytes. RFC 9421 section 7.2.8 and RFC 9530 use the same JSON object and give different digests:</p>\n<pre class=\"code\"><code>{\"hello\": \"world\"}       sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:\n{\"hello\": \"world\"}\\n     sha-256=:RK/0qy18MlBSVnWgjwz6lZEWjP/lF5HF9bvEF8FabDg=:</code></pre>\n<p>RFC 9421 shows the first value. RFC 9530 shows the second next to <code>Content-Length: 19</code>, one byte more than the 18 in the RFC 9421 example. Computing SHA-256 over both byte strings gives exactly those two values: the second input ends in a newline. A client that re-serializes JSON after hashing, or a proxy that reflows it, produces a mismatch even though the data means the same thing. Hash the bytes you send.</p>\n<p>A digest alone is not protection either. RFC 9530 section 6.1 warns: \"In the absence of additional security mechanisms, an on-path malicious actor can either remove a digest value entirely or substitute it with a new digest value computed over manipulated representation data or content.\" The signature over <code>content-digest</code> is the additional mechanism. Remove the digest from the covered list and the body is unprotected again.</p>\n<h2 id=\"replay-protection-needs-a-nonce-and-a-verifier-that-remembers\">Replay protection needs a nonce and a verifier that remembers</h2>\n<p>Section 7.2.2 describes the failure plainly. \"The most extreme form of this would be a signature over no message components. If such a signature were intercepted, it could be replayed at will by an attacker, attached to any HTTP message.\" Even with good coverage, \"a given signature could be applied to two similar HTTP messages.\"</p>\n<p>The RFC's remedies are layered. Cover enough of the message to tell it from others. Add <code>nonce</code> \"to allow the verifier to detect replay of the signature itself if a nonce value is repeated\". Add <code>created</code> and <code>expires</code>, \"limiting the utility of a captured signature value.\"</p>\n<p>Two consequences for an implementer:</p>\n<ul><li>The nonce only works if the verifier stores it. A nonce check against an in-memory set forgets everything when the process restarts, and the window in which that matters is the signature's remaining life.</li><li>The store should keep a nonce until after <code>expires</code>, with a margin for clock differences, so a captured request cannot pass in the last instant of its window.</li></ul>\n<p>A short <code>expires</code> makes the store small. A signature that lives 30 seconds needs 30 seconds of nonce memory, not a day's.</p>\n<h2 id=\"the-verifier-not-the-signer-decides-what-must-be-covered\">The verifier, not the signer, decides what must be covered</h2>\n<p>Section 1.4 puts the burden on whoever deploys it. \"An application or profile of this specification MUST specify, at a minimum\" the set of components and parameters \"that are expected and required to be included in the covered components list\", plus how to find the key and which algorithms are acceptable.</p>\n<p>The reason is that <code>Signature-Input</code> is written by the sender. A valid signature over <code>(\"@method\")</code> proves the sender holds the key and said something about the method. It proves nothing about the path or the body. A verifier that checks the signature and does not compare the declared components against its own required list accepts a signature that binds almost nothing.</p>\n<p>A written policy for an agent gateway then reads like a checklist: required components (<code>@method</code>, <code>@path</code>, <code>@authority</code>, <code>content-digest</code>), required parameters (<code>created</code>, <code>expires</code>, <code>nonce</code>, <code>keyid</code>), a maximum lifetime, an expected key for this agent, and a refusal for anything else. Refuse by default and say which rule failed, so the refusal can be recorded.</p>\n<p>Parsing matters too. Structured Fields allow parameters on inner-list members. A header such as <code>(\"@method\";a=\"@path\")</code> covers only <code>@method</code>, even though a text scan sees both names. A verifier has to parse the list, not search it.</p>\n<h2 id=\"web-bot-auth-fixes-a-profile-for-automated-clients\">Web Bot Auth fixes a profile for automated clients</h2>\n<p>RFC 9421 leaves the profile open. Web Bot Auth is one profile aimed at automated HTTP clients. Cloudflare's <a href=\"https://developers.cloudflare.com/bots/reference/bot-verification/web-bot-auth/\">documentation</a>, read on 3 October 2026, describes it as a way to \"verify bot identity using cryptographic HTTP message signatures\". It says to attach three headers, <code>Signature</code>, <code>Signature-Input</code> and <code>Signature-Agent</code>, to a bot's requests, that the <code>tag</code> parameter should equal <code>web-bot-auth</code>, and that \"Cloudflare's implementation of Web Bot Auth does not support every component and parameter defined in IETF RFC 9421.\"</p>\n<p>The profile is moving. The IETF datatracker lists <a href=\"https://datatracker.ietf.org/doc/draft-ietf-webbotauth-httpsig-protocol/\">draft-ietf-webbotauth-httpsig-protocol-00</a>, an active working-group draft dated 1 September 2026. It requires <code>created</code>, <code>expires</code>, <code>keyid</code> (as a JWK SHA-256 thumbprint) and <code>tag=\"web-bot-auth\"</code>, and at least one of <code>@authority</code> or <code>@target-uri</code>. It recommends expiry \"no more than 24 hours\". It defines <code>Signature-Agent</code> as a Dictionary Structured Header whose members are strings containing <code>https</code> URIs, notes that earlier versions used a bare string, and says signers \"MUST send the dictionary form\". It also says implementations \"MUST NOT use shared HMAC\".</p>\n<p>That last rule is a useful test for any agent scheme. If a verifier and a client share a secret, the verifier can forge requests, and a stolen verifier database is a set of working credentials. An asymmetric key avoids that.</p>\n<p>Treat the draft's numbers as a snapshot. It is a <code>-00</code> document and the earlier individual drafts it replaced have already expired.</p>\n<h2 id=\"where-mandare-fits-and-what-it-does-not-do\">Where Mandare fits, and what it does not do</h2>\n<p>Mandare's gateway (the \"door\") can require an RFC 9421 signature from each agent. The concept docs say agents \"sign every request under their key (RFC 9421 HTTP Message Signatures, with a Content-Digest over the exact body bytes)\", described under <a href=\"/docs/concepts\">Passport in the concepts page</a>. The implementation is <a href=\"https://github.com/mandarelabs/mandare/blob/main/packages/passport/src/request-signature.ts\">the request-signature source file in the passport package</a>, using the <code>web-bot-auth</code> npm package at version 0.1.3, whose npm metadata points to Cloudflare's <code>web-bot-auth</code> repository on GitHub. This applies to the CLI at v0.1.0, which <code>npm view @mandarelabs/cli version</code> reported on 3 October 2026.</p>\n<p>What the code does, as it reads:</p>\n<ul><li>The required covered components are <code>@method</code>, <code>@path</code>, <code>@authority</code>, <code>content-digest</code> and <code>signature-agent</code>. A signature that covers fewer is refused with <code>COMPONENTS_NOT_COVERED</code>, and so is a <code>Signature-Input</code> whose inner-list members carry parameters.</li><li>The signing key must be the key the agent's delegation credential binds. A different key is refused with <code>WRONG_KEY</code>.</li><li>The default signature lifetime is 30 seconds and the ceiling is 300 seconds. The nonce is claimed only after the signature verifies, so a bad probe cannot use up a real client's nonce.</li><li>A body that does not match its digest is refused with <code>BODY_DIGEST_MISMATCH</code>; a repeated nonce with <code>REPLAYED_NONCE</code>.</li></ul>\n<p>The unit tests in <code>packages/passport/test/request-signature.test.ts</code> cover each of those refusals. Once identity is proven, the gateway's later refusals are ledger entries, shown with spend caps in <a href=\"/journal/runaway-loop-dies-at-20\">a runaway loop that dies at €20</a>, and the ledger itself is described in <a href=\"/journal/proofs-not-data\">why it holds proofs, not data</a>.</p>\n<p>Limits, stated next to what they limit:</p>\n<ul><li>A request that fails authentication is not a ledger entry. The gateway's passport-mode test sends an unsigned request, expects <code>401</code> with code <code>PASSPORT_MISSING</code>, and expects the ledger to hold zero entries. Refusals after identity is proven, such as a killed agent's call, are recorded with the agent's DID.</li><li>Passport mode is in the TypeScript SDK only. The <a href=\"/docs/integrations/sdk\">SDK page</a> says Python's standard library has no Ed25519, so the Python client supports token mode. Bodies that cannot be digest-signed exactly, such as streams and form data, are refused client-side.</li><li>The <code>Signature-Agent</code> header carries the agent's <code>did:key</code> as a quoted string, and <code>keyid</code> is the SHA-256 hex of the raw public key. The draft requires <code>https</code> URIs in a dictionary and a JWK thumbprint as <code>keyid</code>. We make no claim that a Mandare-signed request passes a Web Bot Auth verifier.</li><li>Token-mode proofs in the repo's published dead-paper demo do not bind the body. The <a href=\"/docs/threat-model\">threat model</a> says so and states that passport mode closes the gap. The demo transcript in the repo covers token mode, not RFC 9421.</li><li>A signature proves possession of a key at signing time. If the private key leaves the agent's host, the thief can sign valid requests until the credential is revoked. Revocation is a separate mechanism: <code>mandare kill &lt;agent-did&gt;</code> is documented in the <a href=\"/docs/reference/cli\">CLI reference</a>.</li></ul>",
   "image": "https://mandarelabs.com/journal/og/rfc-9421-http-message-signatures-ai-agents.png",
   "date_published": "2026-10-03T16:31:00+00:00",
   "date_modified": "2026-10-03T16:31:00+00:00",
   "tags": [
    "Standards"
   ]
  },
  {
   "id": "https://mandarelabs.com/journal/proofs-not-data",
   "url": "https://mandarelabs.com/journal/proofs-not-data",
   "title": "Proofs, not data",
   "summary": "Why the accountability layer for AI agents must not see your data — and how content-free fingerprints still make the record checkable by anyone.",
   "content_html": "    <p>Every activity-recording system faces the same question sooner or later: <em>who watches the record?</em> If your agents' complete history — every API call, every payment, every approval — sits on a vendor's servers, that vendor is now the most valuable target in your stack. It can be breached. It can be subpoenaed. It can quietly become a data business. Enterprises know this, which is why \"send us your full agent telemetry\" is a hard sell to exactly the buyers who need accountability most.</p>\n\n    <h2>The record stays home</h2>\n    <p>Mandare is <strong>local-first</strong>: the ledger — the append-only, hash-chained record of everything your agents did — lives on <em>your</em> machine. We never see its contents. What leaves your machine is a <strong>content-free cryptographic fingerprint</strong>, sent to a witness you choose — a second machine of your own or a third party: a hash that commits to the record's state without revealing a single byte of what's in it.</p>\n\n    <div class=\"split\">\n      <div class=\"yours\">\n        <span class=\"micro head\">What your ledger holds — on your machine</span>\n        <div class=\"flood\" aria-hidden=\"true\">\n#139  approval.requested  ~0.2778 EUR → owner<br>\n#140  approval.granted    by did:key:z6Mkw9rGDpbp…<br>\n#141  CARD     settle  4.20 EUR · ACME SaaS<br>\n#142  <span class=\"g\">RESULT</span>   settle  0.277797 EUR · api.provider/v1<br>\n#143  <span class=\"r\">DENIED</span>   refused 0.277803 EUR ← policy<br>\n        </div>\n      </div>\n      <div>\n        <span class=\"micro head\">What the witness sees</span>\n        <p class=\"hash\">77014fc7fd8b70f0c785abb2e37179225f7de8b22f99187495770cad6a881a66\n          <span class=\"verdict\">a fingerprint — enough to check integrity, nothing to read, leak, or subpoena</span>\n        </p>\n      </div>\n    </div>\n\n    <p>That fingerprint is not decoration. Because each entry is hash-linked to the previous one and signed by the infrastructure that wrote it — the door the agent passed through, never the agent itself — any later edit to an entry breaks the chain visibly, and <code>mandare verify</code> names the entry. Dropping the newest entries is subtler: the shorter file still verifies on its own. That's what the witness is for. Run on infrastructure you don't control, with the verifier holding the door key out-of-band, it makes truncation and rollback detectable too. The record's <em>integrity</em> becomes publicly checkable while its <em>contents</em> stay private.</p>\n\n    <h2>What this buys you</h2>\n    <p><strong>Nothing to leak.</strong> We can't lose, sell, or be forced to hand over what we never hold. Your agents' activity is not our asset; its integrity is.</p>\n    <p><strong>Nothing custodial.</strong> Mandare never holds the money either — no float, no banking license, no counterparty risk hiding in the fine print. Limits are enforced at the doors; funds never touch us.</p>\n    <p><strong>Evidence that convinces skeptics.</strong> An auditor or a finance team doesn't have to trust your agent's self-report — agents demonstrably misreport their own actions — or trust us. They verify the chain themselves. Verification is free, for anyone, forever; the verifier is open source (Apache-2.0). You can see exactly what that verification looks like in <a href=\"/journal/runaway-loop-dies-at-20\">a runaway loop dying at €20</a>, where the refusal itself becomes a checkable ledger entry.</p>\n\n    <h2>No invented standards</h2>\n    <p>The cryptography here is deliberately boring: HTTP Message Signatures (RFC 9421) for signed requests, a JWK Set key directory for publishing door keys (modelled on the Web Bot Auth directory drafts; we make no claim that a Web Bot Auth verifier accepts a Mandare signature or directory), SD-JWT VC for credentials, did:key for identifiers. Mandare's job is to compose the standards the web is already adopting — and to fit into that world rather than fight it.</p>\n\n    <p>In an industry nervous about AI oversight, an oversight layer that <em>cannot</em> surveil you is rare. That's not a compromise we accepted. It's the design. The full picture — identity, mandates, and this ledger — lives on the <a href=\"/#trust\">Mandare homepage</a>.</p>",
   "image": "https://mandarelabs.com/journal/og/proofs-not-data.png",
   "date_published": "2026-07-23T09:00:00+00:00",
   "date_modified": "2026-10-04T09:00:00+00:00",
   "tags": [
    "Design"
   ]
  },
  {
   "id": "https://mandarelabs.com/journal/runaway-loop-dies-at-20",
   "url": "https://mandarelabs.com/journal/runaway-loop-dies-at-20",
   "title": "A runaway agent loop dies at €20",
   "summary": "71 API calls in 0.1 seconds. Call #72 refused by a mandate signed once — and the refusal becomes tamper-evident evidence. Real transcript, no mockups.",
   "content_html": "    <p>The most common way an AI agent hurts its owner isn't malice. It's a loop. A retry that always fails, a plan that keeps \"making progress\" against an API that keeps billing, a sub-task that spawns the same sub-task. Left alone overnight, it bills all night — and in most teams, nobody can even say <em>which</em> agent spent what.</p>\n\n    <p>The uncomfortable part: the agent can't be argued out of it. A loop looks like work from the inside. Asking the model to \"be careful with spend\" is a suggestion, not a control — and per-action approval prompts don't help either, because people approve 93% of them (<a href=\"https://www.anthropic.com/engineering/claude-code-auto-mode\">Anthropic's figure for Claude Code</a>) and attention fades. If the stop is going to happen, it has to happen <strong>outside the agent</strong>.</p>\n\n    <h2>One signature, one boundary</h2>\n    <p>In Mandare, the owner signs a <strong>mandate</strong> once: this agent may spend €5 per call, €20 per day, €100 in total. The agent never sees a permission prompt again — it works freely inside that boundary. But every call it makes must pass through a door (the gateway) that checks the mandate before anything is spent. The agent doesn't enforce its own limits, so it can't talk itself past them.</p>\n\n    <p>Here's what happens when we deliberately release a runaway loop against that door:</p>\n\n    <div class=\"terminal\">\n      <div class=\"terminal-bar\"><span class=\"title\">demo/S2 — runaway loop · recorded 2026-07-21</span><span class=\"badge\">live capture</span></div>\n      <pre><code><span class=\"c\"># mandate signed ONCE: €5/call · €20/day · €100 total</span>\n<span class=\"c\"># releasing a runaway loop — it will NOT stop itself…</span>\n\ncall #  1   <span class=\"ok\">200 OK</span>     ~€0.28 spent so far\ncall # 10   <span class=\"ok\">200 OK</span>     ~€2.78\ncall # 30   <span class=\"ok\">200 OK</span>     ~€8.33\ncall # 50   <span class=\"ok\">200 OK</span>     ~€13.89\ncall # 70   <span class=\"ok\">200 OK</span>     ~€19.45\ncall # 72   <span class=\"no\">403 DENIED — THE LOOP DIES HERE</span>\n  code:   PER_DAY_EXCEEDED\n  reason: reserved 0.00 + settled 19.72 + estimate 0.28 &gt; cap 20.00\n  the refusal itself is ledger entry <span class=\"hi\">92938e3617eb03c8…</span>\n\n<span class=\"c\"># the runaway made 71 calls in 0.1s before the mandate killed it</span></code></pre>\n    </div>\n\n    <p>Seventy-one calls went through in a tenth of a second — far faster than any human could react, faster than any dashboard alert could fire. The seventy-second call was refused at the door. Total damage: <strong>€19.72, against a €20 cap</strong>. (This is the no-docker <code>pnpm demo</code>; the docker quickstart runs the stack's real defaults and stops at call #24, €19.17 settled.)</p>\n\n    <h2>The refusal is evidence</h2>\n    <p>Stopping the loop is half the story. The other half: every one of those calls — including the refusal — was written to an append-only, hash-chained ledger <em>by the door</em>, never by the agent. Intent is logged before the action runs; results are logged as they settle. Then anyone can verify the record:</p>\n\n    <div class=\"terminal\">\n      <div class=\"terminal-bar\"><span class=\"title\">mandare verify</span><span class=\"badge\">chain: VALID</span></div>\n      <pre><code><span class=\"p\">$</span> mandare verify --db ledger.db --spend\nentries:  143\nchain:    <span class=\"ok\">VALID</span> — every entry hash-linked and door-signed\nspend:    settled 19.723587 EUR · 71 call(s) · 1 refused\n  #142  RESULT   settle  0.277797 EUR\n  #143  <span class=\"no\">DENIED</span>   refused 0.277803 EUR  ← refused by policy\ncounters: <span class=\"ok\">CONSISTENT</span> — equal to a fresh replay of the ledger</code></pre>\n    </div>\n\n    <p>That last line matters more than it looks: the running totals aren't a second source of truth that could drift — they're checked against a full replay of the ledger itself. And critically, that ledger lives on your machine, not ours — all that ever leaves it is a content-free fingerprint for a witness you choose, which is a design choice worth its own entry: <a href=\"/journal/proofs-not-data\">proofs, not data</a>. When a finance team asks \"what did this agent actually spend, and why did it stop?\", the answer isn't a screenshot or an agent's self-report. It's a verifiable file.</p>\n    <p>One honest caveat about this run: <code>pnpm demo</code> has no witness. Any <em>edited</em> entry fails verification, but dropping the newest entries — the refusal included — leaves a shorter ledger that still verifies. That's what a witness on separate infrastructure (or a saved <code>--prev-head</code>) catches; the docker quickstart runs one, and <code>pnpm demo:witness</code> shows it catching exactly that.</p>\n\n    <h2>Why enforcement lives at the door</h2>\n    <p>Because the boundary is enforced by infrastructure the agent must pass through, it holds in every failure mode that matters: the agent loops — capped. The agent is prompt-injected into ambition — capped. The agent's credential is stolen and driven by an attacker — still capped, because the mandate binds the credential, not the agent's intentions. The human signed once; the mandate did the saying-no.</p>\n\n    <p>This demo is one of five that CI runs on every push — a stolen credential turning to dead paper, one mandate replacing forty approval prompts, a card declining at the network (so far against a Stripe mock that signs webhooks like Stripe), and a witness catching a truncated ledger are the other four. All five are in the <a href=\"https://github.com/mandarelabs/mandare\">open-source repo</a>. You can also see the whole system on the <a href=\"/#how\">Mandare homepage</a>.</p>",
   "image": "https://mandarelabs.com/journal/og/runaway-loop-dies-at-20.png",
   "date_published": "2026-07-23T08:00:00+00:00",
   "date_modified": "2026-10-04T08:00:00+00:00",
   "tags": [
    "Demos"
   ]
  }
 ]
}
