Agents that can call your APIs and write to your repos need a boundary that isn't the model's judgment. This is the specific set of decisions LiveGraph enforces in code — the model's output is never the boundary; the boundary is code the model can't reach.
Provider keys, GitHub tokens, SSH keys: accepted once and stored AES-256-GCM-encrypted, with the key derived server-side. No API response ever carries a secret back out — list endpoints return metadata only, and decryption happens in memory, inside the specific outbound call that needs it. API keys you mint for programmatic access are stored as a SHA-256 hash — the plaintext leaves the server exactly once, in the response that created it. Tool outputs and error messages pass through secret redaction before they reach a run's event log.
The first write in a run creates a fresh git worktree on its own branch, off the repo's current HEAD — under the allowlisted file root, in a .livegraph/worktrees/ directory your project never tracks. Everything the agent writes goes there; your working tree and its uncommitted state are never touched. Each hop's changes are committed automatically, so every run leaves a reviewable, revertable, cherry-pickable trail on a livegraph/* branch.
The path boundary is enforced below the tool layer: file paths resolve through a realpath walk-up, so a symlink inside the allowed root can't escape it. And agents cannot push or open PRs — there is no push tool. /push and /pr are matched against the literal text a human typed, by deterministic code, before any model involvement. SSH pushes pin the remote's host key (GitHub's published keys; operator-pinned keys for other hosts) rather than trusting first contact.
A node's own settings are written by whoever edits the graph — so node config is never the security boundary. Every dangerous capability is dual-gated behind a separate operator-level allowlist, and every one is empty-deny: unset means no agent gets it at all.
http_request nodes call only endpoint prefixes the operator allowlisted (ALLOWED_HTTP_ENDPOINTS), re-checked at run time. The model supplies a configured slug, never a URL, and the path can't escape the endpoint's own origin and prefix — a crafted path can't redirect one endpoint's credentials to another host.ALLOWED_MCP_SERVERS prefix list.ALLOWED_FILE_ACCESS_ROOTS) and write roots (ALLOWED_FILE_WRITE_ROOTS) are independent allowlists: read never implies write, and write is re-checked at tool-resolution time on every run.The same shape repeats elsewhere: cross-graph dispatch resolves targets by name against a pre-authorized list with ownership re-verified per call, and sandboxed code execution runs in a fresh, network-less sandbox per call — never in-process.
Two levels, both parking at the same review queue. A node-level gate pauses the whole run before that node executes. A per-tool gate (toolApprovalPolicy) pauses mid-hop on a specific call — scoped as narrowly as one endpoint, e.g. http_request:publish-report — with the exact tool name and arguments visible to the approver before anything executes. Only calls that can change something wait — an HTTP GET to a gated endpoint runs without asking. Approvals and denials are stamped with who decided and when; a denial resumes the run with an execution-denied result rather than running the call. Silence fails — a gate that times out never auto-approves.
Every hop lands as an ordered, append-only event: its input, output, status, the routing explanation for auto-resolved edges, the graph version it resolved against, and its own token/cost footprint. Mutations to the graph itself — including mid-run edge drags — are recorded in a routing-changes log keyed to the run that made them, so "what did this run actually execute" is always answerable.
Graphs are shared by role — viewer (watch runs, read the graph) or editor (change it, approve gates) — and removal revokes live access immediately, open canvas streams included. Member-launched runs spend the owner's credentials, so the owner can set a per-member monthly spend cap: shared edit access can't silently become a shared budget. Every graph can also carry a per-run dollar cap of its own.
This is product security posture, not a compliance page. We haven't completed a SOC 2 or ISO audit, and SSO/OIDC and audit-log export aren't shipped yet — they're on the roadmap, not in the product. Agents still write wrong code and take wrong branches; the claim is narrower — when an agent is wrong, the blast radius is a git branch you haven't merged, inside a directory you allowed, on a system where the irreversible actions need a human's literal keystrokes.
The decision-by-decision version of this model — including the review findings that shaped it — is on the blog: Giving agents write access to real code without losing sleep. To report a vulnerability, see the security policy in the repository.