中文版 SAFA iconSAFA

Why Your AI Agent Should Never Hold Your SSH Keys

AI agents are doing operations work now — running diagnostics, restarting services, checking databases. The most common instinct is to hand the agent a key or a password. That instinct is exactly the wrong move. Here is the access pattern that keeps the agent useful and the credentials unreachable.

SAFA — Secure Access for Agents2026-08-19github.com/juju-w/safaSee a real diagnosis

The problem: agents are finally doing ops — with the wrong access model

In 2025 and 2026, coding agents and ops agents stopped being a novelty. Claude Code, Codex, and their peers regularly SSH into servers, run kubectl, tail logs, and inspect databases. The standard onboarding pattern for "let the agent touch my infrastructure" is: paste the host, paste the username, point it at your private key, let it use your ssh config.

This is the equivalent of giving a contractor the master key to your building because they asked to check the thermostat. The request is reasonable; the credential is wildly oversized for it — and, unlike a physical key, the digital one is copyable, reusable, and quotable.

Non-human identity (NHI) security is among the fastest-growing segments of cybersecurity: every credential an agent holds is a credential an attacker can steal, reuse, or exfiltrate through a single prompt injection.

Industry research has converged on the same conclusion. Anthropic has published detailed containment work on how its own agents are sandboxed and how credentials are kept out of model reach. NCC Group's security audit of AI coding agents mapped the credential-handling gaps across mainstream tools. Browser-agent research showed agents routinely mishandling cookies and session tokens. The through-line is consistent: the moment a reusable secret becomes model-visible, it is a liability, not a capability.

Why "just give the agent a key" fails

The failure modes are not hypothetical, and they compound:

The pattern: separate the agent from the secret

SAFA (Secure Access for Agents) is an open-source implementation of one idea: the agent never holds a reusable credential; it holds a safe alias and an intent, and a native local boundary does the rest.

The architecture splits authority across processes so that no single agent-facing component can both interpret a request and resolve a secret:

ComponentResponsibilityCredential authority
safa CLIParse agent commands, encode one canonical TOON resultNone
safa-brokerResolve protected records, enforce policy, authorize, connect, sanitizeKeychain / vault owner
safa-askpassDeliver one child-bound, short-lived SSH secretOne-shot only
safa-trusted-setupCollect hidden protected fields after local authenticationNo persistent storage

Concretely, the agent works with safe aliases (nas.primary), never IPs or usernames. Every remote action is checked against an allow-list: exact, reviewed argument vectors for read-only diagnostics (df, uptime, free, ps, systemctl is-active, docker ps/stats). Anything else is refused with a stable, structured error — there is no gray zone, and a format string one character off is refused too.

The agent-facing CLI speaks TOON (schema dev.safa.cli/v2), a canonical structured text format designed for agents (AXI principles): every success, empty state, refusal, and error is one machine-readable document on stdout, with stderr reserved for redacted diagnostics. Remote stdout and stderr are untrusted data, never instructions.

Authorization is the rule, not the exception

SAFA does not just intercept — it gates. High-privilege actions require the user to be present on the Mac. This is already how the preview behaves, not a roadmap promise:

The invariant: the sudo password, the SSH key, the vault key — none of them ever appears in agent argv, environment, stdin, stdout, stderr, chat history, or audit records. Ever. The credential exists only inside the Broker boundary, for the duration of one bounded operation.

Real evidence, not a marketing mock

The live demo is a real recorded session against a real registered resource, replayed step by step with the actual TOON output. In that session the agent:

Every number in that replay is verbatim. The agent never saw an IP, a username, or a key.

Where this fits

SAFA occupies a specific, still-sparse niche: local-first, open-source, agent-native infrastructure access. Enterprise zero-trust platforms (Teleport's agentic identity, P0's authz control plane, BastionZero, Apono) solve similar problems with servers, tenants, and rollout teams. Cloud vendors are shipping agent identity primitives (Entra Agent ID, AWS AgentCore). SAFA is the personal-scale, inspectable implementation: the Skill teaches any capable agent the workflow, the native Runtime is a real security boundary on your own Mac, and the contracts, conformance fixtures, and threat model are public for review.

It is also a deliberately honest preview: macOS-only for now, non-sudo read-only execution today, sudo with scoped approval specified next, database/object-store/cache adapters registered but not yet operable. The guarantees that matter — no credential return path, fail-closed verification, untrusted remote output, explicit scope — are designed in from the first commit, not bolted on.

Get started

On macOS: npx skills add juju-w/safa --skill safa, register a resource once, then just ask. See the product, the architecture explainer, and the real diagnosis.

See the real diagnosis View on GitHub

FAQ

Should I give my AI agent the SSH key?

No. A reusable credential enters chat history and model context, grants an unaudited broad session, and can be exfiltrated through prompt injection. Keep the secret in a native boundary; give the agent aliases and bounded, approved actions.

How does SAFA keep credentials away from the agent?

Process separation: the CLI has no credential authority, the Broker owns policy/credentials/transport, and narrow helpers handle credential delivery and trusted setup. The agent receives only safe aliases and bounded TOON evidence.

What can a SAFA agent do?

Allow-listed read-only diagnostics run automatically. Privileged and protected actions are gated by macOS user-presence approval (Touch ID). Sudo is refused in the preview; scoped, approved sudo is the specified next capability.

Is SAFA open source?

Yes — MIT-licensed. Skill/contracts at github.com/juju-w/safa, native Swift/macOS Runtime at github.com/juju-w/safa-runtime. Currently a macOS source preview.