From fd25de32a10d481013bbda8b25ed87b662390596 Mon Sep 17 00:00:00 2001 From: Skipper Date: Sat, 18 Apr 2026 15:14:03 +0200 Subject: [PATCH] docs: move to folder and update to new challenge payload --- .gitignore | 2 +- CLAUDE.md | 150 +------------------- ARCHITECTURE.md => docs/ARCHITECTURE.md | 6 +- IMPLEMENTATION.md => docs/IMPLEMENTATION.md | 8 +- 4 files changed, 13 insertions(+), 153 deletions(-) rename ARCHITECTURE.md => docs/ARCHITECTURE.md (97%) rename IMPLEMENTATION.md => docs/IMPLEMENTATION.md (94%) diff --git a/.gitignore b/.gitignore index 6777228..d73db15 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ scripts/__pycache__/ .DS_Store .cargo/config.toml .vscode/ -docs/ +docs/superpowers diff --git a/CLAUDE.md b/CLAUDE.md index 9af228d..95ed972 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,149 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -Arbiter is a **permissioned signing service** for cryptocurrency wallets. It consists of: -- **`server/`** — Rust gRPC daemon that holds encrypted keys and enforces policies -- **`useragent/`** — Flutter desktop app (macOS/Windows) with a Rust backend via Rinf -- **`protobufs/`** — Protocol Buffer definitions shared between server and client - -The vault never exposes key material; it only produces signatures when requests satisfy configured policies. - -## Toolchain Setup - -Tools are managed via [mise](https://mise.jdx.dev/). Install all required tools: -```sh -mise install -``` - -Key versions: Rust 1.93.0 (with clippy), Flutter 3.38.9-stable, protoc 29.6, diesel_cli 2.3.6 (sqlite). - -## Server (Rust workspace at `server/`) - -### Crates - -| Crate | Purpose | -|---|---| -| `arbiter-proto` | Generated gRPC stubs + protobuf types; compiled from `protobufs/*.proto` via `tonic-prost-build` | -| `arbiter-server` | Main daemon — actors, DB, EVM policy engine, gRPC service implementation | -| `arbiter-useragent` | Rust client library for the user agent side of the gRPC protocol | -| `arbiter-client` | Rust client library for SDK clients | - -### Common Commands - -```sh -cd server - -# Build -cargo build - -# Run the server daemon -cargo run -p arbiter-server - -# Run all tests (preferred over cargo test) -cargo nextest run - -# Run a single test -cargo nextest run - -# Lint -cargo clippy - -# Security audit -cargo audit - -# Check unused dependencies -cargo shear - -# Run snapshot tests and update snapshots -cargo insta review -``` - -### Architecture - -The server is actor-based using the **kameo** crate. All long-lived state lives in `GlobalActors`: - -- **`Bootstrapper`** — Manages the one-time bootstrap token written to `~/.arbiter/bootstrap_token` on first run. -- **`Vault`** — Holds the encrypted root key and manages the Sealed/Unsealed vault state machine. On unseal, decrypts the root key into a `memsafe` hardened memory cell. -- **`FlowCoordinator`** — Coordinates cross-connection flow between user agents and SDK clients. -- **`EvmActor`** — Handles EVM transaction policy enforcement and signing. - -Per-connection actors live under `actors/user_agent/` and `actors/client/`, each with `auth` (challenge-response authentication) and `session` (post-auth operations) sub-modules. - -**Database:** SQLite via `diesel-async` + `bb8` connection pool. Schema managed by embedded Diesel migrations in `crates/arbiter-server/migrations/`. DB file lives at `~/.arbiter/arbiter.sqlite`. Tests use a temp-file DB via `db::create_test_pool()`. - -**Cryptography:** -- Authentication: ed25519 (challenge-response, nonce-tracked per peer) -- Encryption at rest: XChaCha20-Poly1305 (versioned via `scheme` field for transparent migration on unseal) -- Password KDF: Argon2 -- Unseal transport: X25519 ephemeral key exchange -- TLS: self-signed certificate (aws-lc-rs backend), fingerprint distributed via `ArbiterUrl` - -**Protocol:** gRPC with Protocol Buffers. The `ArbiterUrl` type encodes host, port, CA cert, and bootstrap token into a single shareable string (printed to console on first run). - -### Proto Regeneration - -When `.proto` files in `protobufs/` change, rebuild to regenerate: -```sh -cd server && cargo build -p arbiter-proto -``` - -### Database Migrations - -```sh -# Create a new migration -diesel migration generate --migration-dir crates/arbiter-server/migrations - -# Run migrations manually (server also runs them on startup) -diesel migration run --migration-dir crates/arbiter-server/migrations -``` - -### Code Conventions - -**`#[must_use]` Attribute:** -Apply the `#[must_use]` attribute to return types of functions where the return value is critical and should not be accidentally ignored. This is commonly used for: - -- Methods that return `bool` indicating success/failure or validation state -- Any function where ignoring the return value indicates a logic error - -Do not apply `#[must_use]` redundantly to items (types or functions) that are already annotated with `#[must_use]`. - -Example: - -```rust -#[must_use] -pub fn verify(&self, nonce: i32, context: &[u8], signature: &Signature) -> bool { - // verification logic -} -``` - -This forces callers to either use the return value or explicitly ignore it with `let _ = ...;`, preventing silent failures. - -## User Agent (Flutter + Rinf at `useragent/`) - -The Flutter app uses [Rinf](https://rinf.cunarist.org) to call Rust code. The Rust logic lives in `useragent/native/hub/` as a separate crate that uses `arbiter-useragent` for the gRPC client. - -Communication between Dart and Rust uses typed **signals** defined in `useragent/native/hub/src/signals/`. After modifying signal structs, regenerate Dart bindings: - -```sh -cd useragent && rinf gen -``` - -### Common Commands - -```sh -cd useragent - -# Run the app (macOS or Windows) -flutter run - -# Regenerate Rust↔Dart signal bindings -rinf gen - -# Analyze Dart code -flutter analyze -``` - -The Rinf Rust entry point is `useragent/native/hub/src/lib.rs`. It spawns actors defined in `useragent/native/hub/src/actors/` which handle Dart↔server communication via signals. +Refer to @AGENTS.md for instructions. diff --git a/ARCHITECTURE.md b/docs/ARCHITECTURE.md similarity index 97% rename from ARCHITECTURE.md rename to docs/ARCHITECTURE.md index 80119fd..cbf78d9 100644 --- a/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -22,12 +22,14 @@ Arbiter distinguishes two kinds of peers: All peers authenticate via public-key cryptography using a challenge-response protocol: 1. The peer sends its public key and requests a challenge. -2. The server looks up the key in its database. If found, it increments the nonce and returns a challenge (replay-attack protection). -3. The peer signs the challenge with its private key and sends the signature back. +2. The server looks up the key in its database. If found, it generates a fresh challenge from random bytes plus the current timestamp. +3. The peer signs the canonical challenge payload with its private key and sends the signature back. 4. The server verifies the signature: - **Pass:** The connection is considered authenticated. - **Fail:** The server closes the connection. +Authentication challenges are per-connection, ephemeral values. They are not persisted in the peer tables, and peer records store no challenge state. + ### 2.2 User Agent Bootstrap On first run — when no User Agents are registered — the server generates a one-time bootstrap token. It is made available in two ways: diff --git a/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md similarity index 94% rename from IMPLEMENTATION.md rename to docs/IMPLEMENTATION.md index 3a0bfd9..57d38b4 100644 --- a/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -45,7 +45,13 @@ flowchart TD K -- yes --> J([Session started]) ``` -Auth challenges are generated from fresh random bytes plus a timestamp. They are signed as the canonical challenge payload and are not persisted in `program_client`. +Auth challenges are generated from fresh random bytes plus a nanosecond timestamp. The server keeps the issued challenge only in the in-flight authentication state for that connection, then verifies the signature against the same canonical challenge payload. + +The authentication schema stores peer identity, not replay counters: + +- `program_client` stores the SDK client's public key, metadata binding, and timestamps. +- `useragent_client` stores the User Agent public key and timestamps. +- Neither table stores an authentication nonce, and challenge generation does not update either table. ---