Abstract
Revocation looks simple in centralized systems.
An authorization is valid. Something changes. The authorization becomes invalid.
In distributed financial infrastructure, that transition is rarely atomic.
A capability may already exist in several services. A worker may be offline. A queue may contain delayed execution commands. A signer may have cached policy state. An external transaction may have been submitted but not confirmed. A revocation event may be committed in one region while another region continues operating from stale authority.
The financial question is therefore not merely:
Was the capability revoked?
It is:
Which copies of that authority may still exist,
which executions may already have started,
which effects remain preventable,
and which effects now require compensation?
This article examines revocation as a distributed state transition under partial failure. We explore revocation epochs, bounded staleness, offline verification, execution leases, queue semantics, unknown outcomes, revocation races, cache invalidation, signer isolation, capability descendants, authority graph invalidation, and the difference between preventing future execution and compensating for authority that escaped before revocation converged.
The central idea is uncomfortable but necessary:
Revocation is not a boolean.
It is a race between authority propagation and authority invalidation.
Authority spreads faster than revocation
Consider a capability:
C:
action = external_withdrawal
amount = 500000
account = A
expires_at = 14:10
The capability is issued at 14:00.
It propagates through:
API
->
orchestrator
->
queue
->
worker
->
custody
At 14:03, fraud detection decides the account must be frozen.
The system commits:
revoked(C) = true
at 14:03:01.
But the capability may already exist in several places:
orchestrator memory
queue payload
worker local cache
custody request
retry buffer
The revocation exists.
So does the authority.
For some period, both statements are true.
Revocation has several times
A useful revocation model distinguishes multiple timestamps.
For example:
decision_time
commit_time
publication_time
observation_time
enforcement_time
Suppose:
14:03:00 risk decides to revoke
14:03:01 revocation committed
14:03:02 event published
14:03:05 worker observes event
14:03:07 custody cache invalidated
What happened between 14:03:01 and 14:03:07?
The capability was revoked at the source.
It may still have been executable elsewhere.
That interval is revocation exposure.
Revocation exposure
Let:
T_commit = authoritative revocation commit time
T_enforced = time all relevant execution points reject authority
Then:
revocation_exposure_window =
T_enforced - T_commit
In practice, there may be no single T_enforced.
Different consumers converge at different times.
So we may instead have:
T_enforced(service_i)
The maximum exposure is:
max_i(T_enforced(service_i)) - T_commit
This is one of the most important quantities in an authority system.
A revocation architecture is only as strong as its slowest meaningful enforcement point.
Immediate revocation is usually fictional
Systems often document:
revocation takes effect immediately
What that usually means is:
the authoritative database row changed immediately
That is not the same thing.
If a signer can continue accepting stale authority for another sixty seconds, then practical revocation latency is sixty seconds.
If a queue worker can execute a command four minutes later without revalidation, then revocation latency is at least four minutes.
The system should measure actual revocation latency, not policy prose.
Online validation
The strongest revocation model is online validation at execution time.
Before performing an economic action:
verify capability
check current revocation state
check current epoch
check current state version
execute
This reduces stale authority.
But it introduces a new dependency.
If the revocation service is unavailable, what happens?
The executor has three broad choices:
fail open
fail closed
use bounded cached authority
Each has economic consequences.
Fail open
Fail-open means:
revocation state unavailable
capability otherwise valid
execute anyway
This preserves availability.
It also allows revoked authority to execute during control-plane failure.
For irreversible financial actions, this is often unacceptable.
The most dangerous property of fail-open is that the very condition that prevents checking revocation also disables revocation enforcement.
An attacker does not necessarily need to defeat the capability system.
They may only need to isolate its verifier.
Fail closed
Fail-closed means:
revocation state unavailable
reject execution
This protects authority boundaries.
It can also stop legitimate financial activity during control-plane outage.
That may be correct for:
high-value withdrawal
treasury movement
custody signing
and excessive for:
low-value internal action
Revocation availability therefore needs risk-tiered semantics.
Bounded offline authority
A middle ground is bounded offline validation.
Suppose an executor has a cached revocation snapshot valid for:
30 seconds
It may continue accepting capabilities only while:
now - snapshot_time <= 30s
After that:
fail closed
This creates a maximum stale-authority window.
The system can then say something meaningful:
revocation convergence is bounded by 30 seconds
under verifier connectivity loss
That is far stronger than saying:
revocation is eventually consistent
because eventually is not a financial control objective.
Revocation epochs
One practical mechanism is epoch-based invalidation.
Suppose each account has:
withdrawal_epoch = 17
Capabilities issued for withdrawals contain:
epoch = 17
If the account must be frozen:
withdrawal_epoch = 18
Every capability with epoch 17 becomes stale.
Validation becomes:
capability.epoch == current_epoch
This can revoke many capabilities without tracking each individually.
Epoch revocation is coarse
Epochs trade precision for efficiency.
If one capability is compromised, incrementing the account epoch may revoke:
every outstanding withdrawal capability
That may be desirable.
It may also disrupt unrelated operations.
A system can therefore maintain several epochs:
withdrawal_epoch
transfer_epoch
custody_epoch
delegation_epoch
This reduces blast radius.
The cost is more state and more validation logic.
Epoch caching still has staleness
Suppose an executor cached:
withdrawal_epoch = 17
The authority source updates:
withdrawal_epoch = 18
The executor remains unaware.
Epochs simplify invalidation.
They do not eliminate propagation delay.
So the real model remains:
epoch mechanism
+
freshness bound
not epoch alone.
Capability expiry as passive revocation
Short-lived capabilities reduce the need for active revocation.
Suppose capability lifetime is:
20 seconds
Even if a revocation message is lost, stale authority disappears soon.
This is useful where capabilities can be regenerated cheaply.
The upper bound on stale authority becomes approximately:
capability lifetime
assuming execution validates expiration correctly.
Lifetime versus operational latency
Capability lifetime cannot be chosen independently of workflow latency.
Suppose:
capability TTL = 20s
queue latency p99 = 18s
execution setup = 5s
Legitimate requests will frequently expire before execution.
Teams then increase TTL.
Eventually:
TTL = 30 minutes
and active revocation becomes critical again.
Capability lifetime must therefore be tied to actual execution latency and economic consequence.
Queue boundaries are revocation traps
Queues are particularly dangerous.
Suppose capability C is validated by Service A.
Service A publishes:
ExecuteWithdrawal {
operation_id = W
}
The queue delivers it five minutes later.
If the worker executes because:
message came from trusted queue
then revocation cannot stop the operation after enqueue.
Authority was effectively converted into a durable command.
This is authority laundering through asynchronous infrastructure.
Revalidate at the irreversible boundary
A strong principle is:
validate authority as late as possible
before irreversible effect
Service A may perform early validation.
The final executor should validate again.
For example:
API validation
->
orchestration
->
queue
->
worker revalidation
->
custody revalidation
->
external execution
Repeated validation is not redundant.
Each boundary operates at a later point in the authority lifecycle.
Durable authorization versus durable intent
Queued messages should ideally preserve intent, not final authority.
For example:
WithdrawalIntent {
operation_id
}
The worker loads current authorization state before execution.
Compare that with:
ExecuteWithdrawalUnconditionally {
amount
destination
}
The first is revocable.
The second is effectively an already-exercised permission waiting for CPU time.
Reservation complicates revocation
Suppose capability C has already reserved 500k.
Then the account is frozen.
Should revocation release the reservation?
If no execution has begun:
yes, probably
If execution may already have been submitted externally:
not safely
The capability lifecycle therefore matters.
For example:
Issued
Reserved
Executing
ExecutionUnknown
Consumed
Revocation behavior depends on the current state.
Revocation by lifecycle state
A possible model:
Issued:
revoke immediately
Reserved:
revoke and release reservation
if execution has not started
Executing:
prevent downstream steps where possible
do not assume rollback
ExecutionUnknown:
freeze authority state
reconcile external outcome
Consumed:
revocation has no preventive effect
compensation may be required
This is much more realistic than:
revoked = true
The point of no return
Every financial workflow has a point beyond which revocation cannot guarantee prevention.
Call it:
PONR = point of no return
Before PONR:
revocation can prevent external effect
After PONR:
revocation may only prevent later effects
or trigger compensation
The exact point depends on the rail.
For a blockchain transaction:
before signature
before broadcast
after broadcast
after inclusion
have different revocability.
For a bank transfer:
before submission
after submission
before bank acceptance
after booking
again differ.
Revocability should be explicit
The capability state should expose:
revocation_class
For example:
fully_revocable
revocable_until_submission
revocable_until_external_acceptance
non_revocable
This helps risk and operations reason about what revocation can still accomplish.
Revocation after submission
Suppose a transaction has been submitted but not confirmed.
The system receives a revocation request.
Possible outcomes include:
provider cancellation succeeds
provider cancellation fails
provider does not support cancellation
outcome unknown
The revocation workflow itself becomes a distributed transaction.
It needs:
attempt identity
evidence
timeout semantics
reconciliation
because the system may not know whether cancellation actually happened.
Revocation is another economic operation
This is a useful conceptual shift.
Revocation is not merely metadata.
It can create:
reservation release
cancellation request
compensation obligation
customer restriction
authority graph invalidation
Therefore revocation should often have its own operation identity:
revocation_id
and state machine.
Revocation state machine
For example:
Requested
|
v
Committed
|
v
PropagationInProgress
/ \
/ \
v v
Enforced Partial
|
v
Reconciled
A final state may be:
FullyEnforced
PartiallyEnforced
CompensationRequired
This is much more informative than a timestamp column.
Descendant capabilities
Delegation makes revocation harder.
Suppose:
C0
->
C1
->
C2
If C0 is revoked, what happens to C1 and C2?
Usually:
they should become invalid
because their authority derives from C0.
This means revocation propagates through the authority graph.
Transitive invalidation
Let:
Parent(C1) = C0
Parent(C2) = C1
If:
Revoked(C0)
then:
Invalid(C1)
Invalid(C2)
unless the model explicitly defines delegation as authority transfer rather than derivative authority.
This semantic choice must be explicit.
Derived authority versus transferred authority
There are two very different delegation models.
Derived authority:
child depends on parent remaining valid
Transferred authority:
child becomes independently valid
parent gives up some capacity
These have different revocation semantics.
If authority is transferred:
revoking parent
may not revoke already transferred child authority.
This can be desirable for isolated execution domains.
It can also surprise operators.
The architecture must define which model it uses.
Revocation graph cost
A naive system may recursively traverse every descendant whenever a root is revoked.
For large authority graphs, that can be expensive.
Epochs offer one compression mechanism.
For example, descendants reference:
authority_epoch = 44
Revoking the root increments:
authority_epoch = 45
Every descendant becomes invalid without explicit traversal.
But again, consumers must observe the new epoch.
Scope epochs
A useful pattern is tying epochs to authority scopes.
For example:
root_authority_epoch
account_epoch
asset_epoch
operation_epoch
A capability carries the relevant epoch vector:
epochs:
account = 17
withdrawal = 9
Validation requires all relevant values to match.
This supports broad or narrow revocation.
Capability revocation lists
Individual capabilities may still need direct revocation.
A revocation list can store:
capability_id
revoked_at
reason
This works for small or high-value sets.
At high scale, online lookup for every operation can become expensive.
Caching reintroduces staleness.
Nothing escapes physics merely because we named the table revocations.
Revocation bloom filters
One optimization is distributing compact revocation structures.
For example, a Bloom filter can answer:
possibly revoked?
False positives are possible.
False negatives should not be.
A consumer seeing:
not present
can proceed according to snapshot freshness.
A consumer seeing:
possibly present
performs authoritative lookup.
This can reduce online validation load.
But the filter itself needs:
version
generated_at
valid_until
or it becomes another stale trust artifact.
Revocation snapshots
Execution services may consume signed snapshots:
RevocationSnapshot:
sequence
generated_at
valid_until
epoch_map
revoked_capability_set_root
The snapshot can be cryptographically authenticated.
Consumers reject snapshots older than policy allows.
This creates bounded offline enforcement.
Sequence numbers
Revocation updates should be ordered.
Suppose a consumer sees snapshots:
101
102
100
It must not regress to 100.
Enforce:
new_sequence >= current_sequence
for monotonic control-plane state.
Otherwise delayed messages can resurrect old authority.
Authority resurrection
This is a particularly dangerous failure mode.
Suppose:
epoch 17 valid
epoch 18 revoked old capabilities
A stale cache restore accidentally reloads:
epoch 17
Capabilities thought dead become executable again.
Revocation state must therefore be monotonic.
The control plane should never accept lower revocation versions.
Rollback safety
Infrastructure rollback is normal.
Authorization rollback is not.
Suppose a deployment rollback restores an older database snapshot.
If capability epochs or consumed nonces roll backward, the platform may resurrect:
revoked authority
consumed authority
This can create direct economic loss.
Security-critical monotonic state should be separated from ordinary rollback semantics.
Consumption state is revocation-related
A consumed capability is effectively permanently revoked from future use.
Therefore capability consumption state has the same monotonicity requirements as revocation.
For one-shot capability C:
Issued -> Consumed
must never become:
Consumed -> Issued
even after disaster recovery.
Backup restoration
Disaster recovery must therefore reconcile:
capability issuance
consumption
revocation
external execution
Restoring an older capability database without replaying later authority events is unsafe.
The system may need an append-only authority log whose history survives ordinary database snapshots.
Authority event log
For example:
CapabilityIssued
CapabilityDelegated
CapabilityReserved
CapabilityConsumed
CapabilityRevoked
EpochIncremented
These events form the durable authority history.
Materialized capability state can be rebuilt from them.
This resembles event sourcing, but the safety motivation is monotonic authority reconstruction.
Multi-region revocation
Now consider multiple regions.
Region A commits:
revoke C
Region B is partitioned.
Region B still has:
C valid
Should Region B continue executing?
This is a consistency decision.
For high-value financial authority, allowing execution under stale authorization may be worse than losing availability.
This is where distributed systems theory stops being a diagram and starts having a P&L.
CP versus AP for authority
Under network partition, a system can prioritize:
availability
or:
current revocation consistency
It cannot always guarantee both.
For high-risk authority, the system may choose:
partition -> stop execution
For lower-risk authority:
partition -> allow bounded stale capability
The policy should be explicit by action class.
Region leases
One mechanism is granting regions short-lived authority leases.
For example:
Region B may execute withdrawals
until 14:05
under revocation sequence >= 991
If it cannot renew the lease:
execution stops
This converts indefinite partition risk into bounded partition risk.
Lease semantics
A lease may contain:
region_id
authority_scope
max_value
revocation_sequence_floor
issued_at
expires_at
Region B can execute only while:
now < expires_at
AND local_revocation_sequence >= sequence_floor
This gives central control a bounded stale-authority window.
Lease value limits
Time alone may not be enough.
A partitioned region might execute enormous value during a short lease.
So a lease can also carry:
maximum aggregate amount
For example:
5-minute lease
max spend = 1M
The risk is bounded by both:
time
value
Revocation budget
This suggests a useful concept:
revocation budget
The maximum economic authority that may remain executable while revocation information is stale.
For a region:
revocation_budget =
lease_capacity
For a service:
revocation_budget =
outstanding short-lived capabilities
The platform should know how much value could still escape after a revocation decision.
Value-at-stale-authority
A useful metric is:
value_at_stale_authority
Meaning:
maximum economic effect currently executable
using authority that has not refreshed revocation state
within target freshness
If this number grows large, the architecture has a revocation problem even if no incident has occurred.
Revocation latency distribution
The platform should measure:
decision-to-commit
commit-to-publication
publication-to-observation
observation-to-enforcement
Then report:
p50
p95
p99
max
for revocation convergence.
Security statements can then become concrete:
99% of withdrawal revocations
reach all active execution nodes
within 4.2 seconds
That is operationally meaningful.
Maximum matters
Averages are not enough.
One worker that remains stale for twenty minutes may be sufficient for loss.
Revocation systems care deeply about tails.
A useful SLO might be:
no executor may use revocation state
older than 15 seconds
for high-value withdrawals
That is stronger than:
average revocation delay < 2s
Revocation acknowledgments
Execution nodes can acknowledge applied revocation state.
For example:
ExecutorAck:
executor_id
revocation_sequence
observed_at
The control plane can see which nodes are stale.
It may then quarantine those nodes from receiving new economic work.
Stale executor quarantine
Suppose Worker 17 has not advanced beyond:
revocation sequence 991
while current is:
1004
The scheduler can stop assigning sensitive operations to Worker 17.
This prevents stale authority from remaining silently active.
Revocation-aware scheduling
The scheduler should consider:
worker revocation freshness
worker policy version
worker key state
before dispatching high-value work.
Execution capacity is therefore partly a function of control-plane freshness.
Cached authorization decisions
Another trap is caching:
account_allowed = true
for ten minutes.
The account is frozen after one minute.
The cache keeps returning true.
The revocation system may be perfectly correct.
The authorization cache bypasses it.
Revocation analysis must include every derived authorization cache.
Cache invalidation really is authorization invalidation
The old joke about cache invalidation becomes considerably less amusing around money.
Any cache that stores:
approved
allowed
withdrawable
trusted
holds economic authority.
Its TTL is part of revocation latency.
That should be documented and measured as such.
Negative caching
Even negative authorization can cause issues.
Suppose an account is temporarily blocked and the block is lifted.
A stale negative cache continues rejecting valid operations.
This is economically safer than stale positive authority but still operationally relevant.
The system may intentionally use asymmetric TTLs:
positive authorization TTL = 5s
negative authorization TTL = 30s
because stale permission is more dangerous than stale denial.
Asymmetric caching
This asymmetry is useful across security.
For example:
allow cache:
short lifetime
deny cache:
longer lifetime
The system favors false denial over false authorization where consequences justify it.
Signer revocation
Custody systems deserve special attention.
Suppose a signing service receives:
sign transaction T
with capability C.
While the signing protocol is running, C is revoked.
Should the signer finish?
The answer depends on protocol stage.
Before any irreversible signature material is produced:
abort
After a complete valid signature exists:
revocation may be too late
because the artifact can potentially be submitted elsewhere.
Signature material is authority
Once a valid transaction signature exists, the capability has effectively been transformed into a narrower but potentially irreversible artifact.
Revoking the original capability may not revoke the signature.
This is another point of no return.
Therefore high-assurance signing systems may:
bind signature to exact transaction
avoid exposing raw reusable signing authority
submit immediately through controlled path
The objective is to minimize the interval where valid but unsubmitted signed transactions exist.
Signing and broadcast coupling
One architecture:
authorize
->
sign
->
broadcast
with separate services creates a signed-artifact gap.
Another approach tightly couples:
sign-and-submit
under one bounded operation.
This reduces revocation flexibility after signing but also reduces the number of places where executable signed artifacts can leak.
There is no free option.
Blockchain cancellation
Some blockchain workflows support replacement transactions.
Revocation after broadcast may attempt:
replace transaction
cancel nonce
increase fee
But cancellation is probabilistic until the replacement becomes canonical.
The correct state is:
cancellation_attempted
not:
cancelled
until evidence proves the outcome.
Bank cancellation
Similarly, a bank API may return:
cancellation request accepted
This does not necessarily mean:
transfer cancelled
The cancellation request itself requires finality semantics.
Revocation inherits all the evidence problems of the operation it tries to stop.
Compensation boundary
Once an action cannot be prevented, revocation becomes a recovery trigger.
Suppose:
capability revoked
transaction already settled
The system should not keep retrying preventive revocation.
It should transition:
prevention
->
compensation
Possible actions include:
create receivable
freeze account
recover funds
reverse internal credit
trigger legal or compliance workflow
Prevention and compensation should not be conflated
A common incident mistake is continuing to call an operation:
revoked
even after the financial effect occurred.
Better states are:
revocation_requested
revocation_enforced_before_execution
revocation_too_late
compensation_required
compensation_complete
These describe reality.
Revocation provenance
Every revocation should record:
who requested it
why
which authority was targeted
which evidence triggered it
when it became authoritative
which execution points acknowledged it
whether any effects escaped
For example:
RevocationDecision:
revocation_id
capability_id
reason
evidence_refs
actor
committed_at
policy_digest
Then:
RevocationEnforcement:
executor
revocation_sequence
enforced_at
This creates an audit trail of convergence.
Revocation cause matters
Not all revocations are equivalent.
Examples:
customer cancellation
fraud detection
sanctions update
key compromise
policy change
operator correction
provider failure
The cause may change required response.
Key compromise may require revoking an entire authority subtree.
Customer cancellation may target one operation.
Sanctions action may require broader freezing.
The revocation class should therefore influence propagation.
Key compromise revocation
Suppose issuer key K is compromised.
The system may need to invalidate:
all capabilities issued by K
during interval [T1, T2]
This is much broader than individual capability revocation.
The authority graph helps identify affected descendants.
Time uncertainty under compromise
The exact compromise time may be unknown.
Suppose evidence shows:
compromise occurred sometime between 12:00 and 14:00
The platform may need to treat all capabilities issued during that interval as suspect.
This is another epistemic-capacity problem.
Uncertainty about compromise expands the revocation set.
Revocation storms
A large incident may require revoking millions of capabilities simultaneously.
For example:
issuer compromise
provider compromise
account class freeze
Can the revocation system handle this?
Individual database updates may not scale.
Epoch invalidation becomes valuable here.
One atomic epoch increment can invalidate an entire authority class.
Revocation scalability
The system should stress-test:
100 individual revocations
100k revocations
global epoch change
regional partition during mass revocation
because the security mechanism must work precisely when the system is under abnormal load.
Control-plane overload
A revocation storm can overload:
message buses
cache invalidation
signer lookups
authorization APIs
If overload causes fail-open behavior, the system fails exactly backward.
Security control planes should be provisioned for emergency peaks, not normal averages.
Revocation priority
Not every revocation has the same urgency.
A possible priority hierarchy:
critical:
compromised signing authority
high:
fraud-confirmed account
medium:
customer cancellation
low:
policy cleanup
Propagation infrastructure may prioritize critical revocations.
This reduces time-to-enforcement for the highest-value threats.
Revocation preemption
A high-priority revocation should be able to overtake queued low-priority work.
For example:
signing requests queue length = 5000
A fraud freeze arrives.
The revocation should not sit behind those 5000 jobs.
Security events require priority channels.
Kill switches
At the highest level, systems often need coarse emergency controls.
Examples:
disable all external withdrawals
disable asset X
disable provider Y
disable custody signer group Z
These are coarse revocation mechanisms.
They sacrifice availability for containment.
They should be:
simple
strongly authenticated
independently available
well tested
A kill switch nobody has tested is a decorative control.
Kill-switch independence
If the normal control plane is compromised, the emergency mechanism should not rely entirely on the same trust path.
For example, an emergency withdrawal halt may require:
separate credentials
separate approval path
independent signing authority
Otherwise attacker control of the normal authority plane may include control of its own revocation.
Revocation and formal methods
Several useful properties can be formalized.
For example:
Revoked(C, t)
AND
ExecutionStarts(A, t')
AND
t' > t + Delta
should imply:
not AuthorizedBy(A, C)
where Delta is the maximum permitted revocation propagation bound.
This is a bounded-staleness safety property.
Descendant invalidation property
For derivative delegation:
Descendant(C_child, C_parent)
AND
Revoked(C_parent)
implies:
Invalid(C_child)
after bounded convergence.
No resurrection
For revocation sequence r:
AppliedSequence(node, t+1)
>=
AppliedSequence(node, t)
This prevents stale state rollback.
Consumed authority monotonicity
For one-shot capabilities:
Consumed(C, t)
implies:
Consumed(C, t')
for all:
t' > t
The system must never reconstruct C as unused.
Revocation-before-effect
For operations whose execution has not passed the point of no return:
RevocationCommitted(C)
before
PONR(operation)
should imply:
ExternalEffect(operation) = false
subject to the documented propagation bound.
This is a meaningful testable safety objective.
A Rust sketch
A capability might carry epoch state:
#[derive(Debug, Clone)]
pub struct Capability {
pub capability_id: String,
pub subject_id: String,
pub action: Action,
pub account_epoch: u64,
pub action_epoch: u64,
pub expires_at: u64,
}
#[derive(Debug, Clone)]
pub struct RevocationView {
pub account_epoch: u64,
pub action_epoch: u64,
pub sequence: u64,
pub observed_at: u64,
}
Validation:
pub fn validate(
capability: &Capability,
view: &RevocationView,
now: u64,
max_view_age: u64,
) -> Result<(), ValidationError> {
if now > capability.expires_at {
return Err(ValidationError::Expired);
}
if now.saturating_sub(view.observed_at) > max_view_age {
return Err(ValidationError::RevocationViewStale);
}
if capability.account_epoch != view.account_epoch {
return Err(ValidationError::AccountAuthorityRevoked);
}
if capability.action_epoch != view.action_epoch {
return Err(ValidationError::ActionAuthorityRevoked);
}
Ok(())
}
The interesting part is not the code.
It is that:
max_view_age
is now an explicit security parameter.
Revocation as a bounded-consistency contract
This gives us a more honest system statement.
Not:
revocations are immediate
but:
high-value withdrawal authority
cannot remain executable
more than 10 seconds after
authoritative revocation commit,
assuming at least one control-plane path remains available
That is specific.
It can be measured.
It can be violated.
It can therefore be engineered.
Revocation SLOs
Possible operational objectives include:
p99 revocation propagation < 5s
max stale revocation view < 10s
100% high-value executors fail closed
after revocation view expiry
zero authority resurrection
after snapshot restore
These are much stronger than:
revocation service uptime = 99.99%
A revocation service can have excellent uptime and still propagate too slowly to protect money.
Revocation observability
Useful metrics include:
current revocation sequence
executor sequence lag
maximum revocation age
value behind stale executors
active revoked-but-unexpired capabilities
revocation propagation p99
execution-unknown value after revocation
descendant capabilities pending invalidation
One particularly important metric is:
value_at_revocation_risk
Meaning:
maximum economic effect
still executable by authority
that should already be dead
That number should be aggressively boring.
Testing revocation
Unit tests are insufficient.
The system should inject:
delayed revocation event
worker partition
stale cache
queue backlog
database rollback
region isolation
signer offline
duplicate revocation
out-of-order epoch update
Then verify economic outcomes.
For example:
revoke capability
partition one worker
wait beyond allowed stale window
attempt withdrawal through worker
Expected:
rejected
Not because the worker learned the revocation.
Because it failed to refresh within the permitted bound.
This distinction is fundamental.
Revocation under total control-plane loss
Suppose every revocation source is unavailable.
The system cannot know whether any existing capability has been revoked.
What should happen?
For high-value irreversible authority:
stop execution when local lease expires
For low-value reversible actions:
possibly continue inside bounded exposure
The system should degrade by economic consequence.
Revocation debt
If some executors are temporarily offline, revocation may not be applied immediately.
When they return, they must catch up before processing new work.
Until then, they carry revocation debt.
A node coming online should:
load latest revocation state
verify monotonic sequence
refresh policy
then accept economic work
Not:
start worker
process backlog
eventually refresh auth
That ordering matters.
Startup safety
A newly started process with no revocation state should begin:
unauthorized to execute
until it obtains sufficiently fresh authority state.
Default-deny at startup prevents cold nodes from becoming revocation bypasses.
Revocation and long-running workflows
Some operations last minutes or hours.
Should authority be checked only at start?
Probably not.
Long-running workflows may require checkpoints.
For example:
batch settlement:
prepare
reserve
sign
submit
Revocation may be checked before each irreversible phase.
Authority becomes phase-scoped.
Phase capabilities
Instead of one long-lived capability:
execute settlement
issue:
prepare capability
reserve capability
sign capability
submit capability
Each later phase requires stronger or fresher authority.
This reduces how much stale permission survives through long workflows.
Escalating proof near irreversibility
A general principle emerges:
As an operation approaches irreversible external effect:
authority should become
narrower,
fresher,
and more strongly validated.
Early planning stages may tolerate broader, cached authority.
Final execution should not.
Revocation and human operators
Manual emergency revocation matters too.
An operator may need to freeze:
one account
one asset
one provider
all withdrawals
The interface should make scope explicit.
A button labeled:
Disable
is not enough.
Disable what?
For whom?
Which already-issued authority becomes invalid?
Which in-flight operations continue?
Operational tooling should surface those semantics.
Human revocation confirmation
For high-impact actions, the system may show:
This action will invalidate:
14,882 active capabilities
3.2M of reserved withdrawal authority
412 in-flight operations
92 operations are past the point of no return.
Now the operator understands the actual effect.
This is much better than:
Are you sure?
which remains one of software's more ambitious attempts at risk management.
Conclusion
Revocation in distributed financial systems is not a boolean transition.
Authority may already have propagated through services, queues, caches, workers, signers, regions, and derived capabilities before revocation begins.
Once revocation is committed, the system enters a race.
Can invalidation reach every relevant execution point before stale authority produces an irreversible effect?
A resilient architecture therefore treats revocation as a distributed protocol.
It defines:
where authority exists
how fresh revocation state must be
which actions fail closed
how epochs invalidate authority
how descendants inherit revocation
where the point of no return lies
how stale executors are quarantined
how unknown execution outcomes are reconciled
Most importantly, it distinguishes prevention from compensation.
Before the point of no return, revocation should stop authority.
After it, revocation cannot rewrite history.
It must trigger recovery.
The useful invariant is not:
revoked == true
It is:
After authoritative revocation,
no economically significant effect
may be created by that authority
beyond the system's explicitly bounded
revocation exposure window.
That window has two dimensions:
time
and
value
A mature financial system knows both.
Because authority that should be dead but can still move money is not merely stale state.
It is live risk.
Top comments (1)
The line "revocation exposure = max_i(T_enforced(service_i)) − T_commit" is the right quantity, but I think it undercounts. A validity check followed by an irreversible effect has its own window, time of check to time of use. If the custody call validates the capability at T and the withdrawal takes a retry loop or a queue hop to land at T + d, the exposure for that execution is the cache or epoch staleness plus d, and d is unbounded when the signer retries. So the bounded-offline rule of "snapshot age ≤ 30 s" gives a 30 s bound only if the check happens in the same step that makes the effect irreversible, or the effect carries the epoch it was validated against and the settlement side rejects a stale one.
Second,
capability.epoch == current_epochtreats a capability from a newer epoch than the verifier's snapshot as stale. That's the safe direction for a lagging verifier, but the result is rejection of legitimately issued capabilities right after any epoch bump, so the unavailable-vs-unsafe trade shows up there as well as at the revocation service. Do you log the two rejection reasons separately (older than current vs newer than snapshot)? The second count is a direct measure of verifier lag.(Reasoned from the article, not run.)