Skip to main content
An agent can complete an authenticator-app sign-in step without ever seeing the authenticator secret or the code.

The guarantee

A stored authenticator (TOTP) secret never leaves the Agent Vault backend. Only the current code is derived from it, only for a fill on the login’s registered site. The backend returns that code to the proxy, which types it into the site, so the proxy and the site receive the code. The agent and the account holder never see it. The guarantee covers a secret once it is stored. Before that, during enrolment by an agent, the site’s page shows the new otpauth:// secret to the proxy, and the proxy reads it and sends it to the backend to encrypt. The agent does not receive it on that path either. A TOTP secret is long-lived and generates every future code, so it must never enter an agent’s context. But an agent that cannot complete a two-factor step cannot sign in to most sites worth signing in to. Agent Vault resolves this by letting the agent fill a token while the backend derives the code.

How it works

The secret stays in the backend, encrypted at rest. What crosses to the proxy is a short-lived code, valid for the current window only.
Agent-facing tool descriptions summarise this as “the proxy derives the code”. Precisely, the backend derives it and the proxy types it. What the agent can observe is the same either way: neither the secret nor the code.

What the agent knows

A login without tokens.totp has no stored two-factor secret. A _totp token that fails with “no matching record on file” means exactly that.

Supported factors

Submit promptly. The code is valid for the current window only. If a site requires an unsupported second factor, the sign-in cannot complete unattended, and the agent should say so rather than retrying.
Holding a totp token does not make an unrelated prompt the agent’s to answer. Only the authenticator step of a sign-in the agent just performed, on that login, is. An SMS code, an email code or a payment verification at checkout belongs to the account holder. A code spent on the wrong prompt is wasted and can trip the site’s retry limits.

URL locking applies

A TOTP token is a login token. It resolves only on the login’s registered website or an extraWebsites entry, exactly like the password. See URL locking.

Enrolment by an agent

There is exactly one path on which a secret moves, and it is proxy to backend, never backend to proxy. When an agent sets up an authenticator on a site, the site displays an otpauth:// URI on a page the proxy is already rendering. The proxy reads it there and hands it to the backend to encrypt:
The secret is already on a screen the proxy controls, so reading it there discloses nothing the proxy did not already have. The agent never receives it.

Conditions for enrolment

All of these must hold. Three are checked before the page is read and fail with their own code: a non-loopback page not on HTTPS (insecure_page), a session that has filled no token yet, and a session that has not signed in as that login (both sensitive_data_blocked). Any other failure returns one hint_unresolved, without saying which condition failed.
The proxy refuses to read a setup secret in a session it was not already protecting, with sensitive_data_blocked. Until a token has been filled, screenshots are not blacked out, so the agent could otherwise see the secret. What counts is that login’s password (or its two-factor code) landing in a field. Filling its username does not.
Two accounts on one site share a host, so nothing else could tell them apart, and a secret stored against the wrong one cannot be moved. The holder enrols from the dashboard instead, where they can choose the login.
Exactly one match, so the backend never chooses between a secret and a recovery key beside it. Visible, so reveal the “can’t scan the QR code?” option first: a hidden element can hold a stale key. Top-level rather than in an iframe, because the secret is authorized against the address of the page. HTTPS (or a loopback http:// page, for a local demo), or the read fails with insecure_page. A QR code image or a <canvas> cannot be read; href is the only attribute this hint reads.
A secret is stored only on a login that has none, and only on a site the login is registered for.

Confirm the setup on the site

Most setup pages then ask for a code. Fetch the catalog again: the totp token is now present. Do not skip this step. An unconfirmed setup leaves the site without two-factor protection while Agent Vault holds a secret it will not overwrite, which the account holder then has to resolve by hand.

When the result is uncertain

hint_uncertain means the write reached the backend but the answer was lost, so whether it stored is unknown. Do not retry: enrolment refuses to overwrite, so a retry reports a second failure. Ask the account holder to check the login on their Logins page.

The account holder is told, on a best-effort basis

After an agent enrolment, Agent Vault tries to send a totp_enrolled notification. Enrolment is the only path that stores a credential the account holder did not type, so the notification is meant to reach them every time. The notification is best effort. The secret is stored first, and a failure to create the notification does not undo it. So a stored secret can exist with no Activity entry, email or push about it. The Logins page is the record of what is stored.

What this does not protect against

  • A compromised dashboard account. The holder’s own view of their logins can add, change or remove a stored secret.
  • Exposure inside Agent Vault. The backend decrypts the secret to derive each code, so Agent Vault’s backend is inside the trust boundary.
  • The current code at the moment of the fill. It is typed into the site’s page and is valid until the window ends.
  • Unsupported factors. SMS, email codes and passkeys remain with the account holder.

How to verify

1

Add a test login with an authenticator secret

On the Logins page, add a login for a test account and import its otpauth:// URI.
2

Read the catalog

Call get_token_catalog. The login shows hasTotp: true and a tokens.totp string. No secret and no code appear anywhere in the response.
3

Fill the token off-site

Fill the totp token on a page that is not the login’s site. The fill fails with login_blocked.
4

Fill it on the site

Fill it on the site’s authenticator step. The sign-in completes, and get_run_report records a TOTP fill by kind, with no value.

Signing in

The agent-side guide to sign-in flows.

Page hints

The hint mechanism enrolment uses.