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...
Source:https://dev.to/doomhammerhell/economic-capability-security-in-distributed-financial-systems-delegation-replay-attenuation-and-211f