# Phase 5 Runbook (Session Reuse Prototype) This runbook starts a minimal `k_server` + `k_proxy` prototype for session reuse testing. Last updated: 2026-04-25 ## What This Prototype Covers - `k_proxy` creates short-lived sessions. - Session creation uses a card-presence check (`fido2_probe.py --json`) as the current auth gate. - Valid sessions can repeatedly access a protected `k_server` counter endpoint without re-running card auth each request. - Session status and logout/invalidation paths are implemented. ## Modes There are two useful ways to run this prototype: - Same-VM quickstart: `k_proxy` and `k_server` run on one VM for app-local testing. - Split-VM chain: `k_proxy` runs in `k_proxy`, `k_server` runs in `k_server`, and the Qubes forwarding layer must permit the chain. ## Start Services ### Same-VM quickstart This matches the code defaults and is useful for basic app behavior only. In the chosen VM: ```bash python3 /home/user/chromecard/k_server_app.py --host 127.0.0.1 --port 8780 --proxy-token dev-proxy-token ``` In the same VM: ```bash python3 /home/user/chromecard/k_proxy_app.py \ --host 127.0.0.1 \ --port 8770 \ --session-ttl 300 \ --server-base-url http://127.0.0.1:8780 \ --proxy-token dev-proxy-token ``` ### Split-VM chain This is the current Qubes target shape. In `k_server` VM: ```bash python3 /home/user/chromecard/k_server_app.py \ --host 127.0.0.1 \ --port 8780 \ --proxy-token dev-proxy-token \ --tls-certfile /home/user/chromecard/tls/phase2/k_server.crt \ --tls-keyfile /home/user/chromecard/tls/phase2/k_server.key ``` In `k_proxy` VM: ```bash qvm-connect-tcp 9780:k_server:8780 ``` Notes: ```bash python3 /home/user/chromecard/k_proxy_app.py \ --host 127.0.0.1 \ --port 8771 \ --session-ttl 300 \ --server-base-url https://127.0.0.1:9780 \ --server-ca-file /home/user/chromecard/tls/phase2/ca.crt \ --proxy-token dev-proxy-token \ --tls-certfile /home/user/chromecard/tls/phase2/k_proxy.crt \ --tls-keyfile /home/user/chromecard/tls/phase2/k_proxy.key ``` In `k_client` VM: ```bash qvm-connect-tcp 9771:k_proxy:8771 ``` Notes: - Current validated split-VM path is `k_client localhost:9771 -> k_proxy localhost:8771 -> k_proxy localhost:9780 forward -> k_server localhost:8780`. - Use `--cacert /home/user/chromecard/tls/phase2/ca.crt` for TLS verification in `curl`-based checks. - Raw VM-IP routing is not the validated path for the current prototype. ## Ownership And Concurrency - `k_proxy` is authoritative for session state. - `k_server` is authoritative for the protected counter state. - Sessions are in-memory only in `k_proxy` and are lost on proxy restart. - The protected counter is in-memory only in `k_server` and resets on server restart. - Both services use `ThreadingHTTPServer`. - `k_proxy` guards its session store with a single process-local lock. - `k_server` guards counter increments with a single process-local lock. - Qubes localhost forwarders are transport plumbing only; they are not a source of state authority. ## Test Flow Use the proxy port that matches the mode you started: - Same-VM quickstart: `8770` - Split-VM chain: `9771` from `k_client`, `8771` inside `k_proxy` Create a session (runs auth gate once): ```bash curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:/session/login \ -H 'Content-Type: application/json' \ -d '{"username":"alice"}' ``` Copy `session_token` from response, then: ```bash TOKEN='' ``` Check session: ```bash curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:/session/status \ -H "Authorization: Bearer $TOKEN" ``` Call protected resource multiple times (should not require new login): ```bash curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:/resource/counter \ -H "Authorization: Bearer $TOKEN" curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:/resource/counter \ -H "Authorization: Bearer $TOKEN" ``` Logout/invalidate: ```bash curl -sS --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:/session/logout \ -H "Authorization: Bearer $TOKEN" ``` Re-check after logout (should fail with 401): ```bash curl -i --cacert /home/user/chromecard/tls/phase2/ca.crt -X POST https://127.0.0.1:/resource/counter \ -H "Authorization: Bearer $TOKEN" ``` ## Regression Script For the split-VM chain, use the host-side regression helper: ```bash /home/user/chromecard/phase5_chain_regression.sh ``` Defaults: - Drives the test from `k_client` over SSH. - Uses `https://127.0.0.1:9771` and `/home/user/chromecard/tls/phase2/ca.crt` inside `k_client`. - Logs in as `alice`. - Runs `20` counter requests at parallelism `8`. - Verifies that returned counter values are unique and gap-free, then logs out and checks for `401` after logout. Useful overrides: ```bash REQUESTS=50 PARALLELISM=12 /home/user/chromecard/phase5_chain_regression.sh ``` ```bash /home/user/chromecard/phase5_chain_regression.sh --username alice --client-host k_client ``` Verified result on 2026-04-25: - Live split-VM chain passed end-to-end. - Login, session status, counter reuse, and logout all worked from `k_client`. - A `20` request / `8` worker concurrency burst returned unique, gap-free counter values `23..42`. ## Current Limitation - This uses card-presence probing, not a full WebAuthn assertion verification path. - Intended as a Phase 5 starter for session semantics and proxy/server behavior. - Session and counter state are currently process-local only; restart loses state. - Upstream trust still relies on a shared static `X-Proxy-Token`. - Experimental direct FIDO2 mode now exists in `k_proxy_app.py` behind `--auth-mode fido2-direct`, but it is not the default runtime: - direct registration on the current `k_proxy` card/library stack still fails with `No compatible PIN/UV protocols supported!` - a CTAP1 fallback probe did not complete quickly enough to promote as the working path - the deployed service was restored to default `probe` mode so the validated Phase 5 chain remains usable - Raw CTAP debugging helper now exists at `/home/user/chromecard/raw_ctap_probe.py`: - use it on `k_proxy` to exercise low-level `makeCredential` / `getAssertion` - it logs keepalive callbacks and transport exceptions - Current blocker before the next direct-auth attempt: - `k_proxy` currently has no visible `/dev/hidraw*` - `python3 /home/user/chromecard/fido2_probe.py --list` in `k_proxy` returns `No CTAP HID devices found.` - restore card visibility first, then retry the raw CTAP probe and stop to tell the user when to press `yes` or `no` - Latest retry after card reattach: - `/dev/hidraw0` and `/dev/hidraw1` are visible in `k_proxy` again - `/dev/hidraw0` opens successfully as the normal user, but `/dev/hidraw1` is still permission-denied - raw `makeCredential` still shows no card prompt, so the hang is before the firmware confirmation UI - next step is to identify which hidraw interface `python-fido2` is selecting