Abstract
Financial authorization is usually modeled through identities and roles.
A user is authenticated. A service has a role. An operator belongs to a group. A backend checks whether the caller may perform an action.
This model works until economic authority becomes distributed.
A payment orchestrator may be allowed to spend from a treasury account, but only for a specific settlement. A custody service may sign a transaction, but only within an approved amount, asset, destination, and policy window. A downstream service may receive permission to execute a withdrawal without receiving authority to create another withdrawal. A merchant may delegate refund authority to a processor while retaining the ability to revoke it. A risk engine may authorize 500,000 units of provisional exposure without granting permission to consume another 500,000 simply because the same request is replayed.
These are capability problems.
An economic capability is not merely proof that some state is true. It is bounded authority to cause a specific financial state transition.
Once financial authority is represented explicitly, an entire class of security questions becomes visible: replay, delegation, attenuation, confused-deputy attacks, capability laundering, stale authority, double consumption, issuer compromise, transitive privilege expansion, and revocation under partial failure.
This article develops a capability-security model for distributed financial infrastructure. We examine how economic authority should be scoped, delegated, attenuated, consumed, revoked, and audited without allowing downstream systems to manufacture stronger authority than they received.
The core safety requirement is simple:
Authority may move.
It must never grow accidentally.
Authentication is not authorization
Consider a withdrawal endpoint:
POST /withdrawals The request arrives with an authenticated user.
The backend checks:
user.is_authenticated == true Then:
user.balance >= amount Then it executes the withdrawal.
This looks like authorization.
It is mostly authentication plus a balance check.
The system still needs to answer:
Is this user allowed to withdraw this asset?
To this destination?
At this amount?
At this moment?
Using which source of funds?
Under which settlement state?
After which compliance decision?
Against which liquidity allocation?
How many times may the authorization be exercised?
Can another service exercise it on the user's behalf?
Can that service delegate it again?
Authentication answers:
Who is making the request? Capability security answers:
What exact economic authority does this caller possess? Those are not interchangeable questions.
Ambient authority
Many financial systems rely on ambient authority.
A service has credentials that allow it to call another service.
For example:
withdrawal-orchestrator can call custody-service The custody service trusts the caller because:
service_role = withdrawal-orchestrator This creates broad authority.
Any code executing under that service identity may now potentially request signatures.
The authorization exists because of who the caller is, not because of which specific economic operation it is authorized to perform.
This is ambient authority.
It is convenient.
It is also the foundation of many confused-deputy problems.
Explicit authority
A capability model changes the question.
Instead of:
withdrawal-orchestrator may request signatures the model becomes:
withdrawal-orchestrator possesses authority to sign exactly this withdrawal for at most this amount to this destination before this expiration under this policy The authority becomes an object.
For example:
EconomicCapability: capability_id subject action asset amount destination operation_id issued_at expires_at issuer The custody service no longer needs to trust the withdrawal orchestrator with general signing authority.
It verifies whether the supplied capability authorizes the exact requested action.
That is a substantial reduction in privilege.
Capabilities represent permission, not identity
A capability can be held by an authenticated service, but the capability itself represents authority.
This distinction is useful.
Suppose Service A receives:
Capability C: withdraw 100 USDC from account X to address Y Service A may be authenticated as:
service_A but its ability to execute the withdrawal comes from C.
If Service A calls the custody system with:
withdraw 200 USDC the request fails even though Service A is perfectly authenticated.
If it asks for:
withdraw 100 USDC to address Z the request also fails.
Identity answers who presented the capability.
The capability answers what that identity is allowed to do.
Capability scope
Economic capabilities should be narrowly scoped.
A capability may bind:
action asset amount source destination operation time window network counterparty policy For example:
action: external_withdrawal asset: USDC network: Base maximum_amount: 100000 source_account: treasury_01 destination: 0xabc... operation_id: withdrawal_8841 expires_at: 14:05:00 The more irreversible the operation, the narrower the useful scope.
A capability saying:
may transfer funds is barely better than a role.
A capability saying:
may settle operation 8841 for at most 100,000 USDC to destination 0xabc on Base before 14:05 contains enough structure to enforce economic intent.
Authority should follow intent
A useful principle is:
capability scope <= business intent scope If a customer authorized:
transfer 500 to recipient R no downstream capability should authorize:
transfer up to 1000 to arbitrary recipient That would increase authority beyond the original intent.
Every derived capability should preserve or reduce the original authority.
Never increase it.
Attenuation
Capability attenuation means deriving a weaker capability from a stronger one.
Suppose Treasury issues:
C0: spend up to 1M USDC for settlement batch B The settlement orchestrator may derive:
C1: spend up to 250k USDC for settlement operation B1 Then a transaction builder derives:
C2: spend exactly 247,812 USDC to destination D Authority becomes narrower at every step:
C2 ⊆ C1 ⊆ C0 The important invariant is:
Authority(child) subset_of Authority(parent) A child capability may restrict.
It must never expand.
Attenuation as an order relation
Let:
C1 <= C0 mean:
every action authorized by C1 is also authorized by C0 Then attenuation requires:
child <= parent This gives us a useful partial order over authority.
For example:
C0: asset ∈ {USDC, USDT} amount <= 1M destination ∈ approved_destinations C1: asset = USDC amount <= 500k destination = D1 Then:
C1 <= C0 If instead:
C2: amount <= 2M then:
C2 not<= C0 The derivation must fail.
Authority conservation
Financial systems already enforce conservation of value.
Capability systems need something analogous for authority.
Suppose a capability authorizes spending:
1M If it can generate two independent children:
C1 = 1M C2 = 1M and both can be consumed independently, the system has created 2M of spend authority from 1M.
That is capability inflation.
So attenuation alone is insufficient.
We also need conservation when authority represents consumable capacity.
Splittable capabilities
Suppose:
Parent capacity = 1M The system may split it into:
Child A = 400k Child B = 600k with:
400k + 600k = 1M A useful invariant is:
sum(active_child_capacity) + consumed_parent_capacity + remaining_parent_capacity <= original_parent_capacity This turns delegation into a resource-allocation problem.
Delegation
Delegation means transferring some authority to another principal.
For example:
Treasury -> Settlement Orchestrator -> Custody Executor Treasury may delegate permission to spend from a settlement reserve.
The orchestrator may delegate only the exact amount required for one transaction.
The custody executor receives execution authority without receiving treasury-wide authority.
This is least authority expressed structurally.
Delegation chain
A delegated capability should preserve lineage.
For example:
C0 issued by Treasury -> C1 derived by Settlement Engine -> C2 delegated to Custody Each capability contains:
parent_capability_id issuer delegate constraints This creates an authority graph.
It allows the system to answer:
Where did this permission come from? and:
Which stronger authority allowed it to exist? Transitive delegation
Delegation becomes dangerous when recipients can delegate again.
Suppose:
Treasury -> Service A Treasury intends Service A to execute a payment.
But Service A can derive:
Service A -> Service B Then Service B derives:
Service B -> Service C Eventually authority reaches code Treasury never intended to trust.
Capability models should therefore include delegation constraints.
For example:
delegation_depth = 0 means:
holder may exercise holder may not delegate Or:
delegation_depth <= 2 limits propagation.
Delegation rights are themselves authority
A capability may distinguish:
use delegate attenuate split revoke These are different permissions.
Someone allowed to exercise a capability does not automatically need permission to mint child capabilities.
For example:
Custody Executor: may_use = true may_delegate = false This reduces authority propagation.
The confused deputy
The confused-deputy problem appears when a privileged service performs an action using its own authority on behalf of a less-privileged caller without correctly binding the action to the caller's permission.
Financial systems are full of potential deputies.
Consider a custody service.
It has broad authority over treasury keys.
A withdrawal service sends:
sign transaction T If custody checks only:
caller == withdrawal-service then the withdrawal service can potentially make custody sign any transaction accepted by that interface.
Custody has become a deputy using its own authority.
The correct design requires the withdrawal service to present a capability proving:
transaction T is specifically authorized Custody should not spend its own ambient authority merely because the caller has the right service identity.
Confused-deputy example
Suppose Service A is allowed to request refunds.
Service B owns treasury execution.
A request arrives:
refund customer 500 Service A sends to B:
transfer 500 to destination X If B trusts A generally, an attacker compromising A may change X.
A proof-carrying capability might instead bind:
refund_id amount asset destination original_payment Service B verifies that the requested transfer exactly matches the authorized refund.
Now A can orchestrate.
It cannot redefine the economic intent.
Capability laundering
Another failure mode occurs when restricted authority passes through a subsystem and emerges as unrestricted authority.
Suppose:
C: may transfer 100 USDC to destination A Service X consumes this capability and produces:
authorization_status = approved Service Y later interprets:
approved == arbitrary transfer allowed The destination restriction disappeared.
Authority was laundered through a lossy representation.
This is the capability equivalent of collapsing settlement semantics into status=completed.
Semantic preservation across boundaries
Every service boundary should preserve relevant authorization constraints.
If input authority says:
asset = USDC amount <= 100 destination = A the next layer cannot reduce it to:
authorized = true unless that boolean is scoped to a specific immutable operation already containing those constraints.
Otherwise downstream code receives more semantic freedom than upstream policy intended.
Bind authority to operation identity
A strong capability should normally reference:
business_operation_id For example:
operation_id = withdrawal_8841 Now the capability cannot authorize another withdrawal even if amount, asset, and destination happen to match.
This protects against authority reuse across business intents.
Replay attacks
Suppose a capability authorizes:
withdraw 500 USDC The operation succeeds.
An attacker submits the same capability again.
If signature verification is the only check, the capability remains perfectly authentic.
The second request may also succeed.
Cryptographic validity does not imply freshness.
Replay prevention must be explicit.
Nonces
A capability may contain a unique nonce:
nonce = 8f12... The execution boundary records:
nonce consumed A repeated capability fails.
Conceptually:
if consumed(nonce): reject replay else: atomically mark consumed execute The word atomically is doing serious work here.
Check-then-act is unsafe
This is incorrect:
1. query nonce table 2. nonce unused 3. execute withdrawal 4. mark nonce used Two concurrent requests can both observe:
unused and both execute.
The consumption must be part of the authoritative state transition.
For example:
BEGIN INSERT consumed_nonce IF NOT EXISTS reserve funds create withdrawal COMMIT If another transaction tries the same nonce, it fails.
Idempotency and replay are related but different
Idempotency answers:
If the same business request arrives again, can we return the original result? Replay protection answers:
Can the same authority create another effect? Suppose a withdrawal succeeds but the caller times out.
It retries.
The system should not respond:
replay attack if the retry refers to the same operation.
It should return:
withdrawal already executed Therefore the capability should bind:
operation_id and the execution service should preserve the operation result.
Stable economic identity
A useful model:
operation_id: identifies economic intent capability_id: identifies authority attempt_id: identifies execution attempt A retry may create a new attempt.
It should not create a new economic operation.
Nor should it consume the capability twice.
One-shot capabilities
Some authority should be exercised exactly once.
For example:
execute withdrawal W The state machine is:
Issued -> Consumed with optional:
Issued -> Revoked Once consumed:
Consumed is terminal The capability remains historically valid as evidence.
It no longer grants authority.
Metered capabilities
Other capabilities represent divisible capacity.
Example:
may settle up to 1M today This is not one-shot.
It is metered.
The system must track:
authorized_amount consumed_amount remaining_amount Invariant:
consumed_amount <= authorized_amount Each consumption must be atomic.
Partial consumption
Suppose:
capability capacity = 1M One operation consumes:
300k Remaining:
700k Another consumes:
500k Remaining:
200k A request for:
300k must fail.
This sounds trivial.
It stops being trivial when consumption is distributed across services.
Central consumption authority
One solution is to maintain a single authoritative capability ledger.
Every consumer reserves capability capacity through that ledger.
For example:
CapabilityLedger: authorized: 1M reserved: 200k consumed: 600k remaining: 200k This prevents double consumption.
The tradeoff is coordination.
Distributed capability spending
If several services must consume the same capability without central coordination, correctness becomes much harder.
Each service may believe capacity remains.
This is essentially double spending of authority.
Financial capability systems should be very cautious about distributing mutable consumable authority without a clear ownership protocol.
One clean solution is to split capability capacity into non-overlapping child capabilities before distribution.
Instead of:
shared capability = 1M issue:
Service A capability = 400k Service B capability = 600k Now consumption domains are isolated.
Capability partitioning
This mirrors financial reserve segmentation.
Shared authority maximizes flexibility.
Partitioned authority improves fault isolation.
If Service A is compromised, the maximum spend is:
400k not:
1M Capability allocation therefore becomes part of blast-radius engineering.
Capability amplification
A dangerous derivation bug may accidentally create stronger authority.
Suppose:
Parent: amount <= 100 A child derivation function mistakenly interprets missing amount as:
unlimited Then:
attenuate(parent, child_constraints) has actually amplified authority.
This is why constraints should not rely on ambiguous omission semantics.
Missing should usually mean:
inherit parent constraint not:
remove constraint Deny by construction
A safe attenuation API should make expansion impossible.
For example:
child.amount = min(parent.amount, requested_amount) not:
child.amount = requested_amount Similarly:
child.expiry = min(parent.expiry, requested_expiry) Destination sets should satisfy:
child.destinations subset_of parent.destinations Asset sets:
child.assets subset_of parent.assets Formal attenuation property
Let Perm(C) be the set of actions capability C permits.
Then:
Attenuate(C, r) = C' must satisfy:
Perm(C') subset_of Perm(C) For all valid attenuation requests r.
This is a useful property for formal verification.
It is much stronger than unit-testing a few examples.
Capability composition
Sometimes an operation requires several independent capabilities.
A treasury transfer may require:
funding capability + liquidity capability + compliance capability + custody capability The final operation is valid only if every required authority exists.
Conceptually:
Execute(T) requires C_funds AND C_liquidity AND C_compliance AND C_custody This limits single-service authority.
Composition should intersect authority
When several capabilities combine, the result should generally reflect the intersection of their permissions.
Suppose:
Funds: max 1M Liquidity: max 700k Compliance: max 500k The operation cannot be:
1M The effective limit is:
500k because all constraints must hold.
Threshold authorization
Some capabilities represent authority from independent parties.
For example:
3 treasury signers 2 required or:
Risk approval Treasury approval Custody approval This is different from attenuation.
It is threshold composition.
A high-value operation may require:
k-of-n authorities before execution.
This reduces single-point compromise.
Threshold is not automatically safe
Suppose three approval services exist.
All three depend on the same administrator account.
Nominally:
3 independent approvals Operationally:
1 shared compromise domain Authority independence has the same hidden-correlation problem we saw with liquidity and guarantees.
Security topology matters.
Context binding
A capability should often bind environmental context.
For example:
network = Base chain_id = 8453 contract = X Without this, a signature intended for one context may be replayed elsewhere.
This is particularly relevant in blockchain systems.
The economic intent should include enough domain separation to prevent cross-context reuse.
Asset identity
Never bind authority merely to:
symbol = USDC Symbols are presentation metadata.
A capability should bind to canonical asset identity.
For example:
network contract_address asset_namespace Otherwise a token sharing the same symbol could satisfy the capability superficially.
Economic authorization must bind to the actual asset.
Destination identity
Likewise, destination should be canonicalized.
Possible forms include:
bank_account_id wallet_address merchant_id beneficiary_id The capability should bind to the destination identifier used by execution.
If policy approved beneficiary B, execution should not reinterpret a free-text alias later.
Amount semantics
Amount should include:
asset minor-unit scale rounding rules fee semantics Suppose capability authorizes:
100 USDC Does that include network fee?
Can execution send:
100 + fee from treasury?
Can fee volatility increase spend beyond authorized amount?
Capabilities need explicit fee policy.
For example:
principal <= 100 fee <= 2 total <= 102 Otherwise fee handling becomes authority expansion.
Time authority
Capabilities often have expiration:
expires_at But expiration depends on clock semantics.
Which clock?
Execution node clock?
Database clock?
Issuer clock?
Distributed clock skew can create inconsistent decisions.
For high-value authority, expiration validation should use a trusted time source or bounded clock skew.
The system should know the maximum tolerated skew.
Not-before constraints
A capability may also have:
not_before This prevents premature execution.
Useful for scheduled settlements, withdrawal windows, or delayed releases.
Again:
not_before <= execution_time <= expires_at must use clearly defined time semantics.
Revocation
Expiration handles planned authority lifetime.
Revocation handles authority that must end early.
Reasons include:
settlement reversed customer account frozen destination compromised policy changed issuer compromised operator mistake A capability model without revocation assumes every issued permission remains safe until expiry.
That assumption gets uncomfortable quickly around money.
Revocation models
One option is online validation.
Every execution asks:
is capability still valid? This gives fast revocation.
It also creates a dependency on the revocation service.
If it is unavailable, the system must choose fail-open or fail-closed.
For financial authority, fail-open can be an expensive personality trait.
Short-lived capabilities
Another strategy is issuing capabilities with very short lifetimes.
For example:
expires in 30 seconds Revocation becomes less critical because authority naturally decays quickly.
This works well when capabilities can be regenerated cheaply.
It works poorly for offline or long-running workflows.
Revocation epochs
A subject may carry a revocation epoch.
For example:
account_authority_epoch = 17 The capability contains:
epoch = 17 If the account is frozen:
account_authority_epoch = 18 All older capabilities become invalid.
This revokes an entire class of authority efficiently.
Epoch granularity matters
A global epoch revokes everything.
That may be too broad.
A system may maintain epochs for:
account asset action class provider policy For example:
withdrawal_epoch can revoke withdrawals without invalidating internal transfers.
This is more precise.
Revocation under partition
Suppose a capability was issued.
Then the revocation service becomes unreachable.
A downstream executor cannot know whether the capability was revoked.
This is a partial-observability problem again.
The system needs a policy.
Possible behavior:
high-risk action: fail closed low-risk action: allow if capability is very fresh emergency mode: require secondary approval Revocation semantics should be designed before the network partition, not improvised during it.
Offline capability risk
The more independently a capability can be verified, the harder immediate revocation becomes.
This is a fundamental tradeoff.
Offline verification improves availability.
Online revocation improves control.
There is no magic configuration that maximizes both.
The system must choose based on consequence.
Capability freshness
A capability can be authentic and unrevoked but still based on stale state.
Suppose:
capability: withdraw 100k issued when balance was:
150k Another operation consumes:
100k The capability is still authentic.
It is now economically stale.
This is why capabilities should either reserve capacity or bind to state versions.
Reservation-backed capability
A stronger capability is issued only after reserving the underlying economic resource.
For example:
reserve 100k issue capability referencing reservation Now another operation cannot consume the same funds.
The capability carries exclusive authority over reserved capacity.
This is much safer than issuing a statement about unreserved balance.
Capability-backed escrow
For larger workflows, authority may map to escrowed value.
For example:
SettlementCapability: escrow_account reserved_amount destination Execution spends only from escrow.
This isolates authority from changes elsewhere in the account.
The cost is reduced liquidity flexibility.
Again, resilience versus efficiency.
Capability laundering through queues
Messaging systems create another subtle problem.
Suppose Service A validates capability C and enqueues:
execute withdrawal W Service B later consumes the queue message.
If the queue message contains only:
withdrawal_id the proof context is lost.
Service B trusts that A must have validated correctly.
That is ambient authority reintroduced through messaging.
The queued command should carry:
capability reference proof context authorization decision identity or reference an immutable authorized operation record.
Authority at rest
Once an authorized operation is persisted, the database row itself may become the capability.
For example:
Withdrawal: id status = AUTHORIZED Any worker that can transition it may now execute economic authority.
This is not necessarily wrong.
But authorization state becomes security-critical.
The system must control who can create AUTHORIZED records and under what proof.
Database privilege escalation
Suppose an internal tool allows operators to edit withdrawal state.
An operator changes:
PENDING_REVIEW to:
AUTHORIZED The worker sees the row and executes.
The operator has effectively minted a financial capability through database mutation.
This is why economically meaningful state transitions should happen through domain operations, not arbitrary row editing.
Manual authority
Operators legitimately need emergency powers.
Those powers should be explicit.
For example:
ManualOverrideCapability: operator operation action amount reason second_approver expires_at Emergency access is still capability-based authority.
It is simply issued under a different policy.
Break-glass capabilities
A break-glass capability may allow emergency action outside normal policy.
It should have stronger audit requirements:
short lifetime high visibility mandatory reason multi-party approval automatic post-incident review The important principle is that emergency authority becomes more observable as it becomes more powerful.
Not less.
Capability graphs
Once delegation exists, authority forms a graph.
For example:
Treasury Root Authority | v Settlement Batch Capability | +------> Payment A | +------> Payment B | +------> Payment C Or:
Customer Intent | v Withdrawal Authorization | v Custody Signing Capability | v Chain Execution Capability This graph is useful for security analysis.
Authority blast radius
If capability C is compromised, the graph tells us:
which descendant authorities remain usable? A broad capability near the root has large blast radius.
A narrow leaf capability has small blast radius.
This gives us an architectural metric:
authority blast radius not merely permission count.
Dominators in authority graphs
Suppose every high-value withdrawal capability descends from one treasury capability.
That capability dominates the authority graph.
Compromising it compromises the whole path.
Graph analysis can identify such concentration.
This is analogous to shared failure domains in economic contagion.
Security authority also has concentration risk.
Capability concentration
A platform may have thousands of microservices but one credential capable of:
sign any treasury transaction Operational architecture looks distributed.
Authority architecture is centralized.
That is a meaningful security mismatch.
Least authority
The principle of least privilege becomes sharper as least authority.
A service should receive:
only the economic capability required for the current operation for the shortest useful period over the smallest useful amount This is more concrete than assigning generic roles.
Privilege escalation
Privilege escalation occurs when an actor obtains authority stronger than intended.
In capability systems this may happen through:
bad attenuation scope confusion claim substitution capability replay delegation bugs issuer compromise context confusion Each path should be threat-modeled explicitly.
Claim substitution
Suppose the system expects a capability proving:
withdrawal_authorized but accepts any signed claim from the same issuer.
An attacker presents:
internal_transfer_authorized If claim type is not bound into verification, signature validity may pass.
The wrong semantic claim gets substituted.
Every capability signature must cover the complete canonical object, including action type.
Audience restriction
A capability intended for Custody Service should not necessarily be usable by another service.
Bind:
audience = custody-service Then another consumer rejects it.
This prevents authority from being replayed across service boundaries.
Purpose restriction
Audience identifies who may consume the capability.
Purpose identifies why.
For example:
purpose = settlement_execut...