Skip to content

Security model

This page owns assets, threats, controls, limits, and non-goals. ContainmentRule, AuditEvent, and Decision are defined on Concepts.

  • Vendor credential bytes resolved from custody for one call (crates/mint-core/src/secret_store.rs:49-51).
  • The audit ledger: a durable, hash-chained record of every attempted credential use (crates/mint-core/src/ledger.rs:1-23).
  • Containment policy and capability declarations: which actor may reach which service with which alias (crates/mint-core/src/policy.rs:123-206).

Mint is intended to prevent an agent from committing, printing, logging, prompting with, or messaging a credential; returning it through a tool result; and sending it to an unintended destination through Mint (VISION.md:153-157). Concretely:

  • Cross-alias injection: an actor allowed to call one host embedding a header placeholder for a credential it was never granted. Blocked because calling a host and injecting an alias are bound in the same rule (crates/mint-core/src/policy.rs:144-150), tested by exfil_cross_alias_injection_is_rejected (crates/mint-broker/tests/exfil.rs:62-116). A header value is always an injection slot: an unpermitted alias there denies the call. A body value or a query value is not — a well-formed placeholder for an unpermitted alias stays inert literal text and forwards unresolved instead of denying the call, exactly like a malformed placeholder (crates/mint-broker/src/proxy.rs:536-550,659-668, tested by crates/mint-broker/tests/inert_unpermitted_placeholder.rs).
  • A credential riding alongside a placeholder: a header that resolves a real placeholder but also carries a second, literal credential the agent still holds. Caught by require_placeholder's post-substitution scan, masking only what Mint itself just injected (crates/mint-broker/src/proxy.rs:460-486).
  • SSRF and private-network egress: a resolved target address inside loopback, link-local, CGNAT, or other private ranges is refused unless the matched rule explicitly opts in (crates/mint-broker/src/proxy.rs:169-183).
  • Open redirects and unpinned DNS: the proxy never follows redirects (crates/mint-broker/src/proxy.rs:726-732 builds the client with redirect::Policy::none()) and resolves DNS to the exact addresses it authorized, rejecting any other address a resolver might substitute (crates/mint-broker/src/proxy.rs:1527-1538).
  • A vendor minting a credential Mint never resolved: an OAuth exchange or "create API key" response. Caught by shape detection independent of which field a vendor used (crates/mint-core/src/redact.rs:134-156).
  • A single hung upstream taking down the broker: the circuit breaker opens after consecutive transport failures or 5xx responses and short-circuits further calls to that host (crates/mint-broker/src/breaker.rs:6-17).
  • A tampered audit history: each record hashes its sequence number, the previous record's hash, and its exact stored payload; any mutation, reorder, or deletion breaks the chain on verification (crates/mint-core/src/ledger.rs:1-23).
  • A client disconnect abandoning a prepared effect: the effect continues on a detached task to its own terminal audit record — see Request flow.
  • Response scrubbing (crates/mint-core/src/redact.rs): exact known secret values are removed byte-for-byte (crates/mint-core/src/redact.rs:26-33), then content-type-aware structural scrubbing runs by field name or value shape across JSON, form, SSE, and plain text (crates/mint-core/src/redact.rs:694-704).
  • never_relay_credentials: for a token-minting endpoint, a matched rule can refuse to relay the response at all rather than trust a best-effort scrub (crates/mint-core/src/policy.rs:163-177).
  • Fail-closed audit: a prepared credentialed effect is durably recorded before vendor contact. Early-denial paths attempt to append a denial record before responding. If that append fails, the gate becomes unhealthy and later effects fail closed, but the refused request may be absent from the audit log (crates/mint-broker/src/audit_gate.rs:397-407).
  • Availability guards: a per-workload/service call-rate guard, kept deliberately separate from containment policy and unable to express methods, paths, credentials, spend, approvals, or task semantics (crates/mint-core/src/call_guard.rs:13-16).
  • Constant-time capability comparison: a dev/loopback capability token is hashed at rest and compared as a fixed-size digest, not the raw bearer (crates/mint-core/src/capability.rs:16-28).

Response scrubbing is layered rather than omniscient: exact resolved bytes are removed, known shapes and sensitive fields add defense, and high-risk routes can refuse to relay a response at all — this is not a general, complete guarantee that every credential shape is caught (VISION.md:166-171). The structural shape detectors cover only vendor prefixes and forms Mint's fleet has observed; an opaque token from an unrecognized vendor with no matching prefix or field name can pass a best-effort scrub uncaught (crates/mint-core/src/redact.rs:146-155).

Mint also cannot eliminate every disclosure surface: bootstrap credentials for tailnet enrollment, host deployment, and custody access exist before Mint can mediate anything, and a compromised broker, host, custody root, or external authority issuer can defeat the boundary (VISION.md:166-171). On a single-user machine, same-user process isolation is not a credible raw-secret boundary on its own — an agent that can inspect a child process can often read its environment, files, or command line (VISION.md:173-178).

A body value or a query value is not an injection slot the way a header is: a well-formed placeholder there resolves only when its alias is permitted, and an unpermitted alias stays inert literal text instead of denying the call (crates/mint-broker/src/proxy.rs:536-550,659-668; crates/mint-broker/tests/inert_unpermitted_placeholder.rs). This closes a false-positive failure mode — a caller merely quoting Mint's own placeholder syntax as documentation or prompt text no longer gets denied — but it does not close the matching risk in the other direction: a permitted alias's placeholder text embedded anywhere in a body or a query value still resolves and still injects the real secret at that location, whether the caller meant it as an injection request or as prose. Mint does not yet distinguish those two intents for a permitted alias (tracked in the dc97a88 commit message as mint-permitted-alias-prose-secret-injection).

Mint does not decide whether an otherwise valid action is wise. A bad pull request, wrong refund, excessive purchase, destructive but externally granted deployment, or data exfiltration through a legitimately permitted API belongs to the authority and execution layers, not to Mint (VISION.md:159-164). Mint is not a general exfiltration-prevention system: it defends the confidentiality of the credential bytes it brokers, not the semantic content of what an already-authorized call sends or receives. Budget, Approval, and semantic Permission are deliberately not Mint product primitives (VISION.md:124-131). Mint may mechanically validate an opaque, externally issued grant before using a credential, but that is enforcement at the containment boundary, not Mint deciding what an agent is authorized to do (VISION.md:74-78).

ClaimSourceLines
Threat model, honestlyVISION.md151-178
Cross-alias injection guardpolicy.rs144-150
Cross-alias injection testexfil.rs62-116
require_placeholder mixed-header re-checkproxy.rs460-486
Private-network egress blockproxy.rs169-183
No redirect followingproxy.rs726-732
Pinned DNS resolverproxy.rs1527-1538
Header alias not permitted denies the callproxy.rs403-421
Unpermitted alias in a body value left inertproxy.rs536-550
Unpermitted alias in a query value left inertproxy.rs659-668
Inert-mention regression coverage, header boundary pinnedinert_unpermitted_placeholder.rs1-18
Shape-based credential detectionredact.rs134-156
Circuit breakerbreaker.rs6-17
Hash-chained ledgerledger.rs1-23
Exact-value redactionredact.rs26-33
Structural scrub dispatchredact.rs694-704
never_relay_credentialspolicy.rs163-177
Availability guard, separate from policycall_guard.rs13-16
Constant-time capability comparecapability.rs16-28
Scrubbing is layered, not omniscientVISION.md166-171
Shape detector coverage is boundedredact.rs146-155
Same-user isolation is not a credible boundaryVISION.md173-178
Permitted-alias prose injection is an open risk, not solvedinert_unpermitted_placeholder.rs1-18
Exfiltration through a permitted API is out of scopeVISION.md159-164
Budget/Approval/Permission are not Mint primitivesVISION.md124-131
External grant enforcement, not authorizationVISION.md74-78