Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Trust boundaries

The single most important decision in Agent Assembly’s Security Model is where it places trust. The answer is recorded formally in ADR 0002 — SDK Security Boundary: the SDK is not a trust boundary; the runtime and gateway are authoritative. This page explains why, and how that decision is made bypass-resistant.

Why the SDK is not a trust boundary

The fastest interception layer — the SDK — runs inside the agent’s own process, which is exactly the component the model does not trust (see the threat model). An attacker who controls the agent controls a modified, outdated, or stubbed SDK. Therefore any guarantee anchored in the SDK is not a guarantee at all: security must hold even when the SDK does nothing.

ADR 0002 audited the prior state and found enforcement in the wrong place — the only credential scan on the SDK fast-path was inside the SDK binding itself, while the trusted runtime forwarded the SDK’s payload without independently scanning it. A removed or bypassed SDK scanner would let raw secrets flow SDK → runtime → gateway. The decision reversed this:

ConcernDecision
Is the SDK a security boundary?No. The SDK is untrusted.
Authoritative enforcement pointaa-runtime — scans, redacts, normalizes every event before forward/audit, unconditionally.
Source of truthGateway / control-plane — policy SoT; audit-write sanitizer as final backstop.
SDK-side detectionBest-effort advisory preflight only. No clean / already_scanned marker exists on the wire, and none is honored.

Invariant. Nothing the SDK asserts can shorten the runtime’s work. The same aa-security scanner is advisory inside the SDK and authoritative inside aa-runtime. Position — not code — confers authority.

Why the runtime / gateway is authoritative

The SDK event fast-path is SDK → UDS → aa-runtime → gRPC → gateway. The runtime is the mandatory chokepoint: every event must pass through it. So that is where the authoritative work lives. RuntimeScanner::enforce (aa-runtime/src/pipeline/enforcement.rs) scans, redacts, and normalizes every secret-bearing field, and the pipeline run() loop (aa-runtime/src/pipeline/mod.rs) calls it before any forward or audit, on every path — both the batched path and the immediate policy-violation path. Its doc comment states the rule directly:

Runs unconditionally — no field of the event can request that scanning be skipped, and there is no SDK trust marker on the wire.

The gateway, in turn, is the source of truth for policy (the agent cannot author its own policy) and applies a final write-boundary sanitizer before anything is persisted.

Bypass resistance — what remains observable when the SDK and proxy are skipped

Trust placement answers “what if the SDK lies?” Bypass resistance answers “what if the agent skips the SDK entirely?” See Enforcement paths and their limitations for the full account of what each mechanism requires and does not guarantee. On Linux, with eBPF loaded and attached, an agent that skips the SDK and the proxy and links TLS directly against OpenSSL is still observed by the eBPF SSL_write / SSL_read uprobes and exec/file syscall hooks (aa-ebpf-probes/src/ssl_probes.rs, aa-ebpf-probes/src/exec_probes.rs), because the kernel sits below anything the agent can reach — but observed only: eBPF returns no verdict, so this is detection, not a second enforcement point, and the same eBPF preconditions (Linux, OpenSSL-linked, loader daemon reachable) apply here as everywhere else on this page’s sibling.

This is verified, not asserted. The bypass-resistance suite drives the public aa_runtime::pipeline::run loop end-to-end and proves every inbound event is scanned + redacted before forward/audit on both paths, with the raw secret never leaving the runtime regardless of SDK behavior (aa-runtime/tests/aaasm_2568_gate_verification.rs). The “no trust marker” guard is partly compile-time — the exhaustive, wildcard-free match over Detail variants forces any new secret-bearing field to be triaged before it compiles.

Trust-boundary diagram

flowchart LR
    classDef untrusted fill:#fdecea,stroke:#d75748,stroke-dasharray: 4 3
    classDef trusted fill:#eaf6ee,stroke:#3aa55b
    classDef sot fill:#e8f1ff,stroke:#5b8def

    subgraph U["UNTRUSTED — agent-controllable process"]
        SDK["Python / Node / Go SDK<br/>+ aa-sdk-client shim<br/><i>advisory preflight only</i>"]:::untrusted
    end

    subgraph T["TRUSTED ENFORCEMENT"]
        RT["aa-runtime<br/>mandatory chokepoint<br/>scan · redact · normalize<br/><b>unconditional</b>"]:::trusted
        PX["aa-proxy<br/>wire egress + scan"]:::trusted
        BPF["aa-ebpf<br/>kernel uprobes / syscalls<br/>bypass floor"]:::trusted
    end

    subgraph S["SOURCE OF TRUTH"]
        GW["aa-gateway<br/>policy SoT · budget<br/>audit-write sanitizer"]:::sot
    end

    SDK -->|"UDS · no trust marker"| RT
    PX --> RT
    BPF --> RT
    RT -->|"gRPC"| GW

    %% the boundary line
    SDK -. "trust boundary" .-> RT

Everything left of the runtime is untrusted and can only advise; everything from the runtime rightward is authoritative. The dashed edge is the trust boundary itself — the SDK’s assertions stop there. See ADR 0002 for the full decision record and the boundary-first migration order that ensured SDK-side scanning was never removed before the runtime became authoritative.

Reviewing boundary changes per release

This page is the authority on where trust sits. To check whether a specific release moved any boundary, fill in the trust-boundary review checklist — a per-release delta form with one row per boundary above, including the guarded “no wire trust marker stays NO” invariant. It is run as part of the /release-security-gate release gate.


Last updated: 2026-08-15 by Chisanan232