Skip to content

Repository files navigation

PAW — Personal Agent Wallet

paw is an Internet Computer canister that enforces policy-governed request execution for agent-driven EVM actions, including buyer-side x402 payment flows. Agent signing/spending requests need to be approved by a human per default, but authorization can be auto-approved using fine-grained policies. Signing is performed using the Internet Computer's threshold signatures, hence, key material is never exposed to the agent or a cloud provider.

Current MVP Scope

  • Deterministic request lifecycle: submit, approve/reject, sign, callbacks, terminalization.
  • Multi-tenant user onboarding (register_user) with optional controller-issued signup invite codes.
  • Self-service wallet creation (create_wallet) and wallet-scoped request ownership.
  • Buyer-side x402 v2 (exact) flow with fail-closed policy checks.
  • Canister-owned signing via management canister ECDSA APIs.
  • Governed policy mutation with approval workflow and immutable policy versions.
  • Stable-memory persistence for users, wallets, requests, audits, replay indexes, and policy history.

Repository Layout

  • src/lib.rs: canister entrypoints and orchestration logic.
  • src/domain/*: domain models and pure validation/transition logic.
  • src/state/*: stable-memory storage and ID generation.
  • tests/*: PocketIC integration tests for lifecycle, policy, and multitenant wallet isolation flows.
  • frontend/src/*: wallet-aware React UI with invite-gated signup and wallet-scoped request views.
  • docs/design/*: shaping/spec/implementation plans and review tasklists.
  • docs/runbooks/agent-invite-signing-runbook.md: operator runbook for invite onboarding and transaction-signing request flow.

Tooling

This project uses icp-cli as the default canister toolchain.

Agent CLI Distribution Policy

  • Production/autonomous agents must execute a preinstalled, pinned paw binary.
    • Install: npm install -g @paw/sdk@<exact-version>
    • Run: paw ...
  • npx may be used only for dev/bootstrap and must be version-pinned.
    • npx -y @paw/sdk@<exact-version> paw ...
  • Do not allow floating npx @paw/sdk without @<exact-version> in CI, scripts, or unattended agents.
  • CI/CI-like runners should use an explicit install step (or repository lockfile with deterministic resolution) and invoke paw by name from PATH.

Example CI invocation pattern:

npm ci
npx -y @paw/sdk@$(node -p "require('./paw-sdk/package.json').version") paw --version

Prerequisites

  • Rust toolchain with wasm32-unknown-unknown target.
  • icp-cli installed and available on PATH.
  • Node.js 20.19.0+ (Vite 7 and SDK tests rely on Node 20 features).

Use nvm use in the repository root to select the pinned Node.js version from .nvmrc.

Common Commands

# Build Rust artifacts
cargo build

# Run unit + integration tests
cargo test --all-targets --all-features

# Run formatting and lint checks
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings

# Start local ICP network
icp network start --background

# Deploy frontend + backend (handles backend init/upgrade args)
./scripts/deploy.sh

# Stop local network
icp network stop

Local Validation Workflow

Git hooks run from .githooks:

  • pre-commit -> ./scripts/validate.sh fast --staged
  • pre-push -> ./scripts/validate.sh full

Validation modes:

# quick local gate (format/lint + changed-scope tests)
./scripts/validate.sh fast --staged

# full local gate (candid, rust tests, sdk tests, frontend test+build)
./scripts/validate.sh full

# dependency/security audits
./scripts/validate.sh security

# release gate (full + security + coverage + mutation tests)
./scripts/validate.sh release

PocketIC stability note:

  • scripts/validate.sh starts one shared PocketIC server per run and reuses it across integration targets.
  • To force tests to use an already-running server, set PAW_POCKET_IC_SERVER_URL (or POCKET_IC_SERVER_URL) to http://127.0.0.1:<port>.

Optional toggles:

  • PAW_PRE_PUSH_MODE=release to run the release gate during pre-push.
  • PAW_PRE_PUSH_RUN_IGNORED_E2E=1 to include ignored real x402 e2e in pre-push.
  • PAW_STRICT_OPTIONAL_TOOLS=1 to fail if optional local tools are missing.

Local Speed and Quality Tooling

scripts/validate.sh auto-detects and uses these tools when installed:

  • cargo-nextest for faster parallel test execution.
  • sccache for Rust compile caching (RUSTC_WRAPPER=sccache).
  • cargo-llvm-cov for coverage output (target/llvm-cov/lcov.info).
  • cargo-mutants for mutation testing on critical policy/x402 modules.

Recommended install commands:

cargo install cargo-nextest
cargo install sccache --locked
cargo install cargo-llvm-cov
cargo install cargo-mutants
cargo install cargo-audit
cargo install cargo-deny

For continuous local loops, use either:

cargo watch -x 'fmt --all -- --check' -x 'clippy --all-targets --all-features -- -D warnings' -x 'test --lib --all-features'
bacon clippy

Local Deploy and Init Args

The backend canister has init(args: InitArgs).

icp deploy (all canisters) does not expose per-canister args on the command line in icp-cli 0.1, so use:

./scripts/deploy.sh

The script behavior is:

  • creates canisters if missing
  • deploys frontend
  • installs backend with InitArgs on first install
  • upgrades backend with profile-specific post_upgrade args
  • applies explicit deploy profiles:
    • local: management canister signing, key dfx_test_key, signup-gate disabled
    • playground: chain-key testing canister signing (vrqyr-saaaa-aaaan-qzn4q-cai), key insecure_test_key_1, signup-gate disabled
    • production: management canister signing, key key_1, signup-gate enabled

Recommended reproducible setup is to store backend init args in a checked-in Candid file:

(
  record {
    config = opt record {
      require_signup_invite_code = opt false;
      ecdsa_key_name = opt "dfx_test_key";
      signing_backend = opt variant { ManagementCanister };
    };
  }
)

Then deploy with:

./scripts/deploy.sh --init-args-file ./deploy/backend-init.args.did.example

Deploy directly to public playground-like environments with:

./scripts/deploy.sh --environment playground

Note: --environment playground uses dfx --playground under the hood for borrowed canisters (no pre-minted cycles required for create). Non-playground environments continue to use icp-cli. The script writes a temporary dfx.json in the project root during this path and restores/removes it on exit. For playground frontend builds, it injects VITE_BACKEND_CANISTER_ID from the reserved backend canister.

Deploy with explicit production profile:

./scripts/deploy.sh --environment ic --profile production

Candid Generation

The Candid interface is exported from Rust (ic_cdk::export_candid!()) and extracted from the compiled Wasm.

Use:

./scripts/generate-candid.sh backend paw.did

Do not hand-edit paw.did.

About

Policy-controlled agent wallet with human approvals, versioned policies, replay protection, and audit trails

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages