When the site has no API, the signed-in session is the credential
Vault
An agent that books restaurants, checks a utility bill, or messages someone on a professional network is mostly working on sites that offer no API and never will. The only way in is a browser that is signed in as the user. Which means the thing your platform has to capture, store, keep alive, and hand to the agent is not a token but a session: cookies, local storage, the state a browser accumulates after someone types their password.
Most platforms discover that a session is a credential the hard way — when one shows up in a log, or a support engineer realises the "session blob" column restores into a working Gmail. Seekrit Vault starts from that fact and gives a browser session the same treatment as an OAuth grant: encrypted on capture to a key only your executor holds, never in your database, never in your agent's context, applied at the moment of use.
How does a session get captured without your backend seeing it?
The person opens the connect link on their phone, taps Allow, and taps Open sign-in. Your executor — the Worker in your Cloudflare account — opens a Browser Run session in that account, fenced to the site's domains, pointed at the site's login page, and hands the person a Live View link. They sign in with their own hands: their password, their MFA prompt, their passkey, whatever the site asks. Nothing about the sign-in is automated, and nothing about it passes through seekrit or your backend.
When they come back and tap I'm signed in, the executor checks the browser
has left the sign-in page, captures its cookies and local storage, encrypts
the result to its own key, and files the ciphertext with seekrit as the
connection's session slot. Optionally the person also saves a username and
password — encrypted on the Connect page before it leaves the device — so the
agent can sign in again when the session eventually expires.
The site is declared in your executor's config, and the declaration is data:
import { siteProfile } from "@seekrit/vault-executor";
siteProfile({
id: "linkedin",
displayName: "LinkedIn",
consentSummary: "browse and message on LinkedIn as you",
startUrl: "https://www.linkedin.com/feed/",
loginUrl: "https://www.linkedin.com/login",
domains: ["linkedin.com", "*.linkedin.com", "*.licdn.com"],
domainSets: ["common-cdns"],
signedOutUrlPrefixes: ["https://www.linkedin.com/login"],
});
domains becomes the Browser Run guardrail on every browser the executor
opens for this site. A bad profile fails at wrangler deploy, not during a
user's sign-in.
How does the agent use it?
It asks for a browser, not for cookies:
const lease = await vault.browser.open({ userRef: "u_123", connectionId });
// lease.sessionId is a Browser Run session in your account, already signed in.
// Connect your own browser tools to it and do the work.
await vault.browser.release({ userRef: "u_123", leaseId: lease.leaseId });
open takes a lease on the connection — one at a time, 409 lease_held
otherwise — restores the session into a fresh browser fenced to the site's
domains, navigates to the start URL, and returns the session id and whether it
landed signed in. Your agent's browser tooling connects to that session and
drives it exactly as it would any other. The cookies went from seekrit's
ciphertext, through the executor's memory, into the browser. They did not go
through your backend, and they are not in the agent's context.
release captures the rotated cookies, writes them back to seekrit as the
next revision, and closes the browser. If the agent never calls release, an
alarm does the same at the lease's end — fifteen minutes by default, and a
site profile can only lower it. The write-back is compare-and-swap: a
revocation or a reconnect that happened during the lease wins, so a connection
the user disconnected mid-run cannot be resurrected by the run's own save.
What happens when the session expires?
Sessions die. The site logs the user out after a week, or a security setting changes, and the restored browser lands on the login page. Three answers, in escalating order of involving the person.
fill. If the person saved a login, the agent navigates to the sign-in
form, focuses a field, and asks the executor to type it:
await vault.browser.fill({ userRef: "u_123", leaseId, field: "username" });
await vault.browser.fill({ userRef: "u_123", leaseId, field: "password" });
await vault.browser.fill({ userRef: "u_123", leaseId, field: "totp" });
The executor applies the password-manager rule before typing anything: the
focused element must be an <input>, on one of the site's declared login
origins, and the right kind of field — a password goes only into a password
field. totp generates the current code from the saved authenticator secret.
The value is typed into the page and the call returns { filled: true }; the
agent never receives it.
reauth. If the site wants something a machine cannot do — a push
approval, a passkey, a challenge — the agent asks for a Live View link, you
text it to the person, and they finish the sign-in themselves. A release
that then finds the browser signed in reactivates the connection.
Reconnect. A release that ends on a sign-in page marks the connection
reauth_required with reason session_signed_out, and a reconnect link
captures a fresh session through the same flow as the first time.
What does the fence actually prevent?
The browser the agent drives can reach only the site's domains, so a
prompt-injected agent cannot navigate the signed-in browser to a page it
controls. A saved password is typed only into a field on the site's login
origins, so it cannot be entered into a look-alike form. The rotated session
is written back only if the connection is still active. And a Live View URL,
which grants control of a page, is returned only to the person's own Connect
page during capture or to you to forward to that person during reauth —
never logged, never in audit metadata. The session stays out of your database
and your agent's context, which are the places sessions leak from.
What Vault does not do here
It does not host browsers. The sessions are Browser Run sessions in your
Cloudflare account, bound to your executor, paid for on your plan. Vault loads
a credential into one and saves it back. It does not stream the browser to
anyone, does not sell browser-hours, and does not ship a read_page tool —
your agent uses its own tools, and Vault makes them signed in. A site with an
OAuth API is better served through OAuth;
browser sessions are for the ones with no alternative.
Why this is worth getting right
A platform that stores session blobs holds something strictly more dangerous than a password hash: a password hash has to be cracked, a session blob just has to be restored. It is also the credential your users least understand they are handing over. The Connect page tells them plainly that neither you nor seekrit can read what they just captured, and the executor's key is what makes that true.
The guide has the full lifecycle,
reauth_required reasons, and the lease API; the trust
model has what the browser path guarantees.