Skip to content

feat(tbtc): add guarded FROST share-repair recovery - #4241

Open
mswilkison wants to merge 7 commits into
fix/anchor-integrity-4222from
codex/share-repair-go-2026-08-10
Open

feat(tbtc): add guarded FROST share-repair recovery#4241
mswilkison wants to merge 7 commits into
fix/anchor-integrity-4222from
codex/share-repair-go-2026-08-10

Conversation

@mswilkison

@mswilkison mswilkison commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add one-shot frost/share_repair/v1 disaster-recovery maintenance for the
    exact helper and target seats named by an offline-signed authorization
  • add a non-networked native preflight mode that emits immutable seat/store/
    transport-key entries, then require a separately authority-signed exact
    helper-then-target roster in the final recovery bundle
  • route only fixed 105-byte native authenticated ciphertexts; complete repair
    scalar sets and transport private keys never enter Go requests or responses
  • reject seats outside the signed repair set before bus delivery and again
    before rendezvous buffering, while retaining legitimate early participant
    frames
  • require every announcement and local native session to match the signed
    public-key/store roster; scope the channel to both authorization and roster
    digests
  • register each logical announcement/receipt/ACK once and retain only the
    retransmitters needed for the current completion phase
  • keep the repaired seat disabled until a signed activation lease attests the
    old-store tombstone and its registry root is pinned by the production
    activation manifest
  • require Rust signer ABI 5.0 and pin feat(tbtc/signer): add offline-authorized FROST share repair #4240's exact commit

The target install remains inside the external state-anchor barrier.
Maintenance exits cleanly after completion; the bundle path must be removed
before restart, and production stays fail-closed until fleet cutover.

Roster issuance is a mandatory trusted incident ceremony. The authority must
authenticate each native preflight source/workload and must not sign public
keys supplied only by a potentially modified host. The runbook keeps the
production launch gate closed unless direct native/KMS or independent workload
attestation is available. An in-process FFI removes routine Go-heap/ABI secret
exposure but is not hardware isolation from arbitrary same-address-space code.

This is disaster recovery only. It does not change the capacity proposal in
#4222 or increase FROST threshold/group/state/anchor budgets. It follows the
share-repair investigation in #4222 and #4229.

Verification

  • go test ./pkg/frost/signing -count=1
  • go test -tags=frost_native ./pkg/frost/signing -count=1
  • go test -tags='frost_native frost_tbtc_signer' ./pkg/frost/signing -count=1
  • go vet -tags='frost_native frost_tbtc_signer' ./pkg/frost/signing
  • focused and race coverage for exact admission, native preflight lifecycle,
    roster substitution, ciphertext shape, Begin/Finish cleanup, and
    receipt/ACK/completion liveness
  • full default and frost_native tBTC package suites
  • linked ABI/readiness tests against the Rust dylib pinned below

Stack

Review/merge #4240 before this PR so the pinned native ABI is available.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27279a63-712d-4186-b1e6-0941973ad40f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mswilkison

Copy link
Copy Markdown
Contributor Author

Addressed both transport-lifecycle findings in the latest update:

  • Announcements now have exactly one application-level Broadcast. The
    underlying BroadcastChannel.Send owns same-sequence retransmission for the
    maintenance context, so a missing participant no longer accumulates a new
    context-lifetime handler every 500 ms.
  • Every logical message now has its own cancelable retransmission context.
    Helper announcements remain live past local rendezvous until the installed
    receipt proves the global barrier; receipt validation then retires the
    helper's public-package, delta, sigma, and announcement handlers, leaving
    only its ACK active during completion wait.
  • After durable, anchor-acknowledged installation, the target keeps its single
    installed-receipt retransmitter alive until every exact authorized helper
    acknowledges SHA-256(receipt_bytes) or the maintenance deadline expires.
  • Helpers no longer exit immediately after a one-shot ACK. Each keeps its one
    ACK retransmitter alive until the target announces that the exact ACK set is
    complete, or until the bounded deadline. The target completion is only an
    early-release signal; losing it cannot turn an already validated durable
    receipt into a helper failure.
  • If target-side ACK collection times out after installation, the API preserves
    the non-nil durable install result and returns an explicit post-install error;
    the runbook now says not to repeat repair arithmetic in that state.

Added regression coverage for one logical announcement registration, exact
helper ACK collection, wrong-context rejection, target completion, helper ACK
lifetime, completion loss at the deadline, and ACK/completion wire shapes.

Local validation:

  • go test ./pkg/frost/signing -count=1
  • go test -tags=frost_native ./pkg/frost/signing -count=1
  • go test -tags='frost_native frost_tbtc_signer' ./pkg/frost/signing -count=1
  • go vet -tags='frost_native frost_tbtc_signer' ./pkg/frost/signing
  • targeted go test -race -tags=frost_native ./pkg/frost/signing ...

@mswilkison

Copy link
Copy Markdown
Contributor Author

Addressed the latest transport/security review in 72ea4b892 (pinned Rust
counterpart: 35259de9aeb2daa25429a2630f1e8290240e500f).

  • Go no longer receives plaintext repair deltas/sigmas or transport private
    keys. The bridge requires ABI 5.0 and routes only exact 105-byte native AEAD
    envelopes under the separately authority-signed store/key roster.
  • Caller-controlled recipient_public_keys, target_public_key_hex, and repair
    data_hex shapes are removed from Part1/Part2/Install.
  • Authenticated wallet seats outside the signed helper/target set are rejected
    before bus delivery and again before the rendezvous pending buffer, closing
    the 1,024-frame nonparticipant abort path without dropping legitimate early
    participant frames.
  • Added the operational two-stage flow: non-networked native preflight,
    owner-only/no-replace public artifact, exact signed roster, strict final
    recovery bundle, mutually exclusive startup modes, and authorization+roster
    channel scoping.
  • The linked-library CI gate now checks all five ABI-5 repair symbols and pins
    the exact Rust commit.

Verification: all signing-package variants, focused/race repair and preflight
tests, full default and frost_native tBTC suites, vet/diff checks, and the full
linked Go/Rust cgo suite with missing-library skips forbidden pass.

The runbook also makes the preflight provenance launch gate explicit: the
authority must use direct native/KMS or independently workload-attested
collection and must not sign caller-invented endpoint keys.

@mswilkison

mswilkison commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the latest bounded-buffer review finding, plus the Go half of restart-stable retries, in c2b6d8df1. This commit pins Rust companion 87492db35f12566f83429fb2a2f751811b5afd95.

The buffering finding was valid: the previous 1,024-message bound allowed an authorized participant to retain hundreds of MiB of 256 KiB public-package frames per local seat before rendezvous.

The fix now:

  • caps public-package wire payloads at 16 KiB (the conservative 100-seat native package fixture serializes to 14,103 bytes);
  • validates message shape and exact authorization context before per-subscriber cloning;
  • enforces lifetime admission budgets of 20 KiB per sender and 2 MiB total per subscriber/session, while retaining the 1,024-message cap;
  • applies the same per-sender/total budgets to the runner's pending queue as defense in depth for custom/in-process buses;
  • charges only successful enqueue operations and safely decrements/clears pending accounting;
  • retains the first authenticated ciphertext for each exact delta/sigma semantic slot. This is required because the companion Rust fix keeps retry plaintext stable but intentionally re-randomizes ECIES. Canonical public-package byte equivocation remains fail-closed.

Regression coverage includes per-sender and total-byte floods, pre-clone rejection, duplicate/full-stream accounting, pending-byte accounting, byte-different delta/sigma retry candidates, strict public-package equivocation, concurrent race coverage, and the real 100-seat package-size launch proof.

Validation: normal and frost-native signing suites, focused race tests, Go vet, diff/gofmt checks, and the real linked CGO suite (including multiprocess libp2p tests) all pass.

Pin follow-up: dbb340ea9a64b329551af68fea2720b6e418a3fb advances the exact Rust mirror pin to CI-only Rust head c0d08c0c0c831c54ff765c9520be674789b12efa; the reviewed Go implementation remains c2b6d8df1.

@mswilkison

Copy link
Copy Markdown
Contributor Author

Addressed the latest Go/native session-ID contract finding in b0709f10cfba684b4e5e1a2c54de3eaf229135ef.

The finding was valid. Go previously accepted nonempty session IDs up to 256 bytes and rejected only leading/trailing whitespace, while the pinned Rust signer accepts at most 128 UTF-8 bytes and rejects every ASCII control byte (including DEL), ASCII space, =, ", and \\. An authority-signed artifact could therefore pass Go digest/signature validation and fail native preflight or BeginShareRepairSession.

The fix adds one share-repair-local byte validator matching Rust's contract exactly, with explicit UTF-8 validation for programmatic Go inputs. ComputeShareRepairAuthorizationDigest now enforces it before digest/signature acceptance, so every decode, roster/lease validation, preflight, runner, and native request path fails closed before invoking the engine.

Regression coverage now proves:

  • exactly 128 ASCII bytes and 128 bytes of multibyte UTF-8 remain valid;
  • 129-byte ASCII and over-128-byte UTF-8 IDs are rejected;
  • space, controls, DEL, =, quote, backslash, and invalid UTF-8 are rejected;
  • Rust-permitted punctuation and non-ASCII whitespace remain valid;
  • a native-invalid signed-artifact candidate is rejected during preflight before any native Begin/Finish call.

Validation passed: focused pre-fix reproducer, full frost_native signing tests, default signing tests, race-enabled preflight tests, Go vet, gofmt/diff checks, linked-CGO tagged tests, and an independent change-aware bypass audit.

@piotr-roslaniec
piotr-roslaniec force-pushed the codex/share-repair-go-2026-08-10 branch from b0709f1 to 53c6f4c Compare August 14, 2026 10:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant