An agent that signs in for you holds the most dangerous thing you own: the keys to your accounts. On 8 September 2026, Meta published how they secured Muse, their personal agent. It is the clearest public design we have seen for AI agent password security, and we rebuilt the Unbrowse vault to follow it. This post walks through what Meta described, what we changed, and what we did not copy and why.
What Meta described
Muse runs each user's agent in its own cloud VM, and splits that VM into two domains. The agent lives in a locked-down container. Around it sit services the agent cannot touch: a credential store, workers that run connector code with narrow privileges, safety classifiers, and one component called Sentinel.
Four ideas carry the design.
The model never sees a real credential. Inside the agent's container, secrets are replaced with surrogate tokens. After a network request is authorised, Sentinel swaps the surrogate for the real credential at the network boundary.
One permission authority. Sentinel is the only thing that decides whether a connector action or outbound request goes ahead. It has three answers: allow, deny, or ask the user.
Approvals are capabilities. Meta's phrase is that approvals are "strict capabilities, not conversational suggestions". The user can grant one-time, session, task, time-bounded or permanent permission. Read-only, previously allowed or low-risk actions go through without a prompt, so the user is not buried in questions.
Bound the damage. Meta is plain that prompt injection is unsolved. They cite Simon Willison's "lethal trifecta" (private data, untrusted content, a way to send data out) and stack defences: model training, labelling outside content as untrusted, classifier ensembles, and human approval for anything that could leak data. The goal is not immunity. It is limiting what a fooled agent can do.
What the Unbrowse vault already did
Unbrowse compiles websites into tools agents call. Many of those tools need you to be signed in, so Unbrowse has had a password manager since September: you save a login (username, email, password, optional 2FA seed), and your agent signs in without seeing it.
Some of Muse's model was already there:
- Surrogates. Agents get a
vault://reference and a masked hint (oc•••at). Page snapshots show[from vault]where a value was typed. Learned tools carry a placeholder where a password goes; the real value is placed into the request as it leaves. - Insertion at the boundary. A value is released only through a single-use lease that names the page it is for. The lease expires within a minute, and the page must match the login's own site (or a subdomain) both when the lease is taken and when it is redeemed. A lookalike domain never matches.
- Sealed storage. AES-256-GCM, a data key per workspace wrapped by a master key, each record bound to its workspace and id.
- Scrubbed traces. The recorder removes typed secrets from traces and evidence, and chat redacts passwords typed as free text.
What was missing was the part Meta puts at the centre: a gate. Before this change, any agent in your workspace could use any saved login, any time, unattended. The lease checked where a value could go. Nothing checked whether you wanted it used.
What we built: Sentinel for the vault
We added one module, sentinel.ts, and made the vault call it before it issues any lease for an agent. It is the only path: filling a login form in the cloud browser, signing in so a learned tool can replay, and reusing a kept signed-in session all pass through the same check.
agent ── unbrowse.browse.act { action: "autofill" } ──▶ engine
│
vault.lease(ref, page, operation)
│
Sentinel.decide
┌──────────────┼──────────────┐
allow deny ask
│ │ │
single-use lease approval_denied approval_required
value → the page (no value) + one-time link
Each site has a standing rule you set in the password manager: Ask me, Always allow, or Never. On an Ask-me site, a use needs a live approval. When there is none, the call stops with approval_required and a one-time link. You open it, see the site, what the agent wants to do and the task it gave, and choose how long:
| Scope | What it covers |
|---|---|
| Just this sign-in | A few minutes: one login form, or one sign-in for a learned tool |
| This browser session | Every fill in the cloud-browser session that asked |
| For 1 hour / For 24 hours | Any agent use on that site until it expires |
| Always for this site | Sets the site's rule to Always allow |
The agent waits with unbrowse.credentials.status and retries. MCP clients that support URL elicitation open the link for you. The agent never gets anything but the link.
Like Meta's approvals, ours are enforced by the server, not by the agent. An approval is a record bound to a site and a scope, checked on every lease. Text in a web page can change what the agent asks for. It cannot change what Sentinel allows.
Three decisions follow Meta's point about not burying the user:
- Saving a login is consent in context. When an agent asks you to save a login, the same page asks how long it may use it (24 hours is preselected). One click, not two.
- Existing logins keep working. Workspaces with logins saved before Sentinel shipped start with those sites at Always allow, so running agents do not break. New logins default to Ask me.
- Org end users default to allow. An agent builder's end users have no Unbrowse sign-in to approve from, so their logins follow the org's decision instead.
Everything Sentinel decides is shown to you. The password manager lists each decision (allowed, refused, asked you, and why), each live approval with a revoke button, and each fill, reveal and change. Revoking takes effect on the next use.
What we did not copy, and why
We would rather say this plainly than let the headline imply more.
- No per-user VM. Muse gives each user a VM and runs the agent in a container inside it. Unbrowse runs shared containers; workspace isolation is enforced in code and by per-workspace keys, not by the kernel.
- Not zero-knowledge. Our server can decrypt your logins; that is what lets agents sign in while you are away. Meta keeps credentials in a separate service inside the user's VM and is testing a confidential VM that would stop even Meta from reading it. We have neither.
- No classifier ensemble. Muse runs several prompt-injection classifiers over agent traffic. We do not. Our defence is structural: the model holds no secret to leak, a login opens only for its own site, and sign-ins on an Ask-me site need you.
- No kernel-level taint tracking. Muse tracks at the kernel whether a process has read user data and drops auto-allow for tainted requests. Our Sentinel gates credential use, not all outbound traffic.
- No task scope. Meta lists task-scoped approvals. Our closest equivalent is the browser-session scope.
- Email one-time codes are not filtered yet. Muse strips reset links, magic links and one-time codes from email before the agent sees them. Unbrowse can read pages you are signed in to, including mail. This is next on our list.
Try it
Connect Unbrowse to your agent (setup is in the docs), then open the password manager at /app/vault. Set a site to Ask me and ask your agent to do something there. You will get the approval link instead of a silent sign-in.
The short version of all of this, in question-and-answer form, is on our security FAQ. For how learned tools work end to end, see how Unbrowse works.