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.
Work from the bottom up:
crates/server(core system of record — gRPC, HTTP REST, operator dashboard, optional EVM)- 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)
- Higher-level SDKs
crates/miden-multisig-client+packages/miden-multisig-client(multisig flow on top of base clients)
examples/(verification and debugging surfaces)examples/demo= CLI/TUI multisig flow (Rust)examples/web= browser multisig flow (TS)examples/rust= low-level Rust integration examplesexamples/smoke-web= TS multisig smoke harnessexamples/operator-smoke-web= operator dashboard auth + account-API smoke harnessexamples/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.
crates/server: GUARDIAN server — gRPC + HTTP +/dashboard/*+ feature-gated/evm/*, storage, metadata, auth, canonicalization jobscrates/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 vocabularypackages/guardian-evm-client: TS HTTP client for the feature-gated EVM surface (/evm/*)crates/miden-multisig-client: Rust multisig SDK on top of Miden + GUARDIANpackages/miden-multisig-client: TS multisig SDK on top of Miden + GUARDIANcrates/shared: shared Rust primitives/utilitiesspec/: system and protocol-level behavior docsdocs/: contributor- and operator-facing documentation hub (start atdocs/CONCEPTS.md)infra/: Terraform for the AWS reference deploymentexamples/: validation apps and reference flows
- Preserve protocol compatibility unless explicitly asked to break it.
- Treat server contract changes as multi-package changes by default.
- Update tests in the layer where behavior changes, plus at least one upstream consumer.
- Prefer minimal, surgical edits over broad refactors.
- Do not introduce silent behavior drift between Rust and TypeScript clients.
- Do not add backward-compatibility layers, migrations, or fallback behavior unless the task explicitly requires them.
Use this when changing endpoints, payloads, status enums, signatures, or auth behavior.
- 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)]/IntoParamsderives, then regenerate the committed specs:cargo run --features evm --bin gen-openapi -- docs(CI fails on drift viacargo run --features evm --bin gen-openapi -- --check docs).
- gRPC:
- Update Rust client compatibility:
crates/client(proto, request/response mapping, auth/signature handling)
- Update TS client compatibility (each affected surface):
packages/guardian-client/src/server-types.ts— per-account APIpackages/guardian-operator-client— when/dashboard/*shapes, permission vocabulary, or session/cookie semantics changepackages/guardian-evm-client— when/evm/*shapes change- request/response adapters and tests in each touched package
- Update multisig SDK layers if proposal/state shape changed:
crates/miden-multisig-clientpackages/miden-multisig-client
- Validate via examples (use the matching smoke harness for the surface you changed):
examples/demofor the Rust CLI multisig flowexamples/web/examples/smoke-webfor the TS multisig flowexamples/operator-smoke-webfor dashboard / operator-client changesexamples/evm-smoke-webfor EVM proposal flow changes
- Run targeted tests, then broader suite.
- Keep business logic in
src/services/; keep transport logic thin insrc/api/. - Respect auth expectations: unauthenticated-only endpoints must remain explicit.
- Document every HTTP handler with
#[utoipa::path](method, path, params, body, per-status responses, andsecurity(...)for authenticated routes) and deriveutoipa::ToSchema/IntoParamson its wire types. Regeneratedocs/openapi*.jsonafter any HTTP contract change; do not hand-edit endpoint shapes intospec/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
filesystemunless 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 rawString/Vec<u8>to a config field or struct. Read-and-wrap in one expression. SeeCONTRIBUTING.md("Secrets in server memory").
- Mirror proto/service changes quickly.
- Keep signer/auth flow explicit and deterministic.
- Verify ack-signature related behavior whenever push/sign flows are changed.
- Keep
server-types.tsaligned 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 withcrates/clientat the transport layer.
- Targets the
/dashboard/*surface, not the per-account API. It is a separate auth domain: challenge → Falcon-signed session → cookie, not per-requestx-pubkey/x-signatureheaders. - When the server's permission vocabulary (
dashboard:read,accounts:pause,policies:write) or allowlist payload shape changes, updatepermissions.tsand 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.
- Feature-gated against the server's
evmbuild 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_URLSkeys on the server side; the client should not maintain its own allowlist. - Validate against
examples/evm-smoke-webwhen changing proposal shapes, session auth, or finality logic.
- 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.
- 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.
- Use examples as integration checks, not just demos.
- If user-facing flow changes, update example docs and startup assumptions.
Run the smallest meaningful set first, then expand:
cargo test -p guardian-server
cargo test -p guardian-client
cargo test -p miden-multisig-client
cargo test --workspaceFeature-gated server suites when relevant:
cargo test -p guardian-server --features integration
cargo test -p guardian-server --features e2ecd 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 testcargo run -p guardian-demo
cd examples/web && npm run devManual policy:
- Treat
examples/demoandexamples/webas required manual smoke checks for changes affecting server/client/multisig behavior. - Document in PR notes what was exercised and any skipped path with reason.
- 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 throughsrc/secret/wrappers)
When touching any high-risk area, add or update tests before finishing.
Before finishing, confirm all are true:
- Architecture impact assessed bottom-up (server -> clients -> multisig -> examples).
- Protocol/data-shape changes reflected in every affected client surface.
- Per-account API change →
crates/clientandpackages/guardian-clientin the same PR. - Dashboard
/dashboard/*change →packages/guardian-operator-clientin the same PR. - EVM
/evm/*change →packages/guardian-evm-clientin the same PR.
- Per-account API change →
- Tests updated where behavior changed.
- At least one upstream consumer validated for changed lower-layer behavior.
- README/docs touched if external behavior changed.
- No unrelated file churn.
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.
- Prefer
rg/rg --filesfor discovery. - Keep edits ASCII unless existing file requires otherwise.
- Keep comments minimal and only where logic is non-obvious.
- Avoid speculative refactors during bugfixes.
- 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:
- Update workspace/dependency constraints.
- Update both multisig SDKs and both base clients as needed.
- Re-run cross-layer validation (including examples).
- Update docs and changelog/release notes to call out the dependency line change.
Apply these rules especially to:
crates/miden-multisig-clientpackages/miden-multisig-client
- 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.
- 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.
- 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
- TypeScript example:
- Keep scheme-specific logic (Falcon/ECDSA) behind focused helpers/strategies.
- Minimize cross-module reach; prefer narrow interfaces.
- 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.
- Prefer type-driven operations over standalone procedural functions.
- TypeScript:
- Prefer classes/types owning parsing and execution behavior.
- Prefer patterns like:
new CosignerAdviceMap(...)instead ofbuildCosignerAdviceMap(...)proposalWorkflow.execute()instead ofexecuteProposalWorkflow(...)ProposalMetadata.fromGuardianMetadata(...)instead offromGuardianMetadata(...)
- Rust:
- Apply the same principle with constructors/associated functions on domain types.
- Prefer patterns like:
SignatureInputs::from_json(delta_payload_json)instead ofparse_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.
- Prefer immutable variable bindings by default.
- Use
constunless reassignment is required. - Use
letonly when mutation/rebinding is intentionally needed.
- 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 (
0xprefix, 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
matchcoverage.
- Deterministic Time/Nonce Sources:
- In core logic, avoid direct
Date.now()and random calls. - Pass clock/nonce providers so logic remains testable and deterministic.
- In core logic, avoid direct
- 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
anyin core modules. - Avoid non-null assertions (
!) where a guard/narrowing can be used.
- Do not use
- Cross-Language Naming Parity:
- Keep Rust and TypeScript names/semantics aligned for equivalent workflows unless divergence is documented.
- 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.
- 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.
- If a public API changes in either multisig client, propagate updates to:
examples/demoexamples/web- any affected docs and tests in the same PR.