Files
arbiter/CLAUDE.md
2026-03-15 16:46:58 +01:00

4.3 KiB

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. Install all required tools:

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

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 <test_name>

# 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.
  • KeyHolder — 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.
  • MessageRouter — Coordinates streaming messages 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:

cd server && cargo build -p arbiter-proto

Database Migrations

# Create a new migration
diesel migration generate <name> --migration-dir crates/arbiter-server/migrations

# Run migrations manually (server also runs them on startup)
diesel migration run --migration-dir crates/arbiter-server/migrations

User Agent (Flutter + Rinf at useragent/)

The Flutter app uses Rinf 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:

cd useragent && rinf gen

Common Commands

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.