Some jobs only exist behind a login. A session profile is Obol’s answer: opt-in browser or device state for one customer-owned account, sealed in the vault, mounted into an isolated environment for a run, and revocable.
The routes exist; the door they open does not, yet. Since 2026-09-10 there are four shipped routes — capture, list, read and revoke — under /workspaces/{ws}/worker-session-profiles. Capture is not a login flow of its own: it seals state from an environment you are already holding through a live control lease on a live run. As of 2026-09-10 a run does not complete end to end (Overview), so there is no live run to hold, and capture is unreachable in practice even though the route answers.Read this page as the model, and read the list and revoke routes as usable the moment there is something to list.

The honest part first

An authenticated browser must possess usable session state inside its environment. Obol does not claim these sessions never enter the sandbox: the contract requires plaintext_present_in_environment: true, as a required field with a fixed value, precisely so that no surface can quietly imply otherwise. That is a different boundary from the one that governs model and vendor API credentials. Those keep gateway-only plaintext handling and reach no environment and no log. A session profile is the narrow, reviewed exception, and it is scoped to a single named account.

What is stored, and what cannot be

The stored state lives sealed behind sealed_state_ref. The contract carries metadata only, and it has no field that could hold a password, a cookie value, a token, or a bearer string. There is no export of the profile bytes. The only supported way to acquire one is user_assisted_login: a person authenticates in a private session and the resulting state is sealed. A password is never placed in a model prompt, and no Worker instruction should contain one — control screens free text for recognisable credential material and refuses the request rather than saving it.

The rules that follow from calling it a secret

The structural refusal is worth stating plainly: a code environment cannot mount a session profile at all. That is enforced in the data model, not by convention, so a Worker that asked for one on a code environment would be refused before it ran.

The routes, and what they do not certify

Two properties are worth knowing before you build against them. No session state crosses this boundary. sealed_state_ref is derived from the workspace and the profile id and is never accepted from a caller, so the capture body has no field a byte could travel in. Control records consent, custody metadata and the door check. It does not read the state back, and it does not certify the bytes. Revocation is a fence, not a flag. Read the response’s state, not its HTTP status. revoked means control observed every holder stop. revoking means the record is unallocatable, the address is destroyed and the fence has been applied — and at least one lease has not yet been observed to stop, because a bumped fencing token is not evidence that a holder halted. Calling revoke again is safe, and it is also how you ask whether it has landed. Four things ADR-0098 asks for are not here yet, and none of them is implied by the routes answering: a control lease’s purpose is not persisted, so capture verifies the door is open but not the sign on it; allocation does not re-read revocation state at the moment a lease is created; no scheduled sweep expires stale profiles, so expiry is enforced by the Doctor and on read; and no revocation signal reaches orchestration, which is what would turn revoking into revoked promptly rather than eventually.

What the Doctor tells you

GET /workers-doctor?worker_id=… checks every profile a Worker’s revision references, and an unhealthy one is a fail and never a warn:
A revoked or expired profile is a failure because a run that mounted one would be driving an authenticated browser whose session Obol has already said it destroyed.

Signing in as part of a run

Where a login has to happen inside a live run — a step nobody could pre-seed — the mechanism is a control lease with purpose: "assisted_login". A person takes the environment for a bounded window and authenticates themselves. That is the same door described in Take over a browser, and it is gated on CHANGE_APPLY for exactly this reason: it is the point at which a human types a password into an environment that then holds an authenticated session.

What a run inside a session proves

Less than it looks like. Being logged in changes what a Worker can reach; it changes nothing about what an observation is worth. A screenshot taken inside an authenticated session is an observation of a screen. What a receipt may claim about a call is decided by the route the call took, and the receipt names the class — see Evidence.