+## 🏗️ How It's Organized
-1. **Key Generation:** Both the host and the token generate a new, persistent ECDSA keypair.
-2. **Public Key Exchange:** The host and token exchange their public keys.
-3. **Golden Hash:** The host generates a "golden hash" of its boot file and shares it with the token. This hash represents the known-good state of the host's software.
+```
+FreeRTOS Tasks (The Chaos):
+├── Serial (Priority 26) - Your USB connection
+├── Watchdog (Priority 27) - Judges everything
+├── WiFi Background (Priority 25) - Talks to WiFi chip
+├── HTTP Server (Priority 5) - Handles web requests
+└── WiFi Init (Priority 5) - Starts the AP, then dips
+```
-### Phase 2: Mutual Attestation & Secure Channel Establishment
+Each task does its thing. Sometimes they play nice. Sometimes they don't.
-This phase is performed on every boot to establish a secure session.
+## � The Problems (Why You're Here)
-
+### Problem #1: Serial Breaks When WiFi Starts
+- **What happens:** You plug it in, start provisioning, then enable WiFi → EVERYTHING BREAKS
+- **Why:** WiFi initialization steals CPU time from serial task
+- **Current fix:** Wait 60 seconds before enabling WiFi so provisioning can finish
+- **Better fix:** TODO (we're working on it)
-1. **Ephemeral Key Generation:** The host and token each generate an ephemeral ECDH keypair.
-2. **Signed Key Exchange:** They exchange their ephemeral public keys, signing them with their persistent private keys from the pairing phase.
-3. **Signature Verification:** Each party verifies the signature on the received ephemeral public key using the other's stored persistent public key.
-4. **Secure Secret Derivation:** A shared secret is derived using the ECDH algorithm.
-5. **Session Key Generation:** A KDF (Key Derivation Function) is used to generate an AES-128 session key from the shared secret.
-6. **Channel Verification:** The channel is verified with an encrypted ping-pong exchange.
+**TL;DR:** Do provisioning FIRST, THEN WiFi is okay.
-### Phase 3: Integrity Verification & Runtime Guard
+### Problem #1.5: Garbage Output During Provisioning (The Real Issue)
+- **What you see:** Corrupted binary data mixed with partial debug messages
+- **Example:**
+```
+�YVOS����fCOO�f=p$
+*Sent T2H_ECDH_SHARE (host-initiated ECDH)
+J��y�����)ۘ%�EU
+```
+- **Why it happens:** Serial ISR receiving data while main protocol handler is processing
+- **Root cause:** No proper frame synchronization - data gets interleaved
+- **This means:** Protocol state machine is running during USB ISR, causing data corruption
+- **Impact:** ECDH exchange fails, provisioning breaks completely
-This phase ensures the host is running the correct software before allowing it to boot.
+**TL;DR:** Serial + WiFi task switching breaks the protocol. Need to disable WiFi polling DURING provisioning.
-
+### Problem #2: macOS Hates Unplugging It
+- **What happens:** Unplug/replug a few times → macOS loses the device completely
+- **Why:** macOS USB driver gets confused (not our problem but we suffer)
+- **How to fix it:** Restart your Mac (bruh)
+- **Pro tip:** Just leave it plugged in. Works fine if you don't touch it.
-1. **Integrity Challenge:** The token sends a random nonce to the host.
-2. **Hash Calculation:** The host calculates a hash of its current boot file.
-3. **Signed Response:** The host signs the hash and the nonce with its persistent private key and sends the signature and hash to the token.
-4. **Verification:** The token verifies the signature and compares the received hash with the stored "golden hash".
-5. **Boot Signal:** If the verification is successful, the token sends a `T2H_BOOT_OK` signal to the host; otherwise, it sends `T2H_INTEGRITY_FAIL_HALT`.
+**TL;DR:** Regular Pico 2 doesn't have this. WiFi chip adds drama.
-### Runtime Heartbeat
+### Problem #3: API Info Crashes
+- **What it tried to do:** Read temperature sensor
+- **What actually happened:** Pico went to another dimension 🌀
+- **Status:** Removed from API
+- **Can we bring it back?** Maybe, if we rewrite it
-After a successful boot, the host sends periodic heartbeat messages to the token to maintain the session.
+**TL;DR:** Just don't ask for it.
-### Shutdown Policy
+## 🔨 Build It
-The system will shut down under the following conditions:
+```bash
+cd build
+cmake ..
+make -j4
+picotool load pico_project_template.uf2 -u -f
+```
-* A protocol phase is not completed within 30 seconds.
-* Either the host or token sends a "no-go" signal.
-* The `T2H_BOOT_OK` signal is not received within 2 minutes of starting the attestation process.
-* The heartbeat timeout occurs more than 3 times.
+Standard procedure. Nothing fancy.
-## Building and Running the Project
+## 🧪 Test It
-### Prerequisites
+### Test via USB Serial
+```bash
+screen /dev/tty.usbmodem* 115200
+# See debug output, watch provisioning happen
+```
-* Raspberry Pi Pico SDK
-* CMake (version 3.13 or later)
-* ARM GCC Compiler
+### Test via WiFi
+Connect to `MASTR-Token` WiFi, then:
-### Build Instructions
+```bash
+# Quick check - is it alive?
+curl http://192.168.4.1/api/ping
-1. **Create a build directory:**
+# Check provisioning status
+curl http://192.168.4.1/api/status
- ```bash
- mkdir build
- cd build
- ```
+# Open web interface
+open http://192.168.4.1
+```
-2. **Configure for your board:**
+## � Stats
- * **For Raspberry Pi Pico (RP2040):**
+- **Firmware Size:** 463 KB (still fits in 4 MB)
+- **RAM Used:** 138 KB (we got room)
+- **Bugs:** Still has some but we shipped it anyway
- ```bash
- cmake .. -DPICO_BOARD=pico
- ```
+## 🎯 What's Next
- * **For Raspberry Pi Pico W (RP2040 with WiFi):**
+1. **FIX: Disable WiFi polling during provisioning**
+ - Add `provisioning_active` flag to protocol_state
+ - Set to `true` at boot, `false` when reaching state 0x40
+ - Modify `wifi_background_task()` to skip `cyw43_arch_poll()` if provisioning_active
+ - This prevents task switching during ECDH key exchange
- ```bash
- cmake .. -DPICO_BOARD=picow
- ```
+2. **Test full flow:** Provision → then WiFi polling starts → both work together
- * **For a generic RP2350 board:**
+3. **Maybe fix `/api/info`** → Temperature reading without crashing
- ```bash
- cmake .. -DPICO_PLATFORM=rp2350
- ```
+4. **macOS USB drama** → Not much we can do, macOS issue
-3. **Build the project:**
+---
- ```bash
- make
- ```
-
-### Running
-
-1. Connect your Pico board to your computer while holding the `BOOTSEL` button.
-2. Drag and drop the `mastr.uf2` file from the `build` directory onto the `RPI-RP2` mass storage device.
-
-## Testing
-
-The project uses the Unity test framework. The tests are located in the `test` directory.
-
-### Running the Tests
-
-1. Navigate to the build directory: `cd build`
-2. Build the test runner: `make test_runner`
-3. Run the tests: `ctest`
+Made on a Pico 2 W. Works most of the time. That's a win in my book. ✌️
\ No newline at end of file
diff --git a/docs/ap_http_architecture.md b/docs/ap_http_architecture.md
new file mode 100644
index 0000000..2bb3e59
--- /dev/null
+++ b/docs/ap_http_architecture.md
@@ -0,0 +1,220 @@
+# Access Point & HTTP API Architecture
+
+This document explains how the Wi‑Fi Access Point (AP) and embedded HTTP API stack are initialized, how requests flow through the system, and the design decisions taken to improve stability (including the recent changes you requested).
+
+---
+## High-Level Overview
+
+Component | Responsibility
+--------- | --------------
+`wifi_ap.c` | Holds persistent AP configuration, starts/stops/reconfigures AP, background tasks.
+`ap_manager.c` | Lower-level AP credential (re)configuration helper (e.g. `reconfigure_access_point`).
+`http_server.c` | Minimal single-connection HTTP server built on lwIP `tcp_*` APIs.
+`api.c` | Registers REST-style endpoints and implements each handler.
+`cpu_monitor.c` | Runtime CPU percentage calculation (used by `/api/cpu`).
+
+Boot sequence (simplified):
+1. `main.c` calls `wifi_ap_init()` (lightweight marker, not full CYW43 init).
+2. Scheduler starts; `wifi_ap_init_task` runs and calls `wifi_ap_start()` to bring up AP (initially OPEN).
+3. `ap_manager` initializes AP stack and calls `http_server_init()`.
+4. `api_register_routes()` registers all endpoint handlers.
+5. `wifi_background_task` wakes periodically to allow lwIP + Wi‑Fi driver progress.
+6. Client connects to AP and issues HTTP requests (curl or browser). Handlers respond and connection closes.
+
+---
+## AP Lifecycle & Configuration
+
+### Persistent Password Storage
+- `wifi_pass_storage[65]` holds the active passphrase (or empty string for OPEN). This avoids dangling pointers when the password changes.
+- `wifi_config.password` always points to this storage.
+
+### Start
+`wifi_ap_start(const wifi_ap_config_t *config)` deep-copies the password, sets IP (192.168.4.1), and calls `start_access_point()`.
+
+### Rotate Password
+`wifi_ap_rotate_password(new_pass)` updates the persistent buffer then calls `reconfigure_access_point()` (no full teardown). If reconfigure fails:
+- Falls back to OPEN (empty password) for recovery.
+
+### Stop
+`wifi_ap_stop()` invokes `stop_access_point()` and marks `is_running=false`.
+
+### Claim Flow
+- `/api/claim` (in `api.c`) generates a random passphrase (currently 16 characters from [A–Za–z0–9]).
+- Sets `g_claimed=true` and starts a one-shot timer (grace period ~750 ms) before applying new credentials via `wifi_ap_rotate_password()`.
+- Clients receive JSON with new password and reconnect before AP reconfigures.
+
+---
+## Tasks & Priorities (FreeRTOS)
+
+Task | Priority (approx) | Purpose
+---- | ------------------ | -------
+Serial / Protocol | High (MAX-6) | Handles secure protocol, crypto events.
+Watchdog | High (MAX-5) | Monitors session timeout, triggers re-attestation.
+WiFi Background (`wifi_background_task`) | High (MAX-7) | Allows lwIP + CYW43 driver housekeeping every 50 ms.
+HTTP Server Task (`http_server_task`) | Low (~5) | Currently passive (monitoring); core HTTP I/O is interrupt/callback-driven.
+AP Init Task (`wifi_ap_init_task`) | Low (~5) | One-shot start of AP after scheduler boot.
+
+Notes:
+- The HTTP server does not require a dedicated worker for each connection; lwIP invokes recv callbacks in the context of its stack processing.
+- The background Wi‑Fi task is critical—without its periodic delay loop, driver events and DHCP timeouts can stall, causing intermittent API failures.
+
+---
+## HTTP Server Design (`http_server.c`)
+
+### Rationale
+The stock lwIP raw TCP interface is used for a tiny, predictable footprint. Earlier instability was traced to closing the TCP connection immediately after queuing writes, occasionally producing client-side errors (curl: Recv failure / connection reset). The updated server defers closure until the data is ACKed.
+
+### Core Structures
+```
+struct route_entry { const char *path; http_handler_fn handler; };
+static route_entry routes[MAX_ROUTES];
+static http_state_t g_state; // Single connection state
+```
+- Single active connection slot to minimize RAM: one `request[1024]` buffer.
+- Additional connections are politely closed (not aborted) while busy.
+
+### Flow
+1. Accept: `http_accept` -> if free, mark `g_state.in_use=true`, install callbacks.
+2. Receive: `http_recv` accumulates data until `\r\n\r\n` (end of headers).
+3. Dispatch: `handle_request` matches path against `routes[]` and invokes handler.
+4. Respond: Handler calls `http_send_json()` -> `send_response()` writes header/body.
+5. Close: `tcp_sent` callback triggers `http_close()` once ACKed.
+
+### Stability Improvements
+Change | Benefit
+------ | -------
+Deferred close (wait for ACK) | Avoids race where client sees truncated or reset connection.
+Polite busy handling (close vs abort) | Prevents RST storms under rapid curls.
+Removed multi-connection experimental code | Simpler state, fewer edge cases.
+Minimal per-request parsing | Keeps latency and RAM usage low.
+
+### Adding Endpoints
+```
+void api_register_routes(void) {
+ http_register("/api/ping", ping_handler);
+ http_register("/api/health", health_handler);
+ http_register("/api/status", status_handler);
+ // ... etc ...
+}
+```
+1. Implement `static void new_handler(struct tcp_pcb *pcb, const char *request)` in `api.c`.
+2. Register with `http_register("/api/your_path", new_handler);`.
+3. Use `http_send_json(pcb, status_code, json_string);` to respond.
+
+---
+## API Layer (`api.c`)
+
+Endpoint | Purpose | Key Operations
+-------- | ------- | --------------
+`/api/health` | Connectivity probe | Returns `{"ok":true}` quickly.
+`/api/ping` | Simple RTT test | Returns `{"message":"pong"}`.
+`/api/status` | Device + protocol state | Uptime, provisioning, claim flag.
+`/api/network` | AP + DHCP lease info | Enumerates connected MAC/IP pairs.
+`/api/ram` | Heap usage snapshot | Total/used/free, percent.
+`/api/cpu` | CPU utilization | Uses `cpu_monitor` runtime stats.
+`/api/temp` | Internal MCU temp | Averaged ADC samples -> °C.
+`/api/claim` | Provisioning password set | Random PSK generation + deferred rotation.
+
+Handler pattern:
+1. (Optional) gather metrics/state.
+2. Format JSON with `snprintf` into a stack buffer.
+3. Call `http_send_json()`.
+
+### Claim Timer Mechanism
+- Creates a one-shot FreeRTOS timer (`xTimerCreate`) after responding.
+- Timer callback spawns `ap_restart_task` (worker task) to perform password rotation after a grace delay (~750 ms). This avoids resetting the AP before the HTTP response leaves the stack.
+
+---
+## CPU & Temperature Metrics
+
+Metric | Source | Notes
+------ | ------ | -----
+CPU% | `cpu_get_percent()` | Runtime stats only; idle task accounted via FreeRTOS.
+Temp | Pico ADC (channel 4) | First sample discarded; 8-sample average; formula 27°C at 0.706V.
+RAM | FreeRTOS heap API | `xPortGetFreeHeapSize()` vs `configTOTAL_HEAP_SIZE`.
+
+---
+## Error Handling & Fallbacks
+Scenario | Behavior
+-------- | --------
+Failed AP start | Logs error, returns false; higher layer may retry.
+Failed password rotate | Falls back to OPEN mode to keep access path alive.
+Route not found | Returns 404 JSON consistently.
+TCP write failure | Aborts connection to prevent partial/inconsistent response.
+Busy server (already serving) | Closes new connection gracefully (no RST).
+
+---
+## Known Limitations / Future Enhancements
+Item | Description | Possible Improvement
+---- | ----------- | -------------------
+Single connection slot | Sequential request handling only | Add small pool (2–4) if concurrency needed.
+No rate limiting | High-frequency curls may monopolize slot | Lightweight token bucket per handler.
+Static JSON formatting | Manual `snprintf` | Consider tiny JSON builder for safety/escaping.
+No persistence of claim flag | Lost on reboot | Store claimed state in flash/OTP.
+Open AP initial state | Convenience for provisioning | Optionally advertise via captive portal & then lock down.
+
+---
+## Quick Reference Cheat Sheet
+Action | How
+------ | ----
+Add endpoint | Implement handler in `api.c`, register in `api_register_routes()`.
+Get AP config | `wifi_ap_get_config()->ssid / password`.
+Rotate password | `wifi_ap_rotate_password(new_psk)`.
+Check claimed state | `g_claimed` (internal to `api.c`).
+Probe health | curl `/api/health`.
+Restart AP manually | `wifi_ap_rotate_password(current_password)` (no change) or add stop/start wrappers.
+
+---
+## Minimal Curl Examples
+```
+# Connectivity
+curl -v http://192.168.4.1/api/health
+
+# Status
+curl -s http://192.168.4.1/api/status | jq
+
+# Claim flow
+curl -s http://192.168.4.1/api/claim | jq # then reconnect with returned password after grace period
+
+# Metrics
+curl -s http://192.168.4.1/api/cpu
+curl -s http://192.168.4.1/api/temp
+```
+
+---
+## Stability Summary of Recent Changes
+Change | Problem Addressed
+------ | -----------------
+Deferred close via `tcp_sent` | Eliminated intermittent client-side RST during short responses.
+Polite busy handling | Prevented abrupt resets under rapid successive curls.
+Health endpoint | Provided ultra-light probe to separate transport vs handler latency issues.
+Password rotation timer | Ensured `/api/claim` response is delivered before AP reconfiguration.
+Runtime-only CPU monitoring | Removed earlier idle tick heuristic that produced 0% or erratic values.
+
+---
+## Troubleshooting Tips
+Symptom | Check | Action
+------- | ----- | ------
+Curl sporadic failures | Is `/api/health` stable? | If yes, inspect specific heavy endpoints (ADC, DHCP). Rate-limit them.
+Claim response received but reconnection fails | Was grace timer executed? | Verify timer creation and worker task ran; increase grace period.
+CPU always 0% | Is `cpu_monitor` initialized? | Ensure runtime stats scaling function compiled and scheduler running.
+No clients listed | DHCP leases empty? | Confirm device associated; check Wi‑Fi channel/interference.
+
+---
+## Glossary
+Term | Meaning
+---- | -------
+AP | Access Point broadcasting SSID for provisioning.
+Grace Period | Delay after claim before applying new password.
+lwIP | Lightweight IP stack used for TCP/UDP.
+CYW43 | Wi‑Fi chip/driver (Raspberry Pi Pico W).
+Idle Task | FreeRTOS lowest-priority task measuring unused CPU time.
+
+---
+## Revision History
+Date | Change | Author
+---- | ------ | ------
+2025-11-09 | Initial architecture doc capturing AP + HTTP design and stability fixes | Generated assistant
+
+---
+End of document.
diff --git a/docs/provisioning_flow.md b/docs/provisioning_flow.md
new file mode 100644
index 0000000..4dfb62e
--- /dev/null
+++ b/docs/provisioning_flow.md
@@ -0,0 +1,258 @@
+# One-time Host–Token Pairing (Provisioning) Plan
+
+This document explains the goal Lucas is aiming for and outlines the concrete API and UI flow to move the first‑run provisioning from debug prints to the HTTP API + web UI.
+
+---
+## Goal in plain terms
+- On first boot, the Token (device) and a Host (PC/server) must pair exactly once.
+- Today, this pairing uses debug console messages to exchange the Token public key and a "golden hash". The ask is to make this happen via API endpoints and a simple UI wizard so the user can copy/paste the necessary data.
+- After pairing, the device should remember the Host and the golden hash, and normal operation begins; subsequent boots should not require pairing again unless reset.
+
+---
+## How it works today (CURRENT STATE)
+
+Mechanism today is console/serial driven:
+1. Device boots and prints debug messages (via `print_dbg`) exposing: public key, intermediate protocol states, and the "golden hash".
+2. A host Python script (or manual copy in a terminal) reads those debug lines, parses out required values.
+3. Host responds (over serial protocol frames) with its public key and expected golden hash.
+4. Device stores these in RAM (not persisted) and transitions the internal `protocol_state.current_state` to runtime (0x40).
+
+Limitations:
+- Manual copy/paste, easy to mis-transcribe.
+- No structured validation feedback (just debug prints).
+- Values are ephemeral; reboot requires repeating unless separately saved.
+- Harder to script remotely (must attach to USB serial).
+- No UI representation; provisioning progress opaque unless watching terminal.
+
+Summary: Pairing is implicit, using the existing framed serial protocol and debug lines, not an explicit transactional API.
+
+---
+## Target architecture (AFTER CHANGES)
+
+Replace ad‑hoc serial exchange with a REST + UI wizard:
+1. Device starts OPEN AP (no password) and advertises provisioning endpoints.
+2. Browser hits `/api/provision/state` to see if `provisioned=false`.
+3. User opens "Provisioning" page: UI calls `/api/provision/token_info` and displays token public key, optional attestation blob, and (optionally) device-generated golden hash.
+4. User (or host software) supplies Host Public Key + Golden Hash via POST `/api/provision/host_submit`.
+5. UI shows submitted values for confirmation; user presses "Confirm" -> POST `/api/provision/confirm`.
+6. Device persists host key + golden hash + provisioned flag in flash; subsequent `/api/provision/state` returns `provisioned=true` and endpoints are locked.
+7. Runtime heartbeat uses `/api/heartbeat` or existing `/api/status`.
+
+Benefits:
+- Predictable JSON contracts; easy to automate.
+- One-time explicit confirmation step.
+- Copy/paste friendly UI (no terminal required).
+- Persistence ensures reboot resilience.
+- Clear error codes (400, 409) instead of generic debug lines.
+
+---
+## Delta: What must change
+
+Area | Current | Required Change
+---- | ------- | ---------------
+Public key exposure | Printed in debug | Serve via `/api/provision/token_info` JSON
+Golden hash exchange | Printed / serial frame | Submit via `/api/provision/host_submit` body
+Provisioning state | Implicit in protocol_state | Explicit `/api/provision/state` (provisioned + step)
+Confirmation | None (implicit) | `/api/provision/confirm` finalizes & persists
+Persistence | RAM only | Flash/EEPROM sector with CRC (host key + hash + flag)
+UI | Debug terminal | Web wizard (three steps) in `index.html`
+Security gating | N/A | Disable provisioning endpoints after success; require factory reset to re-enable
+Attestation | Mixed debug prints | Optional structured field (`attestation.report`, `nonce`)
+Errors | Freeform prints | HTTP status + JSON `{ "error": "..." }`
+
+---
+## Minimal state machine (new)
+
+State | Description | Transitions
+----- | ----------- | ----------
+`start` | Device unprovisioned, waiting for user | -> `token_info` on first GET
+`token_info` | Token info served | -> `await_host` after host submits data
+`await_host` | Host data stored, waiting confirmation | -> `done` on confirm
+`done` | Provisioned complete | (locked) unless factory reset
+
+Implementation hint: Store `current_provision_step` in RAM; persist only the final `provisioned` flag and artifacts to flash.
+
+---
+## Persistence strategy (flash layout example)
+
+| Offset | Length | Field |
+| ------ | ------ | ----- |
+| 0x00 | 4 | Magic (0x504B5450 'PKTP') |
+| 0x04 | 2 | Version |
+| 0x06 | 2 | Host key length |
+| 0x08 | N | Host public key PEM (N bytes) |
+| 0x08+N | 2 | Golden hash length |
+| ... | M | Golden hash bytes (hex or raw) |
+| ... | 1 | Provisioned flag (0x01) |
+| ... | 4 | CRC32 over all preceding fields |
+
+Failure handling:
+- On CRC mismatch -> treat as unprovisioned.
+- On flash write failure -> return 500 from `/api/provision/confirm`.
+
+---
+## Validation rules (to implement)
+- Host public key PEM: begins with `-----BEGIN PUBLIC KEY-----`, length within bounds (e.g. 512–2048 bytes).
+- Golden hash: hex (even length) or base64; server normalizes to hex internally.
+- Reject duplicate provisioning (respond 409 if `provisioned=true`).
+- Attestation (if used): nonce length fixed (e.g. 16 bytes), report base64 size check.
+
+---
+## Migration plan (step-by-step)
+1. Add in-memory scaffolding for new endpoints (no persistence yet) returning mock values.
+2. Integrate real token public key extraction (from crypto subsystem) into `/api/provision/token_info`.
+3. Implement host submission parsing & validation.
+4. Implement flash persistence layer (read/write + CRC) and finalize confirm step.
+5. Extend `index.html` with provisioning wizard section; hide existing metrics until provisioned.
+6. Gate existing sensitive endpoints (if any) while `!provisioned` (optional).
+7. Add factory reset mechanism (e.g., long button press or `/api/provision/reset` with physical presence check).
+8. Remove or minimize debug prints of sensitive material (replace with informational logs only).
+
+---
+## Risks & Mitigations
+Risk | Mitigation
+---- | ----------
+Power loss during flash write | Write to temp sector then atomic flag set; verify CRC on boot.
+Host submits malformed PEM | Strict parser + length limits + 400 response.
+Replay / MITM on open AP | Physical proximity assumption + optional attestation challenge.
+Accidental re-provision | Require factory reset or signed admin token.
+
+---
+## Token vs Host Responsibilities (recap)
+Token: Serve provisioning endpoints, validate inputs, persist artifacts, enforce golden hash.
+Host: Fetch token info, verify/record, POST host key + golden hash, confirm.
+
+---
+## Actors and artifacts
+- Token (this device)
+ - Token Public Key (from secure element ATECC608A or internal key store)
+ - Attestation proof/evidence (optional but recommended)
+ - Golden Hash (reference hash of firmware/config or key material agreed upon with Host)
+- Host (your PC/app)
+ - Host Public Key
+ - Expected Golden Hash
+
+Persistent on Token after pairing:
+- host_public_key
+- golden_hash
+- provisioned flag (true)
+
+---
+## Trust model (high-level)
+- First‑run pairing happens over the Token’s local AP; the user is physically present.
+- The Token shows its identity (public key + optional attestation) so the Host can verify.
+- The Host provides its public key and the golden hash the Token must enforce at runtime.
+- Token stores both and marks itself provisioned.
+
+---
+## Proposed API surface (one-time provisioning)
+All endpoints are unauthenticated only when not yet provisioned; once provisioned, they are either disabled or require auth.
+
+1) GET `/api/provision/state`
+- Purpose: Tell the UI if pairing is needed and what step we’re at.
+- Response:
+```
+{
+ "provisioned": false,
+ "step": "start" | "token_info" | "await_host" | "done"
+}
+```
+
+2) GET `/api/provision/token_info`
+- Purpose: Provide copy/pasteable Token identity to the user/Host.
+- Response:
+```
+{
+ "token_pubkey_pem": "-----BEGIN PUBLIC KEY-----...",
+ "attestation": { "nonce": "...", "report": "base64..." },
+ "golden_hash": "System Monitoring & Device Status
+