The Lux consensus wire is ZAP. The Go struct IS the wire — there is
no codec, no marshal step, no encode or decode. A QuasarCert is a
buffer; accessors are offset reads on that buffer; Bytes() returns
the buffer.
Activated at the genesis of the new final Lux network:
2025-12-25 16:20 Pacific (unix 1766708400).
There is no other wire on this network. No legacy. No backwards
compatibility. The pre-Quasar Edition Lux network (2020–2025) is a
separate network and is out of scope.
type QuasarCert struct {
msg *zap.Message // the wire buffer itself
obj zap.Object // typed window into msg
}
func (c QuasarCert) BLS() []byte { return c.obj.BytesRef(OffsetBLS) } // offset read
func (c QuasarCert) Epoch() uint64 { return c.obj.Uint64(OffsetEpoch) } // offset read
func (c QuasarCert) Bytes() []byte { return c.msg.Bytes() } // return buffer
There is no Marshal. There is no Unmarshal. There is no Encode.
There is no Decode. There is no Serialize. Parse(b) wraps b in
a typed accessor — it does not copy and does not decode. Build(...)
writes fields into a buffer at known offsets and the buffer is the
wire bytes — there is no encoding pass over it.
This is the Cap'n-Proto / FlatBuffers invariant: **wire format is the
in-memory representation**.
Three layers, each one job. They compose. They do not braid.
sha256.Sum256 over canonical bytes |The wire layer does not know about the transcript; it offers
Bytes(). The transcript layer accepts byte slices and length-prefixes
them into a TupleHash; it does not know about ZAP. The identity layer
hashes Bytes(); it does not know about either.
~/work/lux/consensus/pkg/wire/zap/schemas.go is the single registry
of schema IDs. Each schema is a fixed offset map for one consensus
type. The struct embedding a *zap.Message IS the type's value.
QuasarCert | pkg/wire/zap/quasar_cert.go |QBlock | pkg/wire/zap/qblock.go |WitnessProof | pkg/wire/zap/witness.go |MagnetarAggregateCert | pkg/wire/zap/magnetar_aggregate.go |PolarisLegs | pkg/wire/zap/polaris_legs.go |QuasarSig | pkg/wire/zap/quasar_sig.go |EpochBundle | pkg/wire/zap/epoch_bundle.go |PrismCut | pkg/wire/zap/prism_cut.go |StakeWeightedCut | pkg/wire/zap/stake_weighted_cut.go |TxAuthEnvelope | pkg/wire/zap/tx_auth_envelope.go |PQPermit | pkg/wire/zap/pq_permit.go |DAGVertex | pkg/wire/zap/dag_vertex.go |PolicyCert | pkg/wire/zap/policy_cert.go |Unchanged. protocol/quasar/round_digest.go::ComputeRoundDigest and
protocol/quasar/qblock.go::TranscriptHash keep their existing field
layouts. Inputs are now Bytes() from ZAP structs instead of
hand-rolled binary, but byte-for-byte identical for identical fields
because SP 800-185 length-prefix framing is what fixes the transcript
layout — not the upstream byte producer.
Customization tags QUASAR-ROUND-DIGEST and QUASAR-Q-BLOCK-V1 are
cryptographic constants and remain byte-identical.
Unchanged. sha256.Sum256(x.Bytes()) for any consensus type x.
Wires owned by other repositories:
luxfi/pulsar — Pulsar threshold-sig wire (Module-LWE / FIPS 204)luxfi/threshold/protocols/corona — CORS wire (Module-LWE, Ringtail-derived)luxfi/magnetar — Magnetar SLH-DSA wireluxfi/crypto/bls — BLS-12-381 signature wireluxfi/zap — ZAP itself (LP-022)Consensus embeds the opaque byte strings these produce. This LP does
not redefine them.
External RPC surface:
pkg/wire/wire.go — Result / ConsensusParams are coordinatorand AI-consensus JSON-RPC types. JSON at the process boundary.
Not a consensus wire surface.
One wire. Decomplection: the wire layer does exactly one job
(present typed accessors over a byte buffer). Multiple wires would
braid two unrelated concerns (which wire to read) with one job
(read the bytes). One wire is single-purpose.
ZAP because the struct IS the wire. Every other option — protobuf,
RLP, msgpack, hand-rolled binary — is a codec: serialize on write,
deserialize on read. ZAP eliminates the step. The buffer is the value.
x.Bytes() returns it. sha256(x.Bytes()) requires no re-encoding.
That is the property no other option offers.
Activation at the genesis of the new final Lux network. The new
generation came online 2026-05-31 (devnet) and 2026-06-01 (testnet,
mainnet). Activation timestamp predates every block on the new
generation. Every block on the new final Lux network is a ZAP block
by construction. The v1.10.x luxd binary running today not yet
implementing ZAP wire is a binary defect to close in the next
release — not a chain-history fact to preserve.
None.
The new final Lux network has one wire, present in the binary from
the release that ships this LP. A node presenting non-ZAP bytes for
a consensus envelope is not a member of the network by definition.
The pre-Quasar Edition Lux network (2020–2025) is a separate network
with its own wire. Not migrated. Not read. Not referenced.
None. Activation predates every block on the network. There are no
pre-activation blocks to replay under a different wire.
None. The binary speaks ZAP. Byte input that fails ZAP parse is
rejected. No LUXD_ENABLE_* knob, no dispatch by magic byte, no
runtime selection.
test/e2e/{python,cpp,c,rust}_node.go are ZAP-only. The schema
registry pkg/wire/zap/schemas.go is the single source of truth;
each stub generates accessors from that source.
CI gate: test/e2e/cross_impl_byte_equality_test.go produces N random
consensus envelopes via the Go reference, asks each stub to round-
trip them, and asserts byte equality. Required green for any release
tag.
pkg/wire/zap/schemas.go | New — schema ID registry |pkg/wire/zap/quasar_cert.go … policy_cert.go (13 files) | New — one per schema |protocol/quasar/types.go | Delete QuasarCert.MarshalBinary/UnmarshalBinary |protocol/quasar/qblock.go (wire portion) | Delete hand-rolled binary block |protocol/quasar/witness.go | Delete hand-rolled binary |protocol/quasar/polaris.go | Delete EncodeMagnetarAggregate/DecodeMagnetarAggregate |protocol/quasar/epoch.go | Delete hand-rolled bundle binary |protocol/quasar/bls.go | Delete hand-rolled buildVoteDigest binary |protocol/quasar/grouped_threshold.go | Delete hand-rolled checkpoint binary |protocol/auth/tx_envelope.go, permit.go, hash.go | Delete hand-rolled binary |protocol/prism/cut.go, stake_weighted_cut.go | Delete hand-rolled binary |protocol/photon/emitter.go | Delete hand-rolled binary |engine/dag/vertex.go, engine/dag/state/state.go | Re-point at schema 0x0C |pkg/wire/policies.go (QuasarCert wrap) | Re-point at schema 0x0D |utils/codec/codec.go | Delete — orphan JSON codec, zero callers |protocol/quasar/round_digest.go | Unchanged |protocol/quasar/qblock.go::TranscriptHash | Unchanged |pkg/wire/wire.go | Unchanged — external JSON-RPC surface |pkg/wire/candidate.go, credentials.go | Unchanged — canonical-input hashes |Preserved files belong to other layers (transcript, external RPC,
canonical-input identity). Deleted files were the hand-rolled binary
wire layer, now exactly one wire — ZAP.
TestQuasarCertBuildParseFieldEquality build → Bytes() → Parse → fields equal
TestQuasarCertBytesIsUnderlyingBuffer Bytes() returns msg buffer, no copy
TestQuasarCertParseRefusesNonZAP non-ZAP bytes reject at Parse
TestComputeRoundDigest_Stability transcript bytes byte-identical to fixture
TestTranscriptHash_Stability q-block transcript byte-identical to fixture
TestCrossImpl_ByteEquality_AllSurfaces python/cpp/c/rust ≡ Go
TestNoLegacyCodecInBinary grep build symbols — no Marshal*, no Unmarshal*,
no linearcodec, no encoding/binary in consensus
protocol package
Lives in ~/work/lux/consensus/pkg/wire/zap/ and
~/work/lux/consensus/test/e2e/.
~/work/lux/consensus/pkg/wire/zap/ — lands in the luxd release
carrying this LP.
Every future Lux network-upgrade LP that changes the consensus wire
layer on the new final Lux network MUST use the same activation
marker as this LP:
activates: 2025-12-25T16:20:00-08:00
activates-unix: 1766708400
Set once. Does not move.