Chat
Chat is the browser client, served by the q15-web container. It authenticates exactly one owner with
WebAuthn, keeps the passkeys and sessions in its own store rather than in the agent’s memory, and keeps
no transcript of its own: what you read in the browser is the agent’s transcript, read back to you.
What it serves
Section titled “What it serves”Chat is the app you look at: streaming replies that arrive as they are written, the tool activity behind them, paged history, and attachments in both directions. The route list is in the reference.
On your phone
Section titled “On your phone”The app is a web app, so install it from the browser’s menu and it behaves like one you installed yourself: full screen, no browser chrome, and a passkey tap instead of a login.
On Android it also registers as a share target. A scan from a document scanner, or a photo from the gallery, can be shared straight into q15 and arrives in the composer’s attachment tray, already sealed and ready to send. Chrome is the browser that supports it today, and the app has to be installed, not merely open in a tab.
Enrolling, signing in, and revoking
Section titled “Enrolling, signing in, and revoking”Enrollment is a host operation, including for the first device. The commands use podman compose;
on Docker they are the same with docker compose.
podman compose exec q15-web /usr/local/bin/q15-web auth enroll 'Laptop'podman compose exec q15-web /usr/local/bin/q15-web auth listpodman compose exec q15-web /usr/local/bin/q15-web auth revoke DEVICE_IDThe enroll command prints an enrollment blob and waits. You open the locked page in a browser, paste
the blob into its enrollment panel, create the device credential, and paste the one-line response
back into the waiting host command within five minutes. Only the host command submits the
registration, over a private Unix socket, so an origin that can run a browser ceremony still cannot
enroll itself. Enrollment can be run over SSH from the device you are enrolling.
Two constraints matter:
- Every credential requires user verification — a PIN or a biometric check.
- Synced and backup-eligible credentials are refused, because revoking a key that several devices share cannot revoke one physical device. A device-bound platform authenticator or a security key with a PIN is what works.
There is no recovery password. Host access is the recovery path: enroll a spare authenticator in advance, and if one is lost, enroll a replacement and revoke the lost device.
Sessions
Section titled “Sessions”A session cookie alone grants nothing. Each HTTP request and each socket upgrade must carry a fresh proof signed by a session key that lives in the browser and is not exportable, so a copied cookie is not enough to impersonate a device. Reloads reuse the session key without another passkey gesture.
Sessions expire server-side after 30 days, with no sliding renewal; signing out deletes the session server-side. Revoking a device deletes its credential and all of its sessions at once, and other devices are unaffected. The hard limits are 32 devices, 128 sessions and 64 pending login challenges. A login ceremony expires after two minutes and an enrollment ceremony after five.
Content sealing
Section titled “Content sealing”Everything that carries content is sealed between your browser and the agent: chat bodies, reasoning,
tool arguments and results, live deltas and replayed history. A passive carrier gets ciphertext and
frame sizes, and q15-web itself holds no key and parses nothing. The algorithms, the key derivation
and the one cap that applies to attachments are in the reference.
State and volumes
Section titled “State and volumes”The web tier keeps exactly one private directory, q15_web_state mounted at /var/lib/q15-web: the
0600 credential store and a private admin socket. It holds no transcript. Keep it across updates and
rebuilds, because deleting it means enrolling every device again, and read the restore warning in
Backups before reusing an old copy. The bridge socket it dials lives on a second volume,
mounted in the agent and the web tier only. Both are in the
volume table.
What it does not protect
Section titled “What it does not protect”These are the shipped tier’s own stated limits, and they are worth reading before you point a domain at it:
- A TLS terminator sees cookies and routing metadata. Sealing hides content from a passive carrier. It does not hide who is talking, or when, or how much.
- It is not end-to-end encryption from the edge. An origin or relay that serves modified JavaScript, or that replaces the key exchange, is outside the guarantee. Authenticating the code your browser runs would need a separate mechanism.
- Host administrators and the web process itself are trusted. Access to the admin socket or a writable state directory can enroll an identity.
- A compromised browser, operating system or authenticator is out of scope.
- It bounds work and state, not availability. The authentication budget limits how much work a client can cause; it is not a promise that the service stays up under a distributed attack.
See Security for how this fits into the rest of the architecture.