# Phase 5 Runbook (Session Reuse Prototype) This runbook starts a minimal `k_server` + `k_proxy` prototype for session reuse testing. Last updated: 2026-04-26 Related browser demo: - `k_client_portal.py` can now be used in `k_client` at `http://127.0.0.1:8766` to show: - registration - current registered-user list from `k_proxy` - unregister from the browser page - login with card approval/denial - protected `k_server` counter access - logout - explicit "k_server was not called" behavior when login is denied ## 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 ``` For the browser-facing `k_client` page, use the Playwright regression spec: ```bash npm install npx playwright install npm run test:k-client ``` Notes: - default target is `http://127.0.0.1:8766` - override with `PORTAL_BASE_URL=http://127.0.0.1:8766` - the spec expects manual card confirmation during register and login - timeouts can be tuned with `CARD_REGISTRATION_TIMEOUT_MS` and `CARD_LOGIN_TIMEOUT_MS` - from this host, a forwarded portal URL was used successfully: - `PORTAL_BASE_URL=http://127.0.0.1:18766 npm run test: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`. - The Playwright browser regression for `k_client_portal.py` also passed end-to-end: - register - login - protected counter - logout - unregister ## Current Limitation - The stable deployed baseline still uses card-presence probing, not full assertion verification, for the default auth gate. - Session and counter state are still process-local only; restart loses state. - Upstream trust still relies on a shared static `X-Proxy-Token`. - Experimental direct FIDO2 mode exists in `k_proxy_app.py` behind `--auth-mode fido2-direct`: - direct `/enroll/register` now succeeds - direct `/session/login` now succeeds and returns `auth_mode: "fido2_assertion"` - direct `/session/status`, `/resource/counter`, and `/session/logout` also succeed end-to-end - the mode remains optional for now; the deployed service was returned to default `probe` mode so the validated Phase 5 baseline stays reproducible - Raw CTAP debugging helper 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 - `phase5_chain_regression.sh` now supports card-interactive direct auth via: - `--interactive-card` - `--expect-auth-mode fido2_assertion` ## Current Focus - Keep the HTTPS split-VM chain reproducible in default `probe` mode. - Decide whether `fido2-direct` is ready to become the default deployed auth path. - Continue Phase 6.5 concurrency work; the active system limit is still higher-fan-out Qubes forwarding on the browser-facing path rather than basic Phase 5 functionality.