Files
pulsar/DEPLOYMENT-RUNBOOK.md
T
zeekay 394ce572ad docs: clarify v0.3 byte-equality scope + which Combine for which deployment
DEPLOYMENT-RUNBOOK now has TL;DR table mapping deployment scenarios to
the right Combine variant:
- Public BFT, no aggregator-host trust: AlgebraicAggregate (v0.3)
  with caveat that v0.3 sig is not yet circl-Verify-accepted
- Public BFT, interop with stock FIPS 204: v0.1 Combine inside TEE
- M-Chain / A-Chain custody: v0.1 Combine, dealer/TEE in TCB by policy

BLOCKERS.md::PULSAR-V03-1 entry updated with:
- Narrow scope: the bug is byte-equality with circl's mldsa65.Verify
- Primitive-by-primitive source-review verification (NTT, mulHat,
  montReduce, decompose, makeHint, packLeGamma1, packHint, mu, c̃,
  sigEncode pack order) — all match circl source
- Next-session debug plan: side-by-side deterministic-mode sig
  comparison to find first diverging byte
- Workaround: use TransitionalAggregate (v0.2) until closed

No code change; documentation only. Per CLAUDE.md x.x.x+1 rule.
2026-05-22 18:20:20 -07:00

14 KiB
Raw Permalink Blame History

Deployment Runbook — luxfi/pulsar

Operational guidance for deploying Pulsar (threshold ML-DSA-65) in production validator sets. Discloses the v0.1 trust-model caveat mandated by the cryptographer sign-off (GATE-1) and pins the safe operating envelope.

TL;DR: Which Combine variant for which deployment?

Deployment scenario Use Why
Public BFT, no aggregator-host trust AlgebraicAggregate (v0.3) NO sk materialized at any party. PROTOCOL-correct by construction. INTEROP NOTE: v0.3 sig wire-format does not yet pass stock circl mldsa65.Verify due to a subtle bug in our NTT-based polynomial arithmetic (v1.0.16 BLOCKERS.md::PULSAR-V03-1). All primitive checks against circl source verified equivalent — bug needs side-by-side runtime debugging. Use v0.3 today ONLY where the verifier accepts Pulsar's algebraic sig as-is (i.e., a custom Lux-precompile verifier that uses Pulsar's own polynomial layer).
Public BFT, interop with stock FIPS 204 Combine (v0.1) inside a TEE sig byte-equals circl, accepted by any FIPS 204 verifier. But aggregator host MUST be in TCB. Use SGX/SEV-SNP/TDX with remote attestation.
M-Chain bridge custody (single operator, TEE in TCB by design) Combine (v0.1) Same wire as above, dealer/aggregator trust is acceptable by deployment policy.
A-Chain confidential compute (TEE-attested provider) Combine (v0.1) TEE is part of the AI-provider attestation envelope (see aivm).
Dev/test, no security guarantees Either Both produce valid threshold sigs; choose by interop need.

Public-BFT graduation gate

The v0.3 → public-BFT-production gate is: TestAlgebraic_FullCycle_n5_t3 passes (output sig verifies under stock circl mldsa65.Verify). Today this test FAILS. Tracked in BLOCKERS.md::PULSAR-V03-1. Once it passes, the v0.3 wire becomes the canonical wire and v0.1 becomes deprecated.

Audience

  • Validator operators bringing up a Pulsar-enabled Lux chain (or any chain consuming github.com/luxfi/pulsar).
  • Coordinator-service operators running the witness-aggregator pattern.
  • Security reviewers validating production posture against the cryptographer sign-off (CRYPTOGRAPHER-SIGN-OFF.md).

Trust-model disclosure (load-bearing)

The v0.1 reconstruction-aggregator caveat

Operators MUST acknowledge this before running v0.1 in production on funds-bearing networks. It is the GATE-1 deferred disclosure from the cryptographer sign-off.

Pulsar v1.0.8 ships the v0.1 reveal-and-aggregate flow as the runnable reference. In this flow, Combine (in ref/go/pkg/pulsar/threshold.go) briefly reassembles the master ML-DSA-65 seed in the aggregator process's memory by computing Lagrange reconstruction over the revealed partial signatures and invoking mldsa65.NewKeyFromSeed. The seed exists in memory only for the duration of a single KeyFromSeed call and is zeroized on every exit path via ref/go/pkg/pulsar/zeroize.go (zeroizeBytes / zeroizeSeed / zeroizeU16 / zeroizePrivateKey), including error paths.

TCB implication: the aggregator process is in the trusted computing base for that brief window. An adversary who fully compromises the aggregator host (root-equivalent) during that window observes the seed and can forge signatures going forward.

Operational mitigations (all production deployments MUST satisfy at least items 1–4):

  1. Aggregator host hardening: dedicated host, no general-purpose workloads, no shared memory, no /proc access for non-root users, hugepages disabled (prevents same-page deduplication side-channels).
  2. Memory isolation: the aggregator process runs with locked pages (mlock), core-dumps disabled (setrlimit RLIMIT_CORE=0), ptrace disabled (prctl PR_SET_DUMPABLE 0).
  3. TEE attestation (recommended): aggregator runs inside SGX, SEV-SNP, or TDX with remote attestation pinned to the reproducible build of luxfi/pulsar v1.0.8.
  4. Operational scope limits: aggregator role is a single-purpose process; no network listener beyond the consensus message bus; no shell access; no debugger attachment in production.
  5. Defense-in-depth (v0.2 migration target): the v0.2 FSwA Lagrange-linearity flow eliminates the reconstruction step entirely. v0.2 is the high-assurance target and EasyCrypt N1 reduction is stated against it; v0.2 Jasmin sources type-check. Production migration v0.1 → v0.2 is on the roadmap (see ~/work/lux/lps/ROADMAP-CRYPTO-STACK.md).

What v0.1 does NOT do

  • Does NOT persist the reconstructed seed to disk.
  • Does NOT leak the seed to any other process under normal operation.
  • Does NOT have a code path that copies the seed to a buffer that outlives the KeyFromSeed call.
  • Does NOT survive a process crash with seed material present (the seed lives entirely on the stack of one goroutine; OS process teardown reclaims it).

The TCB equivalence is "aggregator process == single-party FIPS 204 signer". The distributed threshold property holds against the participating committee adversaries (any t-1 collusion among the n parties), but it does NOT extend to an attacker who compromises the aggregator host itself.

Pre-deployment checklist

Before bringing up a Pulsar-enabled validator:

  • Operator-readable acknowledgement of the v0.1 trust caveat above on file (RACI sign-off).
  • Aggregator host meets items 1–4 above.
  • If running on funds-bearing mainnet: item 3 (TEE attestation) satisfied OR documented compensating control approved by security review.
  • scripts/check-high-assurance.sh exits 0 against the deployed binary's source tag (i.e., the binary you are deploying was built from a commit where the high-assurance gates were green).
  • CRYPTOGRAPHER-SIGN-OFF.md reviewed; all four gates either satisfied or have a compensating control on file.
  • Run go test -race ./ref/go/pkg/pulsar/ against the deployed tag; PASS.
  • Run test/interoperability/ against the deployed tag; 19/19 N1 subtests PASS.

At-runtime monitoring

The aggregator process should expose:

  • A metric for every Combine invocation (counter + latency-histogram).
  • A metric for every zeroize call on the seed material (counter; should match Combine invocations 1:1).
  • A panic / signal handler that wipes the seed memory before exit.

Anomalous values for the zeroize counter (lower than the Combine counter) indicate a missed cleanup path — page immediately.

v0.1 → v0.2 migration plan

See ~/work/lux/lps/ROADMAP-CRYPTO-STACK.md §Pulsar v0.2 milestones. The migration is backward-incompatible at the wire level (v0.2 partial-signature encoding differs) but byte-equivalent at the output (final FIPS 204 signature is identical). Coordinated validator-set rollover is required.

v0.2 transitional threshold — trust model

Status (v1.0.14): v0.2 ships in ref/go/pkg/pulsar/threshold_v02.go under the Transitional name. The wire protocol is honestly algebraic — parties hold polynomial-vector Shamir shares of (s_1, s_2, t_0) over GF(q), Round 1 commits w_i = A·y_i, an intermediate w-reveal pass exchanges per-party w_i under MAC, Round 2 contributes (z_i, cs2_i, ct0_i) algebraically. The aggregator side is NOT yet algebraic: TransitionalAggregate validates every commit-bind and MAC, then calls mldsa{44,65,87}.SignTo against the master sk packed in TransitionalSetup.SkBytes. This is the v0.2 honesty caveat the rename from Algebraic* to Transitional* exists to declare.

Two paths, two trust models

Pulsar v1.0.14 ships both trust-model targets in parallel:

  • v0.1 reveal-and-aggregate path — pulsar.Combine in ref/go/pkg/pulsar/threshold.go. The aggregator briefly reconstructs the master ML-DSA seed (via Lagrange interpolation over byte-wise GF(257) shares) and emits the signature through mldsa{44,65,87}.SignTo. TEE-attestation is mandatory for funds-bearing networks; the aggregator process is in the trusted computing base for the duration of one sign call.

  • v0.2 transitional path — pulsar.TransitionalAggregate in ref/go/pkg/pulsar/threshold_v02.go. The protocol-side wire shape is FROST-for-FSwA with polynomial-vector shares as the carrier; parties never broadcast a share of the master seed in any form. The aggregator side still holds the master sk via TransitionalSetup.SkBytes and signs with it — the per-party (Z, CS2, CT0) contributions are validated for commit-bind / MAC integrity but are NOT consumed by the inner sign step. The aggregator TCB at sign time is identical to v0.1; only the parties' side of the protocol is honestly algebraic. TransitionalAggregate enforces len(setup.SkBytes) > 0 — TestTransitional_DependsOnSkBytes pins this in code as the load-bearing v0.3 graduation criterion.

v0.2 trust model — when is it safe to deploy?

TransitionalAggregate IS safe to deploy when:

  1. The aggregator host is already in the TCB by other means (TEE attestation pinned to a reproducible Pulsar build; HSM- custody; single-operator deployment where the operator already sees all key material).
  2. M-Chain bridge custody where the bridge MPC node is the aggregator and TEE attestation is mandatory at the operator layer.
  3. A-Chain confidential-compute subnets where the entire aggregator process runs inside SGX/TDX with remote attestation.

TransitionalAggregate is NOT safe to deploy when:

  1. The aggregator host is not in the TCB (public adversarial deployment where any node may be compromised).
  2. The validator set assumes "no single host holds the master sk ever" — v0.2 violates this at the aggregator's brief sign window.

For deployments in the "NOT safe" bucket, use v0.1 Combine behind an explicit TEE attestation layer at the consumer (the wire shape is mature, the TCB caveat is well-documented in the v0.1 disclosure section above), OR wait for v0.3.

v0.3 milestone — graduation gate

The v0.3 work removes the SkBytes field from TransitionalSetup entirely. The graduation gate is one bit:

When TransitionalAggregate no longer needs setup.SkBytes — i.e. when it emits the FIPS 204 signature directly from the aggregated per-party (Z, CS2, CT0) contributions — the v0.3 milestone is met.

Operationally this is tracked by TestTransitional_DependsOnSkBytes in threshold_v02_test.go. The test currently PASSES (because TransitionalAggregate does depend on SkBytes); when v0.3 lands, that test FAILS, and the failure is the load-bearing red flag that the v0.3 graduation is complete. At that point:

  1. Delete the SkBytes field from TransitionalSetup.
  2. Rename TransitionalAggregate → AlgebraicAggregate (forward-only, no compat alias — same discipline as the Algebraic → Transitional rename in v1.0.14).
  3. Rewrite the file-header honesty block to drop the SkBytes caveat.
  4. Delete or rewrite TestTransitional_DependsOnSkBytes.
  5. Update this section of DEPLOYMENT-RUNBOOK.md to reflect that v0.3 is safe for public adversarial deployments.
  6. Close PULSAR-V03-1 in BLOCKERS.md.

Tracked in BLOCKERS.md as PULSAR-V03-1.

What v0.2 buys today (v1.0.14)

  • No seed-share leakage at parties: polynomial-vector shares are the only secret material parties hold. A PolyKeyShare is information-theoretically (t-1)-secret against any coalition of fewer than t parties.
  • Wire-shape stability: the v0.2 message flow (commit / intermediate w-reveal / sign-contribute / aggregate) is stable across v0.2 → v0.3. Consensus integrators that adopt the v0.2 protocol surface today get the message routing right; v0.3 only changes the cryptographic content of TransitionalAggregate → AlgebraicAggregate.
  • Identifiable-abort gates: every MAC and commit-bind check in TransitionalAggregate emits AbortEvidence on tamper detection, matching v0.1's complaint taxonomy.

What v0.2 does NOT yet do (v1.0.14)

  • The inner sign step still uses the master FIPS 204 packed sk via TransitionalSetup.SkBytes. The aggregator TCB at sign time is identical to v0.1. The v0.3 patch removes SkBytes from TransitionalSetup and emits the signature from the aggregated per-party contributions directly.
  • The byte-equality test (v0.2 transitional signature byte-equal to stock FIPS 204 ML-DSA on the same y aggregate) is delivered via the transitional path; the v0.3 pure-algebraic emission requires a self-contained FIPS 204 sign-side polynomial-ring implementation in mldsa_lattice.go (NTT, Montgomery, decompose, MakeHint) that currently has a subtle Montgomery-scaling discrepancy against cloudflare/circl's internal package — see threshold_v02.go header for the diagnosis.

Reference: Cryptographer GATEs

The four gates from CRYPTOGRAPHER-SIGN-OFF.md:

Gate Status as of v1.0.8
GATE-1 — Aggregator trust runbook disclosure CLOSED by this document.
GATE-2 — SUBMISSION.md cross-link to BLOCKERS "Spec ↔ Go-reference protocol drift" CLOSED in SUBMISSION.md "Headline claim" + "What to read first" §12.
GATE-3 — dudect ≥ 10⁹ samples for submission grade OPEN — harness wired (ct/dudect/run-submission.sh); awaiting next nightly window. Per-push smoke runs are informational only per PROOF-CLAIMS.md §3.2.
GATE-4 — Minor doc nits CLOSED: 89.7%→84.2% coverage corrected in SUBMISSION.md; Ed25519→ML-DSA-65 in spec/pulsar.tex §Identifiable abort; PROOF-CLAIMS.md §3.2 dudect cross-link added; zeroize.go row added.

Contact


Document metadata

  • Name: DEPLOYMENT-RUNBOOK.md
  • Version: v0.2 (matches Pulsar v1.0.14)
  • Closes: CRYPTOGRAPHER-SIGN-OFF.md GATE-1.
  • v1.0.14: renamed v0.2 API from Algebraic* → Transitional* and rewrote the trust-model section to honestly disclose the aggregator-side SkBytes dependency. The v0.2 wire protocol is algebraic; the v0.2 aggregator is not. v0.3 (PULSAR-V03-1) closes the gap.