No passwords-to-servers: the three layers, recovery walkthrough, phone bridge · content/guides/login-and-accounts.md
Login & Accounts — how signing in works here
The founder's ask, answered honestly: no passwords-to-servers, no email resets — so how do you get in, and how do you get back in when the laptop dies?
✓ verified against the live stack, 2026-09-12 — recovery walked end-to-end on a real identity (gsg): init --from-seed → backup → recover --force → byte-identical keys, same fingerprint GKA5-JSWK-ZE, same loopd agent ag_6484a71832, balance and cap intact. Passkey gate per wallet-app v7 (2026-09-11).
The one idea
Nothing you type into a website is your account. Your account is 12 words that never leave you.
There is no "password held on a server" to leak, no "reset link to your email" to intercept. Instead there are three layers, each answering one question:
| Layer | What it is | The question it answers | If it leaks/looks |
|---|---|---|---|
| Seed phrase (12 or 24 words) | The root key. Grows every other key by fixed math (seedkit, qal-derivation v1 — frozen label, BIP-39 test vectors verified). | "Who am I, forever." | Anyone with the words is you. Never type them into anything but your own wallet. |
| Passcode / passkey | App password → AES-256-GCM vault on the device (PBKDF2×200k); passkey = fingerprint/PIN via the platform. | "May this device open the wallet now." | Stolen laptop with vault locked → thief has ciphertext. 5 wrong tries → 60 s lockout. Auto-lock 5 min. |
| Per-agent keys | Separate money credentials per app/bot, each with a hard daily cap the ledger enforces (not software asking nicely), revocable in ~2 seconds. | "What may this bot do today (and no more)." | A stolen agent key spends up to today's cap, then hits the wall. Revoke and replace. |
That is the whole security model. Everything else is detail.
Accounts per device
Identities are per device by design — a second computer gets its own fresh keys; messaging is handle→handle across relays. Two consequences, both deliberate:
- Losing one machine does not touch the others.
- A "new" device is either fresh (new identity) or restored (your 12 words re-grow the same keys, byte for byte — same person, new hardware).
# the second-computer pattern from the test kit (content/testkit.md):
python3 qalchat/qalchat.py init alice-2nd --relays http://100.111.199.12:8840
Recovery walkthrough — device lost, 12 words, everything returns
This is the exact sequence run on 2026-09-12 (handle gsg). Your wallet does the same with the "Restore from seed" onboarding option.
1. New device, install, choose Restore from seed, type the words (or the CLI):
python3 qalchat/qalchat.py recover gsg "sibling tumble distance frost tooth flat …"
If an old copy of the identity exists on that machine, the command refuses until you add --force — and it archives the old files first (nothing is ever deleted):
error: identity 'gsg' already exists — recovering over it needs --force
(the old files are archived, never deleted)
-- force run:
prior device state archived → qalchat/data/archive/recover_1789199266438
identity 'gsg' RECOVERED from seed (qal-derivation-v1, fingerprint 3224a1f378d9a453)
— keys are byte-identical to the ones this phrase originally minted
2. Money comes back automatically. Your loopd (ledger) agent secret is also a leaf of the same seed, so recovery re-presents the same credential:
loopd agent ag_6484a71832 re-bound by handle (already — balance and cap travel with the agent)
Same agent id, same balance, same daily cap. A different secret for a claimed handle is refused (409) — impostors fail loudly, proven in the seedkit money test.
3. Conversations re-sync from the relay's audit trail — every replayed envelope is signature-checked and decryption-verified; any doubt aborts that conversation (fail-closed, never fail-open). Honest scope: the seed restores keys, not device-local message history — past sealed traffic stays sealed exactly as before.
4. You're you again. Same fingerprint, same qoll-code (GKA5-JSWK-ZE → qoll-gka5-js), same identity everywhere.
Recovery for agents and devices is even easier — they're never root. Every agent and device names a controller (a human/org DID); controllers re-issue or revoke them at will. python3 qalid/qalid.py recover-plan <did> prints the exact commands for your type. The only unrecoverable loss is a human's seed phrase with no backup — that is why step 2 of getting started is pen and paper.
The phone bridge token model
The phone (qalbridge) does not hold your keys at all. It is a token-gated window onto a bridge handle that lives on your machine:
- You open
http://<tailscale-ip>:8837/phone?t=<token>once; the server mints an HttpOnly session cookie and strips the token from the URL — then you bookmark the clean page. - The URL only resolves inside your tailnet (WireGuard VPN) — unreachable from the public internet by construction.
- Scope: read transcripts, pull whitelisted files, see balance, send as the bridge handle. No seed on the phone, nothing to lose with the phone.
- Treat the token like a password: anyone with it and tailnet access reads the bridge handle's conversations.
Plain-language security table
| Worry | What actually happens |
|---|---|
| "The server got hacked, is my password in there?" | There is no password on any server. Keys live encrypted on your device; relays carry sealed envelopes they can't read. |
| "I forgot my passcode." | Enter your 12 words → keys re-grow → set a new passcode. The passcode was never the identity. |
| "Someone stole my laptop." | Vault locked = ciphertext; 5 tries = lockout. Restore on new hardware from words. Revoke any agents that device ran. |
| "An AI agent went rogue." | It was on a capped per-agent key — the ledger refuses past the cap (HTTP 402), and revoke cuts it off in ~2 seconds. |
| "The app company disappears." | Your words still grow the same keys. Open-source CLI in this repo reads the same identity files. |
| "I lost the paper with the words." | Nothing can be done — by design, no back door exists. Make two offline backups; consider a passphrase (the optional 25th word: different passphrase = entirely different keys; never store it with the words). |
Where to see it for real
bash seedkit/test_seed.sh # 39 assertions: seed → use → destroy device → recover → use again
bash qalchat/test_chat.sh # 38 assertions stay green after recovery
python3 qalid/qalid.py recover-plan <handle> # your exact recovery story
Companions: getting-started · digital-identity-login · identity architecture (the master map) · secure ops (passcode doctrine).