Economic Capability Security in Distributed Financial Systems: Delegation, Replay, Attenuation, and the Confused Deputy
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 actio
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_execution
This prevents a capability issued for settlement from being repurposed for collateral transfer.
Context must be explicit.
Capability wrapping
Sometimes a service needs to transform authority into another representation.
For example:
EconomicCapability
->
blockchain transaction signature
The transformation should be one-way in authority.
The signature authorizes one concrete transaction.
It should not allow recovery of general custody authority.
This is a healthy authority collapse.
Signing as terminal attenuation
A fully specified transaction signature can be viewed as an extremely narrow capability:
execute exactly these transaction bytes
Once the transaction is fixed, many degrees of freedom disappear.
This is desirable.
The closer execution gets, the narrower authority should become.
Late binding
Sometimes destination or amount cannot be known early.
Capability design then needs bounded late binding.
For example:
destination in merchant_set
amount <= 100k
Later the orchestrator selects:
destination = merchant_42
amount = 72k
This is safe if selection remains inside the original constraint set.
Late binding should narrow authority, not reopen previously fixed fields.
Capability race conditions
Two services may try to consume the same remaining capacity simultaneously.
Suppose:
remaining = 100k
Service A wants:
70k
Service B wants:
60k
Both individually fit when read.
Together:
130k
does not.
Capability consumption requires serialization or atomic conditional updates.
For example:
UPDATE capabilities
SET consumed = consumed + 70000
WHERE
id = C
AND authorized - consumed >= 70000
Only one conflicting transaction can succeed once capacity falls below the requested amount.
Durable consumption
Capability consumption must survive crashes.
Suppose:
capability marked consumed
then the external request fails before submission.
Can it be retried?
The answer depends on state machine design.
A safer flow separates:
Reserved
Executing
Consumed
Released
For example:
Issued
->
Reserved
->
Consumed
or:
Reserved
->
Released
after safe failure.
Unknown execution outcome
The hardest case:
capability reserved
external request sent
timeout
The system does not know whether the economic action occurred.
It must not:
release capability
immediately.
That could permit duplicate execution.
The capability enters:
ExecutionUnknown
until reconciliation establishes the outcome.
Capability state therefore inherits the same unresolved semantics as settlement state.
Capability state machine
A more realistic lifecycle:
Issued
|
v
Reserved
|
v
Executing
/ \
/ \
v v
Consumed ExecutionUnknown
|
+----+----+
| |
v v
Consumed Released
And separately:
Issued -> Revoked
Reserved -> RevokedBeforeExecution
depending on policy.
Revocation after reservation
If a capability has reserved funds but has not executed, revocation may release the reservation.
But if execution may already have started, revocation cannot simply undo reality.
The system must inspect execution state.
Again:
revocation != rollback
A familiar pattern by now.
Capability ledger
Financial systems may benefit from an explicit capability ledger.
Not a financial ledger.
An authority ledger.
It records:
issued authority
delegated authority
reserved authority
consumed authority
revoked authority
expired authority
This creates an auditable history of who could have done what.
Authority reconciliation
Just as balances are reconciled, capability capacity may need reconciliation.
For a metered capability:
authorized =
remaining
+ reserved
+ consumed
+ revoked_unused
subject to the capability's semantics.
If totals do not reconcile, authority has disappeared or multiplied.
Neither is a good sign.
Capability observability
Useful metrics include:
active capability value
capability value by issuer
capability value by action
delegation depth
expired but unconsumed authority
revocation lag
execution-unknown capability value
largest authority concentration
break-glass capability usage
These expose security state in economic units.
That is often much more meaningful than counting tokens.
Value-at-authority
Suppose two credentials are compromised.
Credential A can read customer names.
Credential B can issue 50M of withdrawal capability.
Both are security incidents.
Their economic blast radius is obviously different.
Capability architecture allows direct measurement:
value_at_authority
or more precisely:
maximum economic effect currently authorized
This is useful for incident prioritization.
Issuer compromise
Suppose the capability issuer is compromised.
An attacker can create validly signed capabilities.
Verification succeeds.
This means issuer trust must be bounded.
An issuer should have its own issuance limits:
max capability value
allowed actions
allowed assets
allowed destinations
maximum lifetime
The issuer itself should not possess unlimited authority merely because it creates tokens representing authority.
Issuance budget
For example:
Availability Engine:
may issue <= 5M aggregate withdrawal capability
Treasury Engine:
may issue <= 50M settlement capability
Issuance capacity can itself be backed by reservations.
Now compromising the issuer does not automatically expose the entire platform.
Root authority
Eventually capabilities originate from some root authority.
Examples:
customer mandate
treasury mandate
contractual authorization
governance decision
custody key policy
The root should be rare and strongly protected.
Most production operations should use attenuated descendants.
The root should not circulate through ordinary services.
Capability roots and institutional authority
A financial capability is ultimately meaningful because some institution recognizes the authority behind it.
Cryptographic signatures enforce technical authenticity.
The economic system gives them meaning.
For example:
Customer signed intent
matters because the platform accepts that customer as holder of the relevant funds.
Treasury approval
matters because corporate policy gives treasury that authority.
Capability security therefore combines cryptographic structure with institutional semantics.
Formal properties
Several properties are worth stating explicitly.
Attenuation:
Perm(child) subset_of Perm(parent)
Conservation:
consumed
+ reserved
+ remaining
<= authorized
Replay safety:
one one-shot capability
cannot create more than one economic effect
Scope safety:
Execute(action, capability)
implies
action in Perm(capability)
Delegation safety:
Delegated(child, parent)
implies
Perm(child) subset_of Perm(parent)
Expiry safety:
now > expires_at
implies
not Executable(capability)
Audience safety:
consumer != audience
implies
not Executable(capability)
State binding:
required_state_version != current_state_version
implies
Revalidate
These are good candidates for model checking or formal verification.
A Rust sketch
A simplified capability type:
#[derive(Debug, Clone)]
pub struct EconomicCapability {
pub capability_id: String,
pub parent_id: Option<String>,
pub holder: String,
pub audience: String,
pub action: Action,
pub asset: AssetId,
pub max_amount: u128,
pub consumed_amount: u128,
pub source_account: String,
pub destination: DestinationConstraint,
pub operation_id: Option<String>,
pub not_before: u64,
pub expires_at: u64,
pub delegation_depth_remaining: u8,
pub nonce: [u8; 32],
pub issuer: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
InternalTransfer,
ExternalWithdrawal,
Settlement,
Refund,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AssetId {
pub namespace: String,
pub network: String,
pub identifier: String,
}
#[derive(Debug, Clone)]
pub enum DestinationConstraint {
Exact(String),
AllowList(Vec<String>),
}
The representation is not the difficult part.
The semantics are.
Attenuation function
Conceptually:
pub fn attenuate(
parent: &EconomicCapability,
requested_amount: u128,
destination: DestinationConstraint,
requested_expiry: u64,
) -> Result<EconomicCapability, CapabilityError> {
if requested_amount > parent.remaining_amount() {
return Err(CapabilityError::AuthorityExpansion);
}
if requested_expiry > parent.expires_at {
return Err(CapabilityError::AuthorityExpansion);
}
if !parent.destination.contains(&destination) {
return Err(CapabilityError::AuthorityExpansion);
}
if parent.delegation_depth_remaining == 0 {
return Err(CapabilityError::DelegationForbidden);
}
// Construct child with strictly narrower authority.
todo!()
}
The production implementation would need atomic reservation of parent capacity as part of child issuance.
Otherwise two concurrent attenuations can over-allocate the parent.
Safe derivation requires reservation
Suppose:
Parent remaining: 1M
Two concurrent requests derive:
Child A: 700k
Child B: 700k
Both read:
remaining = 1M
Both appear valid.
Together they create:
1.4M
Therefore child creation must consume or reserve parent authority atomically.
Capability derivation is itself an economic state transition.
Authority cannot be treated as metadata
This is the broader lesson.
If capability issuance can increase economic authority, it belongs in the same category as balance mutation.
It requires:
atomicity
idempotency
durability
auditability
reconciliation
Treating capabilities as disposable JWT-like metadata misses the economic consequences.
Capability systems can fail financially while remaining cryptographically correct
This is perhaps the most important security point.
Every signature can verify.
Every nonce can be unique.
Every certificate can be valid.
The system can still be economically wrong because:
capability was too broad
delegation expanded authority
capacity was double allocated
issuer had excessive authority
revocation arrived too late
context was ambiguous
Cryptographic correctness is necessary.
It is not sufficient.
Security boundary
The real security property is not:
token authentic
It is:
the resulting economic action
was inside the exact authority
intentionally granted by the system
That property spans application state, business policy, ledger state, cryptography, and distributed execution.
Capability security and proof-carrying state
The previous model gave us:
Evidence
->
Claim
Capability security extends the chain:
Evidence
->
Claim
->
Capability
->
Reservation
->
Execution
Each stage narrows the set of possible actions.
That monotonic narrowing is desirable.
As the operation approaches irreversible external execution, ambiguity should decrease.
Authority should shrink toward execution
Early in a workflow:
customer may withdraw up to 10k today
Later:
withdrawal W may transfer 2k
Later:
transaction T may send exactly 2k to address A
Finally:
signed transaction bytes X
The authority becomes progressively narrower.
This is the opposite of many service architectures, where generic upstream approval becomes increasingly ambiguous as it travels through the system.
Security under partial failure
Distributed failure complicates authority.
A capability may be:
reserved locally
executed externally
confirmation lost
The correct state is not:
unused
It is:
execution outcome unknown
The authority remains unavailable until reconciliation determines whether the action occurred.
This prevents retries from double spending authority.
Capability debt
There is also such a thing as outstanding authority.
Suppose the system issues large numbers of long-lived capabilities.
Even if none are currently used, they represent latent economic power.
This can be viewed as capability debt.
A platform may have:
10M active balances
but:
80M currently exercisable capabilities
That second number deserves attention.
Authority exposure can exceed current transaction flow.
Minimize dormant authority
A useful principle:
issue authority as late as practical
expire it as early as practical
Do not pre-issue broad financial capabilities merely because execution might happen later.
Dormant authority is attack surface.
Conclusion
Distributed financial systems do not merely authenticate actors.
They distribute economic authority.
A service may receive permission to withdraw, settle, sign, refund, release collateral, allocate liquidity, or delegate part of that authority to another component.
When this authority is implicit in roles, service identities, database fields, or generic approval booleans, its boundaries become difficult to reason about.
Capability security makes authority explicit.
A capability answers:
who may act
what action is permitted
over which value
for which operation
under which constraints
for how long
and whether that authority may be delegated
From there, several invariants become enforceable.
Authority can be attenuated but not amplified.
Consumable authority can be split but not duplicated.
One-shot authority cannot be replayed.
Delegated authority cannot exceed its parent.
A privileged deputy cannot substitute its own ambient authority for the caller's bounded permission.
Revocation removes future authority without rewriting history.
Unknown execution outcomes retain their reservation until reconciliation resolves them.
The deeper principle is that authority behaves like a scarce resource.
It can be issued.
It can be delegated.
It can be partitioned.
It can be consumed.
It can be revoked.
And, if the architecture is careless, it can be accidentally multiplied.
That gives us a useful safety property:
For every economically significant action A,
there must exist an authority chain C such that:
A is inside the scope of C,
every delegation step preserves or reduces authority,
the relevant capacity has not already been consumed,
and execution cannot produce more economic effect
than the original authority intended.
Financial systems already work hard to prevent double spending of value.
They should apply the same seriousness to double spending of authority.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes โ full credit and traffic to the original publisher.