Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
Keep saved logins per Adom account (Adom Bridge 2.1.186 gives every call its account)
pup-bridge 2.0.738 (Node public tier and Rust insiders tier) · Adom Bridge 2.1.186, not shipped yet: Windows https://github.com/adom-inc/adom-bridge/pull/15 · macOS https://github.com/adom-inc/bridge-macos/pull/2
What is changing in Adom Bridge
Adom Bridge 2.1.186 makes the Adom account the unit of access. One machine can be signed in to several accounts at once (a work and a personal one). Each account brings its own containers, machines and mesh, and nothing crosses between them. Signing an account out removes exactly what it brought. A "shared or public computer" sign-in (a technician station, a church office PC) signs itself out after 8 hours and deletes that account's data on sign-out.
pup's vault is the one place this does not reach yet. Saved logins and cards are per OS user, so:
- Across accounts on one machine: a work container's AI can sign in with logins saved for the personal account, and the reverse.
- On a shared computer: the next person to sign in inherits every login the last person imported or captured.
What ab now gives a bridge
X-Adom-Verified-Accounton every verb ab dispatches to a bridge. It is stamped by ab from the transport, never by the caller:- an account id: the relay connection or mesh link the call arrived on belongs to that account;
local: the person at this machine (local CLI, Hydrogen, a local AI);none: a connection that belongs to no account (saved before 2.1.186, or a self-hosted relay);- absent: an Adom Bridge older than 2.1.186.
When the bridge calls back into ab while doing that work (vault sync to a peer, file moves), echo the header. ab trusts it from a token-bearing bridge and keeps the peer call inside that account; without it, the call acts as
local.accountSignedOutinbridge.json: ab calls the verb you name when an account signs out of the machine, with{cascade:true, name, account:{id, handle}, wipe, dataDir}. It runs after the account's connections and session are gone, so no new call for that account can arrive while you lock.wipe:false(a personal computer): lock, keeping the data for the next sign-in.wipe:true(a shared computer, or the person chose "remove my data"): delete it. Same contract asuninstall: idempotent, non-interactive, fast.GET /accountson the loopback API: the signed-in accounts (id,handle,default,signedIn,mode) and each one'sdataDir(~/.adom/accounts/<id>/). It refuses requests that carry anOriginheader.
A design that moves no data
The existing vault belongs to whichever account was signed in when the machine upgraded, because before 2.1.186 a machine held one session. So:
- On the first account-aware start, read
GET /accountsand record the default account as the owner of the existing vault:pup-vault-owner.json {account: <id>, at}besidepup-credentials.index.json. That account keeps using the current index and secret service unchanged, so nothing is copied or re-keyed and a failed migration cannot lose anything. - Every other account gets its own vault: the index under that account's
dataDir(or<state>/accounts/<id>/), and secrets under a per-account service, for exampleadom-bridge-pup@<id>(cards:adom-pup-cards@<id>). - Which vault serves a call:
- an account id: that account's vault, provided
GET /accountslists it as signed in (cache about 10 s; re-read on an unknown id); local: the default account's vault;none: no vault, refused with a hint (the connection belongs to no account, so there is no vault it may use);- absent (older ab): today's single vault, unchanged.
- an account id: that account's vault, provided
- Event-driven paths have no caller (login capture on submit, autofill on load, HTTP Basic auth). Record the account on the pup session when a verb opens it, and use the session's account there. This is the part that touches the most code.
pup_vault_sync/pup_vault_export/pup_vault_importact on the request's account vault, and echo the header on the egress so ab only reaches that account's machines.accountSignedOutwithwipe:false: drop any in-memory secrets for that account (the Windows store'sCACHE). The signed-in check above then refuses it until it signs back in. Withwipe:true: delete that account's index, secrets and cards. If the account owns the legacy vault, delete that too.
One hazard in the Windows store (Rust host-win)
secrets::load(h, service) runs its one-time migrations with the service of whichever call fills the global CACHE first. It renames adom-desktop-pup/* keys to {service}/*, and moves Credential Manager entries named by the base index into {service}/<acct>. If the first call is a per-account service, or adom-pup-cards today, legacy logins land under the wrong service. Run those migrations with the fixed base service (adom-bridge-pup), whatever service the call asked for.
Tests worth having
- Two accounts on one machine: a login saved by a call stamped A is invisible to a call stamped B, and the reverse.
localreads the default account's vault;noneis refused; no header behaves exactly like 2.0.737.- The first account-aware start records the owner and moves nothing; the owner sees every existing login.
accountSignedOut {wipe:true}for a non-owner deletes only that account's vault; for the owner it deletes the legacy vault.- A login captured in a window opened by an A-stamped call is saved into A's vault.
The ab side is in the two PRs above and documented in ab's skills/BRIDGE_SDK_MANIFEST.md (accountSignedOut) and skills/BRIDGE_SDK_CALLBACK.md (X-Adom-Verified-Account). Nothing changes for pup until it opts in: with no accountSignedOut declared, ab simply does not call it.
0 Replies
Log in to reply.