Appearance
Authority and Operator Flow
This page describes the bridge from the authority and operator side — the infrastructure layer that determines whether user transfers actually complete.
Authority role
Bridge authorities are weighted signers registered in the Bridge account for a given chain pair. Each authority has a weight (u64) and an index that tracks its position for efficient vote lookup. Their operational purpose is to observe state on one chain and vote for corresponding state transitions on the other.
Authorities do not "approve transfers" in a general sense. They vote on specific observed deposit entries or withdrawal outcomes, and the protocol finalizes when cumulative vote weight meets the threshold. An authority that votes No on an entry actively counts against finalization — a No vote is recorded and the entry may be rejected if the No weight reaches a blocking level.
Bridge-specific
The threshold is stored as a fixed-point U0F64 value (0.0–1.0) on the Bridge account. Required weight is computed as ceil(total_authority_weight × threshold). This means a threshold of 0.67 with total weight 100 requires weight ≥ 67, not 66.
The operator sequence
1. Observe source-side deposits
Authorities or their automation poll the source chain's DepositTracker for entries with status Initialized that have not yet been acknowledged on the destination chain. The crank compares the source DepositTracker.entry_count against the destination WithdrawTracker.entry_count to discover pending work.
2. Submit ack_deposit
The authority signs and submits ack_deposit on the destination chain. This instruction takes:
first_entry_id— the starting entry index to acknowledge- A list of
ProposedDepositEntryvalues (each withamount,min_amount,destination) deposit_next_finalized_entry— the authority's observed finalization cursor on the source side
For each proposed entry, the instruction creates or updates the corresponding WithdrawEntry and records the authority's vote weight. If the proposed entry would violate rate limits, freeze state, or other token-pair constraints, the authority's vote carries a Rejected result with the appropriate DepositRejectionReason.
3. Wait for threshold finalization
Each WithdrawEntry accumulates vote weight independently. Once cumulative Yes weight meets the threshold, the entry finalizes with DepositResult::Confirmed. If No weight meets a blocking level, it finalizes as Rejected.
The operator does not need to take action during this step — finalization happens as part of the ack_deposit instruction when the threshold-meeting vote is cast.
4. withdraw becomes permissionless
After finalization with Confirmed, anyone can call withdraw for that entry on the destination chain. Operators do not need to execute this, though the crank does not currently automate it either — it is left to users or external automation.
5. Observe destination-side outcome
Authorities poll the destination chain's WithdrawTracker for entries that are Finalized but have not yet been acknowledged back on the source chain. The crank compares the destination-side finalization state against the source DepositTracker to discover entries needing ack_withdraw.
6. Submit ack_withdraw
The authority signs and submits ack_withdraw on the source chain. This instruction takes:
first_entry_id— the starting deposit entry indexproposed_results— a list ofDepositResultvalues reflecting what the authority observed on the destination chain
Each authority's vote weight is accumulated on the DepositEntry. When threshold is met, the deposit entry finalizes with the observed result.
7. cleanup_deposit becomes permissionless
Once the source-side deposit is finalized, cleanup can run. The crank actively discovers cleanup candidates — entries that are finalized but have cleanup_completed == false — and submits cleanup_deposit for each one. Cleanup handles fee refunds and, for rejected deposits, token refunds.
The WithdrawTracker also maintains a WithdrawCleanupTracker that tracks per-authority next_finalized_entry and owed_rent, ensuring rent costs incurred by authorities during ack_deposit are properly accounted for.
Crank loop sequence
Each poll cycle, the crank executes in this order:
- Check for pending
ack_depositwork (source deposits not yet reflected on destination). - If found, submit
ack_deposit. - Check for pending
ack_withdrawwork (destination results not yet reflected on source). - If found, submit
ack_withdraw. - Discover cleanup candidates (finalized, not yet cleaned deposits).
- Submit
cleanup_depositfor each candidate.
The crank reports each cycle as a CrankReport with fields: ack_deposit_attempted, ack_withdraw_attempted, cleanup_attempted, and cleanup_completed.
Recommended runtime shape
The current operational guidance is intentionally simple:
- One Cloud Run service per authority signer
- One bridge pair per service
- Single instance — no horizontal scaling
- Single concurrency — one request at a time
- Always-on polling loop — the crank runs continuously inside the server process
Zink recommendation
For v1 operations, keep the topology boring. One signer, one service, one bridge pair. A "clever" multi-tenant crank that serves multiple authority keys or bridge pairs is harder to reason about during incidents and introduces race conditions between concurrent signers.
Cloud Run deployment settings
| Setting | Value | Notes |
|---|---|---|
min-instances | 1 | Always-on; cold starts would stall bridge operations |
max-instances | 1 | Prevents duplicate authority submissions |
concurrency | 1 | Avoids concurrent crank executions |
cpu | 1 | Sufficient for polling and RPC calls |
memory | 1Gi | Headroom for tracker deserialization |
| Execution environment | Gen2 | Required for always-on CPU allocation |
| Ingress | IAM-protected | Admin endpoints (/kill, /crank/once) require authentication |
Mount the authority keypair from Secret Manager as a file (conventionally /secrets/authority.json) and point BRIDGE_AUTHORITY_KEYPAIR to that path. Do not bake keypairs into container images.
Operator responsibilities
| Area | What to do |
|---|---|
| Key management | Secure the authority keypair; rotate via AuthProposal if compromised |
| Liveness | Keep the server process alive; monitor /health for last_run_at freshness |
| RPC connectivity | Ensure both BRIDGE_SOURCE_RPC_URL and BRIDGE_TARGET_RPC_URL are reachable and performant |
| Monitoring | Watch /health for last_error and total_actions_submitted; scrape /metrics for Prometheus alerting |
| Manual intervention | Use POST /crank/once or the CLI to manually drive operations when the automated loop is stuck or retries are exhausted |
| Configuration changes | Coordinate AuthProposal workflows with other authorities for threshold/token-pair/authority changes |
Configuration sources
- Server and Crank — environment variables and endpoints
- CLI Reference — manual operator commands
- Bridge Config Reference — on-chain configuration detail