Skip to content

feat(helper): the coordinator builds at the lower fee only for vaults whose every seat accepts it, behind an operator switch (#847) - #948

Merged
danielgorgonha merged 11 commits into
stagingfrom
feat/fee-era-builder
Nov 13, 2026
Merged

danielgorgonha merged 11 commits into
stagingfrom
feat/fee-era-builder

Conversation

@danielgorgonha

Copy link
Copy Markdown
Member

What & why

Third step of the fee release (#847): the coordinator may build at the lower network fee ZIP 2009 allows (1,000 zatoshis per action from block 3,590,000), but only for a vault whose every seat reported that its device accepts it, and only when the operator turns the switch off. By default nothing changes: the switch is ON and every vault keeps paying 5,000 per action.

Why one piece: the permission, the builder and the guard only make sense together. Split, the builder would read a permission with nothing checking what it built, or the guard would check a rule nothing builds to. The pieces:

  • konclave-seal: one function gives the builder's fee rule (fee_rule_for, with one predicate for when the lower fee is in force), and one reader gives a PCZT's fee and action count (spends::bundle_fee), used by the device and by the coordinator's guard. A table test pins that every era accepts 5,000.
  • konclave-signer: build-payroll takes --zip2009-permitted --zip2009-from <height> and picks the marginal from the wallet's own target height (ZIP 2009 says the mining height).
  • orchestrator: vault_fee_permission decides per vault from B2's seat reports (feat(helper): each seat reports the fee era its device accepts (#847) #945): permitted only with the switch off and every seat reported; the height is the larger of the table's and the reports', so a report cannot bring the era earlier. The funding check at proposal time stays at 5,000. Between build and proof, guard_built_fee reads the coordinator's own PCZT and refuses it if its fee is not in the permitted set at tip+1, and refuses outright when the tip is unknown.
  • helper-server: the switch is KONCLAVE_FEE_MARGINAL_MAX, where absent or 5000 means ON and per-vault means OFF. Any other value refuses to boot. The boot line says which mode it runs in.
  • The browser package is rebuilt from source (reproducible with wasm-pack 0.13.1; sha256 checked by the audit).

Known limit (in helper-server/CHANGELOG.md): a seat's report is self-declared, and two devices of one seat share one registration, so the coordinator can build at 1,000 where a signing device still refuses it. That device then refuses the ceremony. The effect is on availability, never on funds; the device's own fee check is the defence and KONCLAVE_FEE_MARGINAL_MAX=5000 the remedy.

Rollout: deploy with the switch ON; it goes off only after a 1,000 send on staging after block 3,590,000.

Verify-before-finishing checklist

  • cargo fmt --check + cargo clippy -- -D warnings pass (konclave-seal, konclave-signer, konclave-wasm, orchestrator, helper-server); wasm32 release build
  • cargo test --no-fail-fast on the five crates: all pass except the known timing flake a_history_sync_and_a_funding_check_never_overlap_and_sync_once (helper-server, it fails on staging too); ui/ suites run in CI
  • Mutation round: 16 of 16 killed; new tip-0 test red-checked without the guard
  • No secret, key share, seed, or passphrase in code, logs, URLs, or query strings
  • Every fund-moving path keeps preview + explicit confirmation
  • Reviewed: code, tester, security (plus a delta round on the fixes)
  • CHANGELOG: changelog.d and helper-server/
  • Docs/comments in English; commits are type(scope): description
  • AI disclosure: written with Claude Code, reviewed by the author, who answers for every line. Commits carry Co-Authored-By: Claude <noreply@anthropic.com>. No session link.

🤖 Generated with Claude Code

danielgorgonha and others added 10 commits November 12, 2026 23:28
…and actions are read in one place (#847)

fee_rule_for(target, permitted) gives the marginal a builder pays: the ZIP 2009 era
only with permission and inside the era, else 5,000. The device's bundle shape and the
coordinator's action count now both read konclave_seal::spends::bundle_fee.

Co-Authored-By: Claude <noreply@anthropic.com>
…the wallet's target give (#847)

Reads the wallet's target height before building the rule (the marginal is also the dust
threshold), takes --zip2009-permitted and --zip2009-from, and builds the FeeRule from
konclave_seal::epochs::fee_rule_for. Without the flags it pays 5,000 as before.

Co-Authored-By: Claude <noreply@anthropic.com>
…s own build and boots with an operator switch (#847)

SendConfig carries zip2009_permitted and zip2009_from (false and absent everywhere until the
seats' reports are read). The funding check always builds at 5,000, the coordinator reads the
fee of its own PCZT with the device's reader and refuses it outside the permitted set, and
KONCLAVE_FEE_MARGINAL_MAX is unset (per-vault permission), exactly 5000 (every vault pays it)
or a refusal to boot.

Co-Authored-By: Claude <noreply@anthropic.com>
…emony file cannot grant it, and the switch is on unless per-vault (#847)

zip2009_in_force is read by the signer's decision and the coordinator's guard (one era
lookup, Table::fee_era_at). The guard refuses an unknown tip. SendConfig's permission fields
are not read from JSON, which also makes orchestrate_send legacy-only by construction.
KONCLAVE_FEE_MARGINAL_MAX: unset or 5000 is ON, per-vault is OFF, anything else refuses to
boot. A const assert keeps every fee era accepting the legacy marginal fee.

Co-Authored-By: Claude <noreply@anthropic.com>
… reported it and the switch is per-vault (#847)

vault_fee_permission is the one decision: switch ON, no seat count, no seats or one seat that
reported nothing is the legacy fee; otherwise the permission holds from the highest of the
table's own height and the seats' reports, so a seat cannot report the era earlier than the
table has it. send_config_for reads seat_fee_reports and calls it.

Co-Authored-By: Claude <noreply@anthropic.com>
… the builder reads it (#847)

Co-Authored-By: Claude <noreply@anthropic.com>
…guard before the proof (#847)

Co-Authored-By: Claude <noreply@anthropic.com>
…with the shared reader (#847)

Co-Authored-By: Claude <noreply@anthropic.com>
)

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
konclave-demo Ready Ready Preview Nov 13, 2026 11:12am UTC

Request Review

Co-Authored-By: Claude <noreply@anthropic.com>
@danielgorgonha
danielgorgonha merged commit bdb0be8 into staging Nov 13, 2026
10 checks passed

This branch was successfully deployed

1 active deployment
Preview — 795f63c5 Deployed Nov 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant