A Signed Mandate Is Not Authorization: A Rust SDK for AI-Agent Transactions
A Signed Mandate Is Not Authorization: A Rust SDK for AI-Agent Transactions An AI agent submits a $50 transfer request. Its mandate has a valid Ed25519 signature. The signed scope permits transfers up to $100 per tran
A Signed Mandate Is Not Authorization: A Rust SDK for AI-Agent Transactions
An AI agent submits a $50 transfer request.
Its mandate has a valid Ed25519 signature. The signed scope permits transfers up to $100 per transaction. The requested amount is below that limit.
Would you authorize the payment?
Not yet.
The signature and the amount check establish useful facts. Neither establishes whether the delegation has been revoked, whether the agent has already exhausted a cumulative budget, or whether the request has been processed before.
That distinction matters when designing authorization boundaries for AI agents.
What can we actually verify offline?
Consider an issuer signing a mandate that identifies a principal, an agent, permitted actions, and transaction constraints.
A local verifier can check the issuer's signature against an explicitly trusted public key. It can then evaluate a specific transaction against the verified mandate.
For example:
-
Agent:
agent:treasury-bot -
Action:
transfer - Per-transaction limit: USD 100.00
- Requested amount: USD 50.00
If the signature is valid and the request satisfies the static constraints, the system can conclude that the request is eligible for an authoritative authorization check.
It cannot conclude that the payment should be executed.
There are at least three reasons.
Revocation: An issuer can revoke a delegation after signing it. The old signature remains cryptographically valid.
Cumulative budgets: An agent may have spent its total allowance across earlier requests. A local per-transaction check cannot establish the remaining distributed balance.
Replay: A request can be validly signed and still be submitted more than once.
These are mutable facts that require an authoritative source of current state.
Encoding that boundary in Rust
This distinction is central to FintechBear KYA, an open-source Rust SDK for AI-agent delegated authority verification and transaction policy evaluation.
The SDK separates a local policy result from a final authorization decision.
A successful local evaluation produces a PolicyOutcome. It does not produce an automatic Allow.
There is deliberately no From<PolicyOutcome> for Decision conversion.
Instead, the application must go through the authorize bridge and supply an OnlineAuthorizer that can make the authoritative decision.
The trade-off is deliberate: the SDK cannot independently authorize a payment. Applications need to implement the online authority.
That is a better failure mode than silently treating an offline check as permission.
A runnable example
The repository contains a Rust demo that exercises this distinction.
git clone https://github.com/fintechbear/fintechbear-kya-suite.git
cd fintechbear-kya-suite
cargo run -p kya-examples --example demo
The example:
- Configures an explicitly trusted issuer public key.
- Creates and verifies a genuinely signed Ed25519 mandate using a test-only signing key.
- Evaluates the USD 50.00 transaction against the USD 100.00 limit.
- Rejects a mandate whose signature has been modified.
- Denies a USD 100.01 request that exceeds the signed limit.
- Sends an eligible policy outcome to an example online authorizer that cannot establish approval.
The demo reports an eligible static outcome, but that result is not authorization.
Its failure cases include:
[4] in-scope transaction is ELIGIBLE (requires_online_authorization=true).
[5a] tampered signature rejected fail-closed:
signature did not verify (VerificationSignatureInvalid).
[5b] over-limit intent denied by stage C:
reason=AmountExceedsLimit (terminal, fail-closed).
The online-authority part of the demonstration also shows that an unresolved approval requirement or an unavailable authority does not become an Allow.
These are illustrative test transactions, not real payment executions.
You can inspect the full demo source and captured output.
Why exact money matters
Authorization constraints should not depend on floating-point rounding.
FintechBear KYA uses integer minor units with explicit currency information.
For USD, 10_000 minor units represent $100.00. The example's $100.01 request exceeds that limit by exactly one cent.
That makes the boundary explicit and testable.
It does not solve cumulative spending enforcement. Per-transaction limits and distributed budgets are different problems.
What the SDK does not implement
This is an early v0.1.0 pre-release.
The open-source core provides local evidence verification, deterministic static policy evaluation, and interfaces for the later authorization and execution stages.
It does not currently ship:
- Agent proof-of-possession verification.
- A concrete online revocation service.
- Distributed cumulative-budget enforcement.
- Global replay protection.
- Payment execution enforcement.
Its AP2-style adapter is experimental; confirmed AP2 wire interoperability is not claimed.
The project has not undergone an independent third-party security audit and is not presented as a production-ready payment authorization system.
These boundaries are documented in the security limitations and threat model.
The engineering question
For a financial action initiated by an AI agent, signature verification and static policy are necessary parts of a possible design.
But where should the boundary between verified evidence and current authorization live?
FintechBear KYA puts that boundary explicitly in the API: local eligibility cannot be silently promoted to an authorization decision.
I'd be interested in how other Rust and security engineers approach this separation, particularly when the authoritative service is unavailable.
Source: https://github.com/fintechbear/fintechbear-kya-suite
Disclosure: This article was drafted with AI assistance.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.