Skip to content

Latest commit

 

History

History
355 lines (278 loc) · 17.8 KB

File metadata and controls

355 lines (278 loc) · 17.8 KB

AGENTS.md - Guardian

This file is the operational guide for coding agents working in this repository. It is optimized for safe, cross-layer changes in a multi-language codebase.

1) System Shape (Read First)

Work from the bottom up:

  1. crates/server (core system of record — gRPC, HTTP REST, operator dashboard, optional EVM)
  2. Base clients (one per server surface)
    • crates/client + packages/guardian-client (Rust gRPC + TS HTTP REST for the per-account API)
    • packages/guardian-operator-client (TS HTTP client for the operator dashboard surface)
    • packages/guardian-evm-client (TS HTTP client for the feature-gated EVM surface)
  3. Higher-level SDKs
    • crates/miden-multisig-client + packages/miden-multisig-client (multisig flow on top of base clients)
  4. examples/ (verification and debugging surfaces)
    • examples/demo = CLI/TUI multisig flow (Rust)
    • examples/web = browser multisig flow (TS)
    • examples/rust = low-level Rust integration examples
    • examples/smoke-web = TS multisig smoke harness
    • examples/operator-smoke-web = operator dashboard auth + account-API smoke harness
    • examples/evm-smoke-web = EVM proposal lifecycle smoke harness

If a behavior changes in a lower layer, verify and propagate impact upward across every relevant base client.

2) Repo Map

  • crates/server: GUARDIAN server — gRPC + HTTP + /dashboard/* + feature-gated /evm/*, storage, metadata, auth, canonicalization jobs
  • crates/client: Rust gRPC client SDK (per-account API)
  • packages/guardian-client: TS HTTP REST client SDK (per-account API)
  • packages/guardian-operator-client: TS HTTP client for the operator dashboard surface (/dashboard/*); session-cookie auth, permission vocabulary
  • packages/guardian-evm-client: TS HTTP client for the feature-gated EVM surface (/evm/*)
  • crates/miden-multisig-client: Rust multisig SDK on top of Miden + GUARDIAN
  • packages/miden-multisig-client: TS multisig SDK on top of Miden + GUARDIAN
  • crates/shared: shared Rust primitives/utilities
  • spec/: system and protocol-level behavior docs
  • docs/: contributor- and operator-facing documentation hub (start at docs/CONCEPTS.md)
  • infra/: Terraform for the AWS reference deployment
  • examples/: validation apps and reference flows

3) Core Change Rules

  1. Preserve protocol compatibility unless explicitly asked to break it.
  2. Treat server contract changes as multi-package changes by default.
  3. Update tests in the layer where behavior changes, plus at least one upstream consumer.
  4. Prefer minimal, surgical edits over broad refactors.
  5. Do not introduce silent behavior drift between Rust and TypeScript clients.
  6. Do not add backward-compatibility layers, migrations, or fallback behavior unless the task explicitly requires them.

4) Contract-Change Workflow (Mandatory)

Use this when changing endpoints, payloads, status enums, signatures, or auth behavior.

  1. Update server contract source first:
    • gRPC: crates/server/proto/guardian.proto
    • HTTP shapes/serialization in server services and API modules
    • HTTP OpenAPI: any change to an HTTP handler, its request/response/query/path types, auth behavior, or a feature-gated route MUST update the #[utoipa::path] annotation and #[derive(utoipa::ToSchema)]/IntoParams derives, then regenerate the committed specs: cargo run --features evm --bin gen-openapi -- docs (CI fails on drift via cargo run --features evm --bin gen-openapi -- --check docs).
  2. Update Rust client compatibility:
    • crates/client (proto, request/response mapping, auth/signature handling)
  3. Update TS client compatibility (each affected surface):
    • packages/guardian-client/src/server-types.ts — per-account API
    • packages/guardian-operator-client — when /dashboard/* shapes, permission vocabulary, or session/cookie semantics change
    • packages/guardian-evm-client — when /evm/* shapes change
    • request/response adapters and tests in each touched package
  4. Update multisig SDK layers if proposal/state shape changed:
    • crates/miden-multisig-client
    • packages/miden-multisig-client
  5. Validate via examples (use the matching smoke harness for the surface you changed):
    • examples/demo for the Rust CLI multisig flow
    • examples/web / examples/smoke-web for the TS multisig flow
    • examples/operator-smoke-web for dashboard / operator-client changes
    • examples/evm-smoke-web for EVM proposal flow changes
  6. Run targeted tests, then broader suite.

5) Layer-Specific Guidance

Server (crates/server)

  • Keep business logic in src/services/; keep transport logic thin in src/api/.
  • Respect auth expectations: unauthenticated-only endpoints must remain explicit.
  • Document every HTTP handler with #[utoipa::path] (method, path, params, body, per-status responses, and security(...) for authenticated routes) and derive utoipa::ToSchema/IntoParams on its wire types. Regenerate docs/openapi*.json after any HTTP contract change; do not hand-edit endpoint shapes into spec/api.md.
  • Maintain storage/metadata backend parity (filesystem/postgres where applicable).
  • Preserve canonicalization semantics (pending/candidate/canonical/discarded lifecycle).
  • Default local development/test backend is filesystem unless a task explicitly requires Postgres.
  • Keep shared server layers network-agnostic. Put Miden/EVM-specific logic in src/network/*, and dispatch from services/builders via account network config rather than embedding network-specific assumptions in shared modules.
  • Secret-bearing fields (keys, tokens, credential URLs, DB/RPC URLs) must use a wrapper from src/secret/ (FixedKey<N>, SecretBytes, SecretString, CredentialUrl) — never bind a raw String/Vec<u8> to a config field or struct. Read-and-wrap in one expression. See CONTRIBUTING.md ("Secrets in server memory").

Rust GUARDIAN Client (crates/client)

  • Mirror proto/service changes quickly.
  • Keep signer/auth flow explicit and deterministic.
  • Verify ack-signature related behavior whenever push/sign flows are changed.

TS GUARDIAN Client (packages/guardian-client)

  • Keep server-types.ts aligned with real server JSON responses.
  • Keep conversion code explicit rather than permissive.
  • Validate error-shape handling (GuardianHttpError) when endpoint responses change.
  • This client is HTTP REST against :3000; it does not speak gRPC. Do not assume parity with crates/client at the transport layer.

TS Operator Client (packages/guardian-operator-client)

  • Targets the /dashboard/* surface, not the per-account API. It is a separate auth domain: challenge → Falcon-signed session → cookie, not per-request x-pubkey/x-signature headers.
  • When the server's permission vocabulary (dashboard:read, accounts:pause, policies:write) or allowlist payload shape changes, update permissions.ts and the allowlist parsing tests in the same PR.
  • Validate cookie / session expectations when changing /dashboard/auth/* shapes; the operator allowlist is hot-reloaded on every challenge and authenticated request, so don't add restart-required assumptions.
  • The matching smoke harness is examples/operator-smoke-web.

TS EVM Client (packages/guardian-evm-client)

  • Feature-gated against the server's evm build feature. Treat the client as optional in cross-layer changes — only touch it when server /evm/* shapes move.
  • The allowed chain set is derived from GUARDIAN_EVM_RPC_URLS keys on the server side; the client should not maintain its own allowlist.
  • Validate against examples/evm-smoke-web when changing proposal shapes, session auth, or finality logic.

Rust Multisig SDK (crates/miden-multisig-client)

  • Treat proposal lifecycle and threshold logic as high-risk behavior.
  • Validate online + offline flows when changing proposal or signature handling.
  • Keep execution path and imported/exported proposal path behaviorally consistent.

TS Multisig SDK (packages/miden-multisig-client)

  • Keep transaction/procedure builders stable and typed.
  • Validate external-signing flow when touching signature or signer types.
  • Ensure proposal cache/list/sync semantics remain coherent after mutations.

Examples (examples/)

  • Use examples as integration checks, not just demos.
  • If user-facing flow changes, update example docs and startup assumptions.

6) Fast Validation Matrix

Run the smallest meaningful set first, then expand:

Rust

cargo test -p guardian-server
cargo test -p guardian-client
cargo test -p miden-multisig-client
cargo test --workspace

Feature-gated server suites when relevant:

cargo test -p guardian-server --features integration
cargo test -p guardian-server --features e2e

TypeScript

cd packages/guardian-client && npm test
cd packages/guardian-operator-client && npm test
cd packages/guardian-evm-client && npm test
cd packages/miden-multisig-client && npm test

Examples (smoke/integration)

cargo run -p guardian-demo
cd examples/web && npm run dev

Manual policy:

  • Treat examples/demo and examples/web as required manual smoke checks for changes affecting server/client/multisig behavior.
  • Document in PR notes what was exercised and any skipped path with reason.

7) High-Risk Areas (Require Extra Care)

  • Auth and signature scheme handling (Falcon vs ECDSA paths)
  • Delta/proposal status transitions and canonicalization timing
  • Request/response schema drift between server and TS client
  • Multisig threshold/signature counting logic
  • Offline import/export proposal format compatibility
  • Introducing or moving secret material in crates/server (must flow through src/secret/ wrappers)

When touching any high-risk area, add or update tests before finishing.

8) Definition of Done for Agent Changes

Before finishing, confirm all are true:

  1. Architecture impact assessed bottom-up (server -> clients -> multisig -> examples).
  2. Protocol/data-shape changes reflected in every affected client surface.
    • Per-account API change → crates/client and packages/guardian-client in the same PR.
    • Dashboard /dashboard/* change → packages/guardian-operator-client in the same PR.
    • EVM /evm/* change → packages/guardian-evm-client in the same PR.
  3. Tests updated where behavior changed.
  4. At least one upstream consumer validated for changed lower-layer behavior.
  5. README/docs touched if external behavior changed.
  6. No unrelated file churn.

9) Documentation Impact Check

Do not update docs mechanically for every code edit. Do check and update the matching docs whenever a change affects user-visible behavior, public APIs, SDK methods, auth/signature behavior, configuration, deployment flow, smoke-test workflow, or example startup assumptions.

Common mappings:

  • Server or API behavior -> spec/, docs/CONCEPTS.md, SDK docs
  • Multisig SDK behavior -> docs/MULTISIG_SDK.md, examples/demo, examples/web, examples/smoke-web
  • Operator/dashboard behavior -> docs/DASHBOARD.md, docs/PRODUCTION.md, examples/operator-smoke-web
  • EVM proposal behavior -> speckit/features/001-evm-proposal-support/, packages/guardian-evm-client, examples/evm-smoke-web
  • Deployment, config, infrastructure, or secrets -> docs/PRODUCTION.md, docs/architecture/infra.md, docs/runbooks/secrets.md, docs/SERVER_AWS_DEPLOY.md, infra/README.md
  • Local dev or test startup -> README.md, CONTRIBUTING.md, relevant example README or quickstart

When docs are not updated after a visible behavior change, note why in the final report or PR notes.

10) Practical Defaults

  • Prefer rg/rg --files for discovery.
  • Keep edits ASCII unless existing file requires otherwise.
  • Keep comments minimal and only where logic is non-obvious.
  • Avoid speculative refactors during bugfixes.

11) Versioning Policy

  • Keep crate/package versions aligned with the active Miden dependency line.
  • Current baseline is Miden 0.14.x; changes must remain compatible with that line unless migration is explicit.
  • If a change requires moving to a new Miden line, treat it as a coordinated release task:
    1. Update workspace/dependency constraints.
    2. Update both multisig SDKs and both base clients as needed.
    3. Re-run cross-layer validation (including examples).
    4. Update docs and changelog/release notes to call out the dependency line change.

12) Coding Style (Multisig Focus)

Apply these rules especially to:

  • crates/miden-multisig-client
  • packages/miden-multisig-client

Function Design

  • Prefer small, single-purpose functions over long multi-step procedures.
  • Separate orchestration from transformation:
    • Orchestrators coordinate calls and side effects.
    • Helpers perform pure data transformation/validation.
  • Avoid mixing transport calls, business rules, and serialization in one function.
  • Target shape:
    • Helper functions: short and focused.
    • Long workflows should be split into named steps with explicit inputs/outputs.

Comment Style

  • Do not add inline comments (// ...) in implementation code.
  • Do not add step-by-step procedural comments (for example 1., 2., 3.) in method docs.
  • Prefer clear naming and small functions over explanatory comments.
  • If documentation is required, keep it concise, high-level, and non-procedural.

Module Boundaries

  • Organize by capability, not by file size:
    • Account lifecycle
    • Proposal lifecycle (create/sign/list/execute)
    • Signature/advice preparation
    • GUARDIAN transport adapters
    • Metadata mapping/normalization
  • Group files by concept using folders when two or more files belong to the same concern.
    • TypeScript example: multisig/proposal/parser.ts, multisig/proposal/execution.ts
    • Rust equivalent: client/proposal/parser.rs, client/proposal/execution.rs
  • Keep scheme-specific logic (Falcon/ECDSA) behind focused helpers/strategies.
  • Minimize cross-module reach; prefer narrow interfaces.

API and Type Discipline

  • Keep public APIs explicit and stable unless change is intentional.
  • Prefer typed structures/enums over ad-hoc maps or stringly-typed branching.
  • Prefer mandatory fields in domain and request-facing types over optional ones.
  • If a transport layer forces optionality (for example protobuf message fields), validate at the boundary and convert immediately into required domain types.
  • Model proposal state transitions explicitly (pending/ready/finalized).
  • Ensure Rust and TypeScript behavior remain semantically equivalent for the same workflow.

Type-Centric Operations

  • Prefer type-driven operations over standalone procedural functions.
  • TypeScript:
    • Prefer classes/types owning parsing and execution behavior.
    • Prefer patterns like:
      • new CosignerAdviceMap(...) instead of buildCosignerAdviceMap(...)
      • proposalWorkflow.execute() instead of executeProposalWorkflow(...)
      • ProposalMetadata.fromGuardianMetadata(...) instead of fromGuardianMetadata(...)
  • Rust:
    • Apply the same principle with constructors/associated functions on domain types.
    • Prefer patterns like:
      • SignatureInputs::from_json(delta_payload_json) instead of parse_unique_signature_inputs(...)
  • When logic does not fit an existing domain type, introduce a focused service struct/enum in an appropriate module instead of scattering one-off free functions.
  • Prefer trait-based mocks at dependency boundaries over enum-level mock variants or test-only branches in shared runtime state.

Immutability (TypeScript)

  • Prefer immutable variable bindings by default.
  • Use const unless reassignment is required.
  • Use let only when mutation/rebinding is intentionally needed.

Additional Style Rules

  • Import local types/modules instead of repeating inline fully qualified paths when an alias or direct import makes ownership clear.
  • Hex/Bytes Boundary Rule:
    • Validate and normalize external commitments, pubkeys, signatures, and account IDs at boundaries before domain logic.
    • Enforce expected shape (0x prefix, canonical casing, expected length) at parse time.
  • No Implicit Crypto Conversions:
    • Do not inline ad-hoc conversion between hex/base64/bytes/felts in feature code.
    • Use dedicated codec/helper types/modules for conversions.
  • Exhaustive State Handling:
    • Use exhaustive handling for status/scheme/proposal-type enums.
    • TypeScript: use never-based exhaustiveness checks.
    • Rust: use full match coverage.
  • Deterministic Time/Nonce Sources:
    • In core logic, avoid direct Date.now() and random calls.
    • Pass clock/nonce providers so logic remains testable and deterministic.
  • No Silent Fallbacks:
    • Fallback behavior (for example online -> offline) must be explicit in API flow/return types.
    • Do not hide fallback behavior in side effects.
  • Typed Errors With Stable Codes:
    • Use structured errors with stable codes at module boundaries.
    • Avoid branching on free-form error strings in core paths.
  • TypeScript Strictness:
    • Do not use any in core modules.
    • Avoid non-null assertions (!) where a guard/narrowing can be used.
  • Cross-Language Naming Parity:
    • Keep Rust and TypeScript names/semantics aligned for equivalent workflows unless divergence is documented.

Error Handling

  • Use structured errors at boundaries; avoid losing context in generic string errors.
  • Add context at adapter edges (GUARDIAN, Miden node, serialization/parsing).
  • Fail fast on malformed signatures/metadata instead of silently coercing.

Testing Expectations for Refactors

  • Refactors must preserve behavior:
    • Add characterization tests before moving complex logic.
    • Keep or improve existing integration coverage.
  • For multisig changes:
    • Validate both Falcon and ECDSA paths.
    • Validate online and offline proposal flows.
    • Validate both examples (examples/demo, examples/web) when applicable.

Propagation Rule

  • If a public API changes in either multisig client, propagate updates to:
    • examples/demo
    • examples/web
    • any affected docs and tests in the same PR.