Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Guardian Client
A minimal Rust client library for interacting with the Guardian gRPC service.
API Reference
Client Creation
use Arc;
use SecretKey;
use ;
// Without authentication (only for configure endpoint)
let client = connect.await?;
// With request signing (required for all other endpoints)
let secret_key = new;
let signer = new;
let client = connect
.await?
.with_signer;
Abandoning a Stuck Candidate
If an approved transaction dies client-side after guardian approval (RPC
submit failure, prover timeout, crash), its candidate delta keeps the
account locked — new proposals are answered 409 conflict_pending_delta —
until the server's grace period and retry budget run out. Only the client
knows the transaction will never land:
// Records an abandon intent; the guardian's worker confirms over a short
// quarantine that the tx did not land, then releases the account.
let response = client.abandon_candidate.await?;
assert_eq!;
// Poll the delta for the resolution: still `candidate` -> waiting,
// `canonical` -> the tx landed after all, `discarded` with reason
// `client_abandoned` -> the account is released.
let delta = client.get_delta.await?;
Canonical Nonce
Nonce and commitment of the latest canonical state, without the state blob
(issue #191); the server answers from the nonce and commitment it stores with
the state. The multisig SDK's sync runs this check before get_state. The
full state fetch can be skipped when Guardian's nonce is below the local
account's nonce, or equal to it with the same commitment. An equal nonce at a
different commitment means the local account has diverged from Guardian, so
fetch the state in that case too.
let head = client.get_canonical_nonce.await?;
let in_sync = head.nonce < local_nonce
|| ;
if !in_sync
Delta History
Paginated canonical delta history (issue #413), newest-first by nonce, with
server-decoded note summaries. Pass the previous page's next_cursor to
resume; None on the response means the feed is exhausted. Only canonical
deltas appear, and only transactions pushed through Guardian are visible.
let mut cursor: = None;
loop
Rate Limits and Retries
The server rate-limits both its gRPC and HTTP surfaces. The sustained
per-minute limit is keyed per IP alone, so gRPC and HTTP calls from one
client draw on the same allowance; the burst limit is keyed per IP and
method. An over-budget call fails with ResourceExhausted, code
rate_limit_exceeded, and a backoff hint. ClientError classifies it:
is_retryable() reads the error envelope (falling back to the status-code
class), and retry_after() returns the server's hint, preferring the
retry-after status metadata over the envelope value.
Rate-limit rejections happen before the server touches any state, so retrying them is always safe. The client does not retry rate limits automatically (automatic backoff is tracked in #360); a bounded loop over the exposed hint is a few lines:
let max_attempts = 3;
let mut attempts = 0;
let state = loop ;
Retries are idempotent (the original request timestamp is preserved). The
server refuses with GUARDIAN_CANDIDATE_LANDED when the transaction
demonstrably landed.
Replay-protection retries
Signed requests carry a strictly increasing per-instance timestamp
(max(now_ms, previous + 1)). When a correctly signed request still loses
the server's per-signer replay check (stable code authentication_replay,
typically two in-flight requests landing out of order), the client retries
automatically, up to 2 times with a 50ms backoff, minting a fresh timestamp
and signature over the identical payload each attempt.
ClientError::is_replay_rejection() exposes the classification. Terminal
authentication failures (authentication_failed: clock outside the skew
window, invalid or unauthorized signature) are never retried.
The client retries only authentication_replay; it never retries
authentication_failed. During a mixed server/client rollout, a replay CAS
reported under the older authentication code—or received by a client without
replay-specific retry handling—can therefore surface as a terminal error until
both sides use the same error contract.
Authentication
The client uses Falcon Poseidon2 signatures for authenticated requests. Here is how to set it up:
1. Create a Signer
use Arc;
use SecretKey;
use FalconKeyStore;
// Generate a new secret key
let secret_key = new;
let signer = new;
// Get the public key for authorization
let pubkey_hex = signer.public_key_hex;
2. Configure Client with Signer
let client = connect
.await?
.with_signer;
3. Set Up Account Authorization
use auth;
// Add the public key to the account's authorized cosigners
let auth_config = miden_falcon_rpo_auth;
4. Sign Requests With a Session (optional)
When the Guardian accepts sessions (it advertises sessions on HTTP
GET /status; an older Guardian answers CreateSession as unimplemented),
the wallet can sign one session grant and let a delegated P-256 key sign the
session-eligible requests (state and delta reads, proposal
list/get/create/sign):
use Duration;
use StartSessionOptions;
let info = client
.start_session
.await?;
// ...get_state, get_delta_since, push_delta_proposal: signed by the session key.
client.revoke_session.await?; // this session only, signed by the session key
client.revoke_all_sessions.await?; // every session of the wallet, wallet-signed
Every other route, including configure, push_delta, abandon and account
lookup, keeps using the wallet signer. The session is used only while the
wallet that granted it is the client's signer. When Guardian answers a
session-signed request with session_expired, session_revoked or
authentication_failed, the client drops the session and the next request is
wallet-signed; start a new session then (session() returns None).
authorization_failed (the wallet does not cosign that account) leaves the
session in place. The grant is signed by the configured signer, which this
client holds in process, so there is no confirmation step.
revoke_all_sessions ends the sessions already registered with Guardian. A
grant that was signed but not yet registered can still be registered for up
to about 10 minutes, so when a key may be compromised, call it again 10
minutes later.
Server Signature Verification
After pushing a delta, the server returns an Acknowledgment signature that signs the new commitment. You should verify this signature to ensure the server is signing with the expected public key.
use verify_commitment_signature;
let push_response = client.push_delta.await?;
if let Some = &push_response.delta
The server signs the new_commitment (the resulting commitment after applying the delta) to provide cryptographic proof that it processed the delta correctly.
Example