| Version | Supported |
|---|---|
| 0.0.x (alpha) | ✅ Active development — security patches applied |
Please do not report security vulnerabilities through public GitHub issues.
To report a security issue, use GitHub's private vulnerability reporting:
- Go to the Security tab of this repository.
- Click "Report a vulnerability".
- Fill in the details and submit.
Alternatively, email security@agent-assembly.com
Legacy address.
security@agent-assembly.devremains a legacy compatibility alias. During the in-progress migration to the canonicalsecurity@agent-assembly.comidentity, the legacy address continues to receive mail via Cloudflare Email Routing, so a report sent there still reaches us. The canonical mailbox is not yet live-sending.
with the subject line: [SECURITY] agent-assembly — <brief description>.
- A description of the vulnerability and its potential impact.
- Steps to reproduce or a proof-of-concept.
- The affected version(s) and component(s).
- Any suggested mitigations, if known.
| Stage | Target |
|---|
| Acknowledgement | Within 2 business days | | Initial assessment | Within 5 business days |
| Patch or mitigation | Dependent on severity (Critical: 7 days, High: 14 days, Medium/Low: next release) |
The gateway's gRPC agent plane (default 127.0.0.1:50051, and the optional
Unix-domain socket) carries the agent lifecycle, policy, approval, audit,
topology, and secrets RPCs. Its security model has two layers:
-
Per-RPC credential authentication (always on). Every RPC must present the agent
credential_tokenissued at registration — in thex-aa-credential-tokenmetadata header, or asauthorization: Bearer <token>. The gateway resolves the token to a verified caller identity (agent + tenant) and fails closed (rejects withUNAUTHENTICATED) on a missing, malformed, or unknown token. Approval decisions are bound to the authenticated caller's tenant, and the deciding operator (decided_by) is derived from the verified caller — never trusted from the request body. Rejections are counted in theaa_grpc_auth_rejected_totalmetric. -
Network exposure (operator responsibility). The plane binds to loopback by default and the gateway is not shipped in the limited-function OSS self-host stack. Do not bind the gRPC plane to a routable interface without enabling transport encryption. mTLS is the supported transport hardening for non-loopback deployments; it is configured via
AA_GATEWAY_GRPC_TLS_CERT/AA_GATEWAY_GRPC_TLS_KEY(andAA_GATEWAY_GRPC_CLIENT_CAfor mutual TLS). While the live TLS handshake is being finished (tracked under AAASM-3418), the gateway refuses to start if these variables are set rather than serve plaintext on a socket the operator believes is encrypted.
Honest boundary: per-endpoint authentication is endpoint hygiene, not an absolute control. The sidecar proxy can independently deny egress traffic that fails policy, and eBPF can independently detect what neither the SDK nor the proxy observed — but neither is a backstop that catches what the other misses; each reaches its own claim level (ADR 0033 §6), and an absent mechanism is reported as absent, not covered by another.
The aa-api REST/HTTP surface and the bundled React operator dashboard
(including its WebSocket live-ops, approvals, and alert streams) are designed for
a local / self-hosted / operator-controlled deployment — a single process on
the operator's own host or private network. Treat them accordingly:
- Do not expose the dashboard /
aa-apiHTTP surface directly to the public internet without a trusted authenticating layer in front of it (a VPN, a private network, or an authenticated reverse proxy).aa-apibinds to loopback by default; binding to a routable interface (e.g.--mode remote) puts the API and dashboard on the network. - Browser session auth is a scoped trade-off. The dashboard keeps its session
JWT in
sessionStorageunder a strict CSP. This is an intentional, accepted trade-off for the OSS local threat model — it is not hardened against a same-origin XSS, and it is not the design the SaaS edition uses. See ADR 0012. - WebSocket streams carry no credential in the URL. Browser WS connections
authenticate with a short-lived, single-use ticket minted over an authenticated
REST call (AAASM-4861), so no long-lived token appears in a URL that
proxy/CDN/LB access logs would capture. The application logs the request path
only, not the query string; operators who front
aa-apiwith their own reverse proxy / CDN should still configure edge redaction oftoken/ticketquery parameters — infrastructure outside this repo is not automatically protected.
The DI-API is the local socket by which an untrusted local client — a VS Code
extension, a JetBrains plugin, an installer, or the aasm CLI — asks the runtime
to install, inspect, verify, repair or remove a developer-tool integration.
Four properties define its posture:
- Off by default. The runtime reads
AA_DEVINT_ENABLEDat startup and binds nothing without it. On crates.io only, it is absent altogether:.ci/strip-for-publish.shruns inrelease.yml'spublish-cratesjob and removes the DI-API bring-up fromaa-runtimeand theaasm integrationsclient fromaa-cli, socargo install aasmhas neither end of this channel. Every other channel — the GitHub Release tarballs, thecurlinstaller and the Homebrew formula — ships binaries built from the unstripped tree in thebuildjob, so both ends are present there andAA_DEVINT_ENABLEDis the only thing standing between them and a bound socket. Treat the environment gate, not the strip, as the control that applies to the binaries most users actually have. - A second socket that carries no policy and no agent traffic. It is
deliberately separate from the SDK fast-path socket, and that separation is a
security property rather than tidiness: a DI client never holds a file
descriptor onto agent-action traffic, so that traffic is unreachable to it by
construction rather than by an authorization rule someone has to remember.
Allow/deny decisions still go SDK →
aa-sdk-client→ runtime/gateway, on a different socket with a different verb space. - Two-layer authentication, failing closed. A
0700directory, a0600Unix socket, and a peer-credential check requiring the connecting process's UID to equal the runtime's — a mismatched or unreadable peer credential is dropped before any frame is read. Above that, a per-client capability token written0600; a token file readable by more than its owner is refused rather than used, so a filesystem mistake cannot become a silent authentication downgrade. There is no anonymous tier. Loopback TCP is not offered and will not be — a TCP port is reachable by every local user and by any browser on the machine, the kernel supplies no peer identity for it, and it adds CSRF and DNS-rebinding surface. A deployment that relocates the socket viaAA_DEVINT_SOCKETmust preserve both permission bits. - No sensitive payload can cross it. No DI-API response type has a field able to hold a rendered settings body, an environment-variable value, a policy document or a credential; a policy is named by reference (id, display name, digest), never carried.
aasm integrations install --install-managed-settings is the only privileged,
root-owned write the product performs. It is macOS-only, opt-in, never a
default, and never implied by a protection profile — --scope managed on its own
is refused precisely because it says nothing about administrator authorization.
It elevates for a single file placement and nothing else: aasm never runs as
root, and no other step in any plan asks for authorization.
Before consent is requested, the plan discloses the exact path, the exact content and its SHA-256, the diff against what is on the host, any conflict, and the backup and rollback behaviour. It refuses to replace a managed-settings file Agent Assembly did not write (for example one deployed by your organisation's device management), fails immediately without a terminal rather than blocking on a credential prompt nobody can answer, and rolls the write back if the read-back does not match rather than reporting success on the authorization mechanism's word.
Honest boundary: this is the only route to a Host Enforced protection level,
and Host Enforced means "the managed policy is installed at the OS-managed
path, owned as expected and not writable by you." It does not mean the
bypass has been demonstrated to fail. That half is unmeasured on every host,
including managed ones: no run has yet put a managed-only key against a real
user-side override attempt. Nor is a device-management enrolment what is missing
— the write is a plain install -o root -g wheel to a filesystem path, with no
configuration profile or managed preference domain involved, so an
administrator-consented write on any Mac produces the same artifact. AAASM-5308
carries the measurement. See
Limitations and known bypasses.
We follow coordinated disclosure. Once a fix is available, we will:
- Release a patched version.
- Publish a GitHub Security Advisory.
- Credit the reporter (unless they prefer to remain anonymous).