abri docs

Security architecture

How otto protects your credentials — encryption design, key handling, and what never leaves your machine.

You're trusting otto with the keys to everything else, so this page explains exactly how it works. When you want more depth than any docs page can give, ask — this design is meant to be scrutinized.

The shape: daemon holds the key

The otto CLI never touches your master password after init or unlock. A background daemon (ottod) derives and holds the unlocked vault key in memory; the CLI is a thin client talking to it over a Unix domain socket restricted to your user. This is why reads are fast without re-prompting — and it concentrates the sensitive material in one process that:

  • auto-locks after an inactivity timeout, wiping key material from memory,
  • gates sensitive operations (SSH signing, minting agent tokens) behind a presence check — Touch ID on macOS, a terminal confirmation elsewhere,
  • verifies the identity of every client that connects to its socket.

Encryption design

Your vault is a single SQLite file in which every item is individually encrypted:

  • Key derivation: your master password goes through Argon2id (a memory-hard KDF that resists GPU cracking) to produce a master key. HKDF derives separate sub-keys from it for distinct purposes — the vault key-encryption key, authentication, export — so no key is ever used for two jobs.
  • Per-item envelope encryption: each credential is encrypted with AES-256-GCM under its own random data-encryption key; those item keys are wrapped by the vault key. Rotating your master password re-wraps the item keys — it doesn't re-encrypt your data.
  • Audited primitives: all cryptography comes from the RustCrypto ecosystem; otto contains no homegrown crypto.

Because the vault is one file, backup is cp and the encryption travels with it: a copied vault file is useless without your master password.

Keeping secrets away from AI agents

Injection order of preference, strongest first:

  1. otto run --proxy / otto serve — the agent's environment holds a localhost base URL and a scoped, expiring capability token (otto1.). otto injects the real provider key into the request at forward time. The model never sees the secret: not in context, not in config, not in logs. Every access is written to an audit log with which agent asked for what.
  2. otto run --env — the real value exists only in the child process's environment, never on disk.
  3. otto get — plaintext on stdout, for when you explicitly want it.

Capability tokens are deny-by-default (they can only read the paths you grant), time-limited, and revocable (otto grants / otto revoke).

What leaves your machine

Your secrets don't. otto is local-first: the vault lives on your disk, encryption and decryption happen on your device, and there is no otto cloud holding your keys.

When machines sync (otto sync), vaults exchange encrypted data directly over SSH — peer to peer, no intermediary storage. When a fleet enrolls with the Abri control plane (otto login), the control plane coordinates — device registry, enrollment keys, access policy — but is architecturally incapable of reading your credentials: it never holds vault keys, and secrets never transit it, encrypted or otherwise.

Honest limits

  • otto protects credentials at rest and in transit to processes. A process you inject a secret into can still do whatever it wants with it — scope what you hand to agents via the proxy for exactly this reason.
  • An attacker with root on your unlocked machine can defeat any local credential manager, otto included. Auto-lock and presence gating narrow that window; they don't eliminate it.
  • otto is in early access and an independent cryptography audit is planned before public launch. We'll publish the results.