# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Repository policy - `CR_SDK_CK-main/` is the firmware SDK source tree. Treat it as **read-only**. Do not add or edit files there. - All host-side scripts live at the workspace root (`/home/user/chromecard`). - `Setup.md` and `Workplan.md` are the canonical living docs. After each meaningful execution step, update `Setup.md` for environment/runtime state and `Workplan.md` for phase progress and next blocking action. ## Running tests ```bash # Python unit tests (no VMs or card required, 122 tests) python3 -m unittest tests/test_k_proxy.py # Playwright browser regression (requires services running and forwarded portal) PORTAL_BASE_URL=http://127.0.0.1:18766 npm run test:k-client # Split-VM end-to-end regression helper (runs from host via SSH into k_client) ./phase5_chain_regression.sh ./phase5_chain_regression.sh --interactive-card --expect-auth-mode fido2_assertion ``` ## Probing the card (on k_proxy) ```bash python3 /home/user/chromecard/fido2_probe.py --list python3 /home/user/chromecard/fido2_probe.py --json python3 /home/user/chromecard/raw_ctap_probe.py info python3 /home/user/chromecard/raw_ctap_probe.py make-credential --rp-id localhost python3 /home/user/chromecard/webauthn_local_demo.py # opens http://localhost:8765 ``` ## Generating TLS certificates ```bash python3 generate_phase2_certs.py # Writes tls/phase2/ca.crt, k_proxy.crt/.key, k_server.crt/.key ``` ## Starting services (split-VM shape) **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 ``` **k_proxy VM:** ```bash qvm-connect-tcp 9780:k_server:8780 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 # Add --auth-mode fido2-direct for real CTAP2 (default is probe mode) ``` **k_client VM:** ```bash qvm-connect-tcp 9771:k_proxy:8771 python3 /home/user/chromecard/k_client_portal.py \ --proxy-base-url https://127.0.0.1:9771 # Browser demo page at http://127.0.0.1:8766 ``` Files are deployed to VMs via `scp :~` and run via `ssh `. ## Architecture **Qubes 3-VM topology:** `k_client` → `k_proxy` → `k_server`, each a Debian 13 AppVM. Inter-VM transport uses `qvm-connect-tcp` localhost forwarding (not raw VM-IP routing). Validated chain: - `k_client localhost:9771` → `k_proxy:8771` - `k_proxy localhost:9780` → `k_server:8780` **k_proxy_app.py** — session gateway and FIDO2 auth bridge. Two auth modes: - `probe` (default): validates card presence by subprocess-calling `fido2_probe.py --json` - `fido2-direct`: performs real CTAP2 `makeCredential`/`getAssertion` against the physical card via `python-fido2`; auto-detects the FIDO hidraw device `ProxyState` holds all server-side state: in-memory session store (guarded by one lock), enrollment DB (JSON file), and an `UpstreamPool` of persistent TLS connections to k_server. Sessions are lost on restart. **k_server_app.py** — protected resource backend. Exposes a monotonic counter behind `X-Proxy-Token` auth. Counter state is in-memory only; resets on restart. Lock guards counter increments. **k_client_portal.py** — thin browser-facing portal in k_client. Delegates all auth and resource calls to k_proxy. Holds only a local preferred username; enrollment and session state live in k_proxy. **FIDO2 transport:** Card communicates over USB HID (CTAPHID) on `/dev/hidraw0` (FIDO interface, usage page `0xF1D0`). `/dev/hidraw1` is a separate vendor HID interface. If the card re-enumerates, k_proxy auto-detects the correct node. If CTAPHID stops responding, a full USB power cycle is the recovery path. **CardEmulator** (`tests/card_emulator.py`) — software emulator of the card for unit tests. Implements `make_credential`/`get_assertion` with real P-256 crypto; `user_confirms=False` simulates card rejection. Wire it into tests by patching `_with_direct_ctap2` and `_drop_direct_device` on `ProxyState`. See the module docstring for the exact patch pattern. **Key enrollment endpoints on k_proxy:** `POST /enroll/register`, `GET /enroll/status`, `POST /enroll/update`, `POST /enroll/delete`, `GET /enroll/list`. Usernames are normalized to lowercase, 3–32 chars `[a-z0-9._-]`. **Key session endpoints on k_proxy:** `POST /session/login`, `POST /session/status`, `POST /session/logout`, `POST /resource/counter`. ## Known limits and blockers - Concurrency ceiling on the browser-facing forwarded path is ~10 in-flight requests; higher fan-out triggers Qubes vchan failures (`xs_transaction_start: No space left on device`). - If CTAPHID `INIT` packets get no reply after a card reattach, a full USB power cycle recovers the transport. - `CR_SDK_CK-main` is missing role directories (`mvp`, `setup`, `components`, `samples`) required for the firmware build/flash flow (`./scripts/build_flash_mvp.sh`). `west` and `nrfjprog` must also be installed. - Phases 7 (firmware build) and 9 (phone-wireless transport) are externally gated.