Skip to content

Latest commit

 

History

History
294 lines (239 loc) · 13.5 KB

File metadata and controls

294 lines (239 loc) · 13.5 KB

OneCipher Agent Instructions

TL;DR (30s): Pre-1.0, publish = false — NO backward-compat guarantee. Minimal implementation first: smallest correct change, no speculative APIs. Grow layer by layer: core → crypto/signer → wallet/keyagent → netagent/webui/CLI. Delete old paths when replacing them (retired crates leave no skeleton behind). Secrets via HardenedBytes, never String/Vec<u8>; R56/R12 gates are non-negotiable. Details: layout · hard gates · workflow. Storage split: oc-vault = bytes, oc-wallet = ops, oc-secret = user secrets (boundary).

Scope

This is the OneCipher workspace — a policy-gated, local-key-custody signing stack fully designed and implemented in accordance with the WalletConnect v2 protocol and the Open Wallet Standard (OWS).

  • bin/ contains binary crates (oc-clionecipher).
  • crates/ contains reusable library crates (oc-core, oc-crypto, …).

Workspace Layout

.
├── bin/                    # Binary crates
│   └── oc-cli/             # `onecipher` CLI
├── crates/                 # Library crates
│   ├── oc-core/            # Core types, CAIP, error types
│   ├── oc-crypto/          # Memory hardening (mlock, zeroize, page guards)
│   ├── oc-keyagent/        # Key-Agent lib (sync std, NO tokio — R56)
│   ├── oc-netagent/        # Network-Agent lib (tokio + WC v2 + intent layer)
│   ├── oc-policy/          # Policy Engine v2/v3 (11-step evaluation)
│   ├── oc-secret/          # Secret vault (age-encrypted secrets + TOTP)
│   ├── oc-session-key/     # Multi-chain SessionKeyProvider (EVM/Solana)
│   ├── oc-signer/          # Multi-chain signing
│   ├── oc-vault/           # Wallet vault (filesystem 700/600, .ocbk backup)
│   ├── oc-wallet/          # Wallet operations (key store, policy, migration)
│   ├── oc-walletconnect/   # WalletConnect v2 protocol wrapper (relay, crypto)
│   └── oc-webui/           # Web UI HTTP server (approval queue, WebAuthn auth, static dashboard)
├── docs/                   # Specification documents
└── Cargo.toml              # Workspace root (pure [workspace] declaration)

Note: The payment protocol layer (oc-pay, x402/MPP) was removed from this workspace; it is owned by the sister project ledgerflow. OneCipher now acts as a pure wallet and exposes a loopback JSON-RPC 2.0 WalletSigner server (onecipher wallet-rpc) for ledgerflow. The daemon keeps this endpoint disabled by default unless OC_WALLET_RPC_LISTEN is set, and signing methods on that surface require per-request Passkey authorization.

Storage Boundary (authoritative)

Three crates deal with persisted secrets/state. Their responsibilities are deliberately disjoint — do NOT merge them and do NOT add cross-calls:

Crate Owns Format Notes
oc-wallet::key_store API tokens (oc_key_…) JSON, 0600 Agent/CLI auth tokens; agent-mode tokens additionally carry age-X25519-encrypted wallet-key copies for the token recipient (intentional design — NOT user secrets).
oc-secret User secrets (age-encrypted) + TOTP age ciphertext Never holds keys; keys live in oc-vault.
oc-vault Wallet keyfiles (age-encrypted mnemonics/keys) age/JSON, 0700 dir / 0600 file, .ocbk age bundle The persistence format; oc-wallet::ops is the operation layer that reads/writes it.
oc-wallet::policy_store Signed policy docs JSON, 0600 (+ .sig sidecar) Policies are signed on save with a dedicated Ed25519 key (policy_signing.key); load verifies the sidecar .sig (fail-closed on mismatch; legacy unsigned files load with a loud warning). Counters live in oc-policy state.

Rule of thumb: oc-vault = how bytes are stored on disk; oc-wallet = what wallet operations do with them; oc-secret = user secrets (not keys). oc-wallet::key_store API tokens are NOT user secrets and stay in oc-wallet.

Intent Layer location

The Intent types (Intent, IntentKind, execute_intent, simulate_intent) live in crates/oc-netagent/src/intent (the sole consumer is oc-netagent via HpxRpcClient). The retired oc-intent crate was removed from the workspace entirely; there is no directory to keep.

Execution Strategy

  • Maximize parallelism by dispatching subagents aggressively for independent tasks. Consume tokens freely to complete tasks faster.
  • When fixing errors, edit files FIRST, wait for the edit to succeed, and only THEN run cargo commands to verify. Do not parallelize file edits with cargo builds.

Tool Usage & Commands

  • NEVER execute cargo commands in parallel. Rust's cargo uses strict file locks on the target/ directory — concurrent invocations will fail with Blocking waiting for file lock.
  • ALWAYS run cargo check, cargo build, or cargo test sequentially.
  • If the local machine has a rustc-wrapper (sccache / kache) configured globally but the wrapper binary is missing, prefix cargo commands with RUSTC_WRAPPER= to disable the wrapper for that invocation.
  • Use just <recipe> for common tasks — see Justfile.

Cargo Workspace Rules (Critical)

  1. The root Cargo.toml is a pure workspace declaration — it has no [package] section. All package metadata lives in [workspace.package] and is inherited by sub-crates via version.workspace = true, edition.workspace = true, etc.
  2. Sub-crates MUST use workspace = true for version, edition, license, repository, publish, and [lints].
  3. Shared dependencies are declared once in [workspace.dependencies] and referenced in sub-crates via dep = { workspace = true }.
  4. Sub-crates MAY add features on top of the workspace dep: tokio = { workspace = true, features = ["full"] }.
  5. When adding a new dependency used by 2+ crates, add it to [workspace.dependencies] in the root Cargo.toml first.
  6. Single-crate dependencies may stay inline in the sub-crate's Cargo.toml (e.g. uuid, criterion, bs58).
  7. Never manually type dependency versions for workspace deps; use cargo add <crate> --workspace -p <sub-crate>.

OneCipher Hard Gates

These are non-negotiable invariants verified by cargo tree inspection (R56) and binary symbol analysis (R12), verified by dev-dependency and source scans:

  • R56 (dependency isolation): oc-crypto, oc-policy, oc-keyagent, oc-session-key MUST NOT depend on tokio, reqwest, tungstenite, hyper, async-std, or smol — even as dev-deps. Verified via cargo tree -p <crate> inspection.
  • R12 (no TCP in isolated crates; loopback-only in daemon): Revised from the original nm | grep -i tcp check (which produces false negatives on stripped binaries). Now verified via five sub-rules:
    • R12a (source isolation): oc-keyagent, oc-crypto, oc-policy, oc-session-key source MUST NOT contain TcpListener or TcpStream. Verified via rg -n 'TcpListener|TcpStream' crates/oc-keyagent/src/ ....
    • R12b (daemon may use TCP): The onecipher daemon binary MAY contain TCP symbols from axum/hyper for the Web UI HTTP server and WC relay.
    • R12c (loopback-only bind): Any TcpListener in the daemon MUST bind 127.0.0.1 exclusively. Verified via lsof -iTCP -sTCP:LISTEN -P -n.
    • R12d (T12 seccomp enforcement): At runtime, the Key-Agent's seccomp BPF filter denies connect(2) / bind(2) to non-UDS sockets.
    • R12e (non-loopback rejection): If a non-loopback bind address is configured for [webui] listen, the daemon MUST reject it and refuse to start the Web UI server.
  • R51/R52 (zero I/O in crypto): oc-crypto MUST have zero I/O and zero network dependencies.
  • R55 (no tokio in Key-Agent): The Key-Agent main loop uses sync std::os::unix::net + std::thread, NOT tokio.

Build Commands

# Build entire workspace
cargo build --workspace

# Build a specific binary (use --bin, not -p, for bin crates)
cargo build --release --bin onecipher

# Build a specific library crate
cargo build -p oc-crypto

# Check without producing artifacts (faster)
cargo check --workspace --all-targets

Test Commands

# All unit + integration tests
cargo test --workspace --all-features

# Mutation testing (requires cargo-mutants)
cargo mutants --workspace --all-features

# Incremental mutation testing (only files changed vs main)
cargo mutants --in-place --since main --all-features

Lint Commands

# Format check (requires nightly rustfmt)
cargo +nightly fmt --all -- --check

# Clippy (workspace lints are pedantic + nursery)
RUSTC_WRAPPER= cargo +nightly clippy --all -- -D warnings

# R56 hard gate — verify no forbidden async/network deps in isolated crates
cargo tree -p oc-crypto
cargo tree -p oc-policy
cargo tree -p oc-keyagent

# R12 hard gate — source-level isolation (replaces broken `nm` check)
rg -n 'TcpListener|TcpStream' crates/oc-keyagent/src/ crates/oc-crypto/src/ \
                             crates/oc-policy/src/ crates/oc-session-key/src/
# Expected: no matches (exit code 1)

Justfile Recipes

just           # list recipes
just format    # cargo sort + cargo +nightly fmt
just fix       # auto-fix clippy warnings
just lint      # fmt check + clippy + cargo sort check
just test      # unit + integration tests
just test-all  # alias for `test`
just build     # cargo build --workspace
just check     # cargo check --all-targets --all-features
just ci        # full CI check (lint + test + build)
just docs      # cargo doc --no-deps --open
just setup     # install dev tools (cargo-sort, nightly toolchain)
just mutants   # full mutation testing (cargo-mutants)
just mutants-incremental  # incremental mutation testing

Engineering Principles

Rust Implementation Guidelines

  1. Error handling:
    • Library layer: thiserror.
    • Application/CLI layer: eyre (currently oc-cli uses thiserror).
  2. Concurrency:
    • Key-Agent: sync std::thread + std::os::unix::net (R55 — NO tokio).
    • Network-Agent: tokio + WalletConnect v2 (WSS relay) + async UDS to Key-Agent (length-prefixed prost frames — see oc_keyagent::frame).
    • Prefer lock-free patterns where possible; Mutex is acceptable for low-contention state.
  3. Safety:
    • unsafe is confined to oc-crypto/src/page_guard.rs (mlock/madvise) and oc-keyagent/src/sandbox.rs (seccomp/prctl on Linux).
    • Every unsafe block MUST document the safety invariant.
  4. Memory hardening:
    • Sensitive material (mnemonics, private keys) MUST go through oc_crypto::HardenedBytes (mlock + MADV_DONTDUMP + zeroize on drop).
    • Never hold sensitive material in String or Vec<u8> — use HardenedBytes or secrecy::SecretBox.
  5. Logging:
    • Libraries SHOULD use tracing (NOT println! / eprintln!).
    • The workspace lints allow print_stdout / print_stderr because some modules fully designed and implemented in accordance with the Open Wallet Standard (process_hardening, chain deprecation warnings) use eprintln! for user-facing diagnostics. New code should still prefer tracing.

Key Design Principles

  • Modularity: Each crate is a standalone library with clear boundaries. oc-crypto has zero I/O; oc-keyagent has zero async runtime.
  • Type Safety: Strong static typing across interfaces. Newtypes for distinguished types (e.g. PasskeyPubkey, SessionKeyId).
  • Defense in Depth: Policy engine (pre-signing) + sandbox (runtime) + memory hardening (in-process) + audit log (post-hoc).

Testing Requirements

  • Unit tests: Colocate with implementation (#[cfg(test)]).
  • Property tests: Use proptest for invariant checking (colocated).
  • Integration tests: Place in crate-level tests/.
  • Mutation tests: Run cargo-mutants to verify test quality. Mutants that survive indicate gaps in test coverage or insufficient assertions. Target: zero surviving mutants in security-critical crates (oc-crypto, oc-policy, oc-keyagent).
  • Hard-gate tests: R56 (dependency isolation) is verified via cargo tree inspection; R12a (source-level isolation) via rg scan of isolated crate sources; R12c (loopback-only) via lsof runtime check.

Common Pitfalls

  • Do NOT add tokio to oc-keyagent, oc-crypto, oc-policy, or oc-session-key — R56 hard gate.
  • Do NOT hold sensitive material in String / Vec<u8> — use HardenedBytes.
  • Do NOT use cargo build -p oc-keyagent expecting a binary — oc-keyagent is a library crate only. The sole binary is onecipher (from oc-cli): use cargo build --bin onecipher.
  • Do NOT introduce unwrap() / expect() / panic! in production code paths — the workspace lints allow them (for test code), but code review enforces this for non-test code.

Development Workflow

Use test-driven development for behavior changes:

  1. Start with a failing unit test expressing the expected behavior.
  2. Drive implementation until the test passes.
  3. Add proptest property tests for invariant checking.
  4. Run just mutants to verify test quality — any surviving mutant indicates a gap that needs a new test or stronger assertion.

After each feature or bug fix, run:

just format
just lint
just test

If any command fails, report the failure and do not claim completion.

Language Requirement

  • Documentation, comments, and commit messages must be English only.
  • Code identifiers must be English.