For AI agents: the complete documentation index is available at https://scriptedalchemy.github.io/agent-bundle/llms.txt, the full documentation bundle is available at https://scriptedalchemy.github.io/agent-bundle/llms-full.txt, and this page is available as Markdown at https://scriptedalchemy.github.io/agent-bundle/reference/security.md.
Skip to main content
  • English
  • Security

    Most of what follows is a refusal rather than a feature. These are contracts the framework holds to, not defaults you can relax with a flag.

    Provider credentials

    This package never accepts, requests, injects, or persists a model-provider API key.

    Native Claude and Codex harnesses use an already installed, signed-in CLI, and they run after provider-key environment variables have been removed. Credential-shaped material in the evals configuration block is rejected outright rather than ignored: Agent Bundle reuses the host CLI's existing session, and there is no fallback path that reads an API key from configuration.

    Each Codex trial sets a temporary CODEX_HOME and copies only the installed CLI's opaque auth.json into it. Your normal Codex home, configuration, and installed-plugin state are not used as trial state and are left unchanged. The contributor smoke commands remove provider API-key and credential-shaped environment values and fail if the selected host's normal configuration, settings, or installed-plugin state changes. For Claude that surface is ~/.claude/config.json, settings.json, settings.local.json, and plugins/, plus, in the native contract smoke, the user-scope mcpServers registrations inside ~/.claude.json. The rest of ~/.claude.json is host bookkeeping Claude Code rewrites on every start (cached feature flags, first-start and machine identity, usage counters, per-project session statistics) and is deliberately not guarded.

    Native authenticated smokes are excluded from default and ordinary local test runs, and the trusted self-hosted CI workflow that runs them uses an existing subscription session with no workflow secrets.

    The development server

    agent-bundle dev binds to loopback only and is a foreground development session, not a hosted service. The browser is not a trusted input source: it never supplies a command, a working directory, a native model, or a credential, and browser-supplied native models and credentials are refused. Operations are trusted-local only.

    The foreground server's browser routes, session bootstrap, mutations, and the project-event stream, carry an Origin guard: a request whose Origin is not the foreground URL is refused with AB8003 (one without Origin passes only as Sec-Fetch-Site: same-origin), because any other origin, including another loopback origin such as an MCP App sandbox iframe running third-party plugin code on its own loopback port, could otherwise forge requests against it. The contributor HMR allowlist, agent-bundle dev --workbench-dev-origin <origin>, is the only exception: explicit, loopback-only (anything but a bare loopback http/https origin is refused at startup), and off by default, so without the flag the guard is unchanged. The Rsbuild proxy in that loop deliberately never rewrites Origin: the foreground keeps seeing the browser's real origin and admits exactly the listed one instead of trusting whatever a proxy forwards. See Contributor UI HMR.

    Raw HTML, JSX/MDX, and Mermaid in Skill Markdown are inert in the Workbench renderer. A Skill document is content to display, not markup to execute.

    agent-bundle serve-app follows the same model for one MCP App: loopback only, one process lifetime, one bound server. Its /api/mcp/... routes require a per-launch token that only the served document holds, plus same-origin and loopback Host checks; the App document runs on a second loopback origin inside the framework sandbox, and every App action still crosses the same consent authority as in the Workbench (--allow pre-approves named capabilities on the operator's behalf). It is a local preview host, not a deployment target.

    Inside the App document, the agent-bundle/app client trusts no arbitrary postMessage sender. Every inbound frame must come from the exact configured parent (event.source === parent) and pass strict JSON-RPC validation. When targetOrigin is omitted, the client follows the MCP Apps transport contract: every outgoing frame is posted to that parent with '*', and event.origin does not authenticate incoming messages. The exact parent WindowProxy identity is the load-bearing authentication control.

    A host that can name a trusted origin may pass targetOrigin. It must be an exact http: or https: origin; '*', "null", custom schemes, and non-origin strings are rejected. The client then uses that exact origin for every outgoing frame and also requires every incoming event.origin to match it. The Workbench and serve-app keep this stronger host-side boundary in their separate loopback HTTP sandbox proxy: the proxy validates sources and origins before relaying to MCP. rebind() and dispose() still reject pending work and invalidate stale connection generations.

    The host relay is the security boundary. The App client speaks only to its expected parent; the Workbench, serve-app, or an embedding MCP host forwards permitted requests to the one server the frame is bound to and applies its own authentication, capability, and consent policy. An App never reaches the foreground routes or an MCP backend around the relay, and the relay never merges in the permissions of another App, server, or browser origin. The same holds for cancellation: the framework's host bridge honors an App's notifications/cancelled only for a request that App itself started and that is still in flight, aborting that one operation and denying its pending consent challenge, so cancelling can neither reach around consent nor touch a request the App did not make.

    The Agent API

    The Agent API is off by default. When enabled it is an authenticated Streamable HTTP MCP endpoint mounted at /mcp on the same loopback foreground server, with exactly thirteen fixed, ordered tools.

    • Startup fails before serving if the endpoint is enabled without AGENT_BUNDLE_AGENT_API_TOKEN.
    • The fixed token is read once and is never logged, persisted, or returned.
    • Standard Authorization: Bearer authentication is required.
    • Clients may omit Origin; a supplied origin must match the foreground URL exactly.
    • When the endpoint is disabled it is absent, not merely unauthorized.

    The operational details are in Developer Workbench.

    Installation

    Installing a package never mutates a host's plugin state: no npm lifecycle performs an installation. The standalone install.mjs copies into ~/.cursor/plugins/local/<name> without overwriting collisions, is idempotent for identical content, refuses version or content collisions, never invokes sudo, and never changes PATH.

    agent-bundle doctor is read-only by construction. It inventories durable state by directory entry and filesystem metadata only, it never opens a database, and it never repairs anything.

    Artifact integrity

    Content-addressing is the trust anchor for a distributed bundle. Every emitted file carries a SHA-256 in agent-bundle.manifest.json, so a modified artifact fails validation rather than installing quietly, and the host-install proof level fails closed when source, built-artifact, installed-artifact, and running-process versions disagree.

    Path containment is enforced in both directions: a declared logo that escapes the deploy tree (AB6025), a symlink resolving outside a Cursor bundle (AB6028), an installed local plugin whose symlink escapes ~/.cursor/plugins/local (AB7320), an eval runsDir that leaves the project root, and an MCP server name that would traverse out of its state root are all rejected. createProjectContext and project preparation also fail closed when a dangling or cyclic symlink walk leaves the project root or loops (ELOOP), and when a project-relative identity is not a relocatable POSIX path. A POSIX filename that contains a backslash is rejected rather than rewritten into a slash-separated path, so it cannot collide with a different file or reach agent-bundle.manifest.json.

    What is not claimed

    The package publishes no example RSC provider and no host credentials. The native Claude and Codex eval harnesses (runClaudeTrial, runCodexEvalTrial, --harness claude|codex) drive a CLI you have already installed and signed in to; they carry no credentials of their own. Third-party notices for material bundled into the Workbench ship in the published package.

    See Limitations for what the evidence surfaces do not prove.