Skip to content

Design rationale

ContainmentRule, WorkloadIdentity, and execution mode are defined on Concepts. This page explains why each design choice looks the way it does, not just what it does.

Non-possession over reduced-consequence mitigation

Section titled “Non-possession over reduced-consequence mitigation”

The fleet leaked API keys three times in 72 hours by cat/grep-ing secret files into permanently indexed transcripts, and a later op:// conversion still passed reference strings as bearer tokens while the op CLI hung machine-wide (VISION.md:12-16). Prompt discipline, secret scanning, short TTLs, and narrower vendor permissions reduce the consequences of a leak; they do not make possession itself safe (VISION.md:18-21). Mint's answer is structural rather than behavioral: keep the bytes out of every agent-accessible surface, so there is nothing left for a leak, a scan miss, or a permission gap to expose. That is why mint-mcp and the CLI's observability commands read declarations and audit history but never construct a SecretStore (crates/mint-mcp/src/lib.rs:1-9,103-106).

The egress credential proxy is the default; a narrowly typed effect or protocol gateway is justified only by a discovered local path that cannot use the proxy while preserving non-possession (VISION.md:103-106). The proxy generalizes to any vendor that accepts a credential in a header or body: authenticate, evaluate policy, resolve the placeholder, forward, scrub, audit — one code path for every HTTP-shaped integration (crates/mint-broker/src/lib.rs:1-12). A typed action is the deliberately expensive exception, built only when a vendor's own response mints a transferable session handle the generic proxy cannot safely relay — see below.

External semantic authority, not policy ownership

Section titled “External semantic authority, not policy ownership”

Mint enforces containment; it does not decide whether an action is appropriate. Deciding whether to run a task, who approves it, and how much it may spend belongs to an external authority system or operator, not to Mint (VISION.md:65-76). Mint may authenticate a caller and mechanically validate an opaque, externally issued grant before using a credential — that is enforcement at the containment boundary, not Mint deciding what an agent is authorized to do (VISION.md:74-78). Destination and alias binding stay inside Mint regardless, because sending a secret to the wrong peer is a disclosure failure, not merely a permission failure (VISION.md:77-78). A Powder card or claim may cause an external authority service to issue a grant, but Mint does not infer semantic permission from ticket lifecycle (VISION.md:139-141). This split is why Budget, Approval, and semantic Permission are deliberately absent from Mint's own product primitives (VISION.md:124-131) — an external grant, when one exists, is validated mechanically at the boundary, never reasoned about by Mint's own policy engine.

SharedSecretAuth checks a bearer capability token against X-Mint-Capability, but its authenticate implementation refuses any caller whose peer address does not canonicalize to loopback before it even looks at the header (crates/mint-broker/src/auth.rs:84-86). A bearer token is a boundary a leaked transcript or a curl from any tailnet peer defeats identically, so it was demoted to a dev/loopback-only path; the loopback check is enforced in the authenticator's own type, not merely by which auth mode a config happens to select — a misconfigured deploy that still wires it up cannot accidentally accept a non-local caller (crates/mint-broker/src/auth.rs:50-59). Production instead resolves identity from the caller's real tailnet peer address via TailnetWhoisAuth: a peer's source IP cannot be forged over a completed WireGuard handshake, while an X-Mint-* identity header is just bytes any TCP-reachable client can set (crates/mint-broker/src/tailnet.rs:1-20). shared_secret_auth_from_a_non_loopback_peer_is_refused and shared_secret_auth_from_loopback_still_works pin both halves of this contract (crates/mint-broker/tests/auth_boundary.rs:73-109,111-144).

AuditGate::prepare durably records an effect as Prepared before proxy_handler ever spawns the task that dials a vendor (crates/mint-broker/src/proxy.rs:261-267, crates/mint-broker/src/audit_gate.rs:268-270). This ordering is what makes a crash, an aborted task, or a dropped client connection safe to reason about afterward: an effect that reached prepare but never reached a terminal record is unresolved, not silently lost, and startup recovery can find it by scanning for exactly that gap (crates/mint-broker/src/audit_gate.rs:79-82). Preparing first, contacting the vendor second, is what turns "did this call happen" into a question the ledger can always answer — see Request flow for the full sequence this enables.

Typed actions are not a general command-runner family; the registry is fixed at compile time (crates/mint-core/src/typed_action.rs:14-21), and exactly one action ships today: firecrawl.interact_prompt (crates/mint-core/src/typed_action.rs:56-67). It exists because Firecrawl's own /interact response mints a transferable cdpUrl, liveViewUrl, and interactiveLiveViewUrl alongside its semantic answer — fields that are the vendor session handle, not an incidental leak a response scrub could catch after the fact (crates/mint-core/src/typed_action.rs:1-12). Mint owns the whole scrape → interact → stop session lifecycle inside one Mint-mediated call, so no caller ever receives a live handle to hold onto (crates/mint-broker/src/typed_effect.rs:18-22). The action's input schema is deliberately prompt-only: Firecrawl's code mode accepts arbitrary Playwright/Python and returns raw stdout/stderr/result, which would make this a generic command runner — an explicit non-goal the input schema enforces by having no code field at all (crates/mint-core/src/typed_action.rs:48-55). A typed-action request follows the same admission order as the proxy — audit-ready check, authenticate, evaluate_tool, prepare, detached task — so it can never skip a gate the proxy enforces (crates/mint-broker/src/typed_effect.rs:50-56).

ClaimSourceLines
Why this exists (three leaks in 72h)VISION.md12-21
mint-mcp never touches SecretStorelib.rs1-9
HTTP proxy is the default execution modeVISION.md101-106
Broker data-plane summarylib.rs1-12
External semantic authorityVISION.md65-78
Powder records work, not authorityVISION.md139-141
Budget/Approval/Permission excludedVISION.md124-131
SharedSecretAuth loopback enforcementauth.rs84-86
SharedSecretAuth demoted to dev/loopbackauth.rs50-59
TailnetWhoisAuth rationaletailnet.rs1-20
Loopback/tailnet auth boundary testsauth_boundary.rs73-109, 111-144
prepare before vendor contactproxy.rs261-267
AuditGate::prepareaudit_gate.rs268-270
Startup recovery of unresolved effectsaudit_gate.rs79-82
Typed-action registry is fixedtyped_action.rs14-21
Exactly one action registeredtyped_action.rs56-67
Why firecrawl.interact_prompt existstyped_action.rs1-12
Whole session lifecycle owned by Minttyped_effect.rs18-22
Prompt-only input, no code fieldtyped_action.rs48-55
Typed action mirrors proxy admission ordertyped_effect.rs50-56