Secrets vault

Store credentials the AI needs (a deploy token, a database password) encrypted, and decide for each one what the AI may do with it.

Three states, and the path of a use-only valueEach vault entry is visible, use only or locked. A use-only entry reaches the AI as a placeholder with an ID. The box holds only the placeholder. A request leaves the box through the egress broker, which passes it on with a signed preamble. The key service in a separate VM checks the placeholder ID, the owner and the list of allowed hosts, and inserts the real value on the way out to the target host. Use only works for HTTPS, Git, Postgres and MySQL, not for SSH.One state per entryVisiblethe AI reads the valuein chats you releaseUse onlythe AI sees a placeholderand the allowed hostsLockedseen and used by nothing1. Boxholds only the placeholderzlph_name_ID2. Egress brokerpasses the request onwith a signed preamble3. Key service (separate VM)checks ID, owner, host listinserts the real value4. Target host from allowed forHTTPS, Git, Postgres, MySQL
A use-only value is inserted outside the app server and the box, and only for an allowed host.
Diagram as text
  1. Visible: the AI reads the value in plain text, only in chats you release it for, and only when it asks.
  2. Use only: the AI sees a placeholder such as zlph_github-token_ followed by an ID, and the list of hosts it may be used for.
  3. Locked: the entry is stored in the key service and never inserted anywhere.
  4. The box holds only the placeholder.
  5. The egress broker passes the request on with a signed preamble that names the user and the container.
  6. The key service checks that the placeholder ID is known, belongs to the same account and is not locked, and that the target host is on the list for that entry. It then inserts the real value.
  7. The request goes to the target host. Placeholders in a command line stay placeholders.

Three states per entry

Visible: the AI reads the value in plain text, but only in chats you release it for, and only when it asks. The value is encrypted in the app database, decrypted per chat on request, and every read is logged.

Use only: the AI sees a placeholder such as zlph_github-token_ followed by an ID, and the list of hosts the value may be used for. The real value is stored in the key service and inserted outside the app server and the box, only for an allowed host.

Locked: stored in the key service and never inserted. The AI sees and uses nothing.

Existing entries kept their meaning: those with model access became visible, all others locked and moved to the key service. Each value is encrypted with AES-256-GCM under a key the server or the key service holds, so this is not end-to-end encryption.

How a use-only value is used

The box holds only the placeholder. A request leaves it through the egress broker to the key service, which checks that the ID is known, belongs to your account, is not locked and meets a host on the entry's allowed for list. Then it inserts the value in the URL, in headers and in small JSON, form or text bodies. In a command line, a placeholder stays a placeholder. A refused placeholder is not sent, and the AI is told why.

Use only works for HTTPS and Git targets, and for Postgres and MySQL or MariaDB. For databases the key service completes the login and requires TLS with a checked certificate. A target without TLS is refused, unless you mark that one entry TLS not available. SSH is not covered: pick visible or locked. Git no longer writes its token into the repository configuration; its credential helper hands the box a short-lived placeholder.

Passkey confirmation

Adding a host to allowed for, switching an entry back to visible and showing a value need a fresh passkey confirmation, checked by the key service itself. It is tied to that one action on that one entry, valid for 60 seconds and usable once. The app only relays it.

A new passkey counts only after an existing one confirms it. Removing a passkey and resetting need a signature as well. If every passkey is lost, the key service emails a link to your account address and waits 72 hours after you confirm. After a first passkey or a reset, a waiting period of 24 and 48 hours blocks every confirmation.

The key service reports every change to your protection to you directly, by push message and email, without passing through the app: a new or removed passkey, a reset, a host added, a value shown.

Visible entries and the chat

An entry can be attached to a chat only while its Allow model access switch is on. With the sandbox on, the value of a visible entry is written to a file under /vault in the container (mode 600). The model sees the name, user name, notes and path but not the value, and it can still read the file with commands, so a prompt-injected or misbehaving model could leak it. With the sandbox off, the server decrypts the value and includes it in plaintext in every request to the model provider for that chat.

A chat with an attached credential runs only on a route whose zero-data-retention commitment is confirmed. For Featherless that is the provider’s written commitment of 28 September 2026, which ZeroLog cannot enforce technically. On any other route the request is refused and nothing is sent. You can allow it manually for one chat; the chat then shows that the override is active, and you can revoke it.

Editing or deleting an entry, or detaching it from a chat, wipes the copy in that chat’s running sandbox. If the wipe fails, ZeroLog says so and asks you to delete that chat’s sandbox. A second net in front of every request to a model provider replaces the real values of your entries (blackout).

For keys that agents should use without ever seeing them, see the key proxy. Plain-language overview: API key proxy for AI agents.