5.2 KiB
Lux Governance — Multisig Signer Policy
Status: Active 2026-06-05 → next rotation TBD Owners of this doc: Lux Industries protocol governance
Scope
This document is the canonical record of the launch-time multisig signer configuration for production Safe deployments across the Lux network of blockchains (Lux primary, Hanzo L2, Zoo L2, Pars L2, SPC L2, Osage L2 and all corresponding testnet/devnet pairs).
It is the only place the signer derivation, threshold, and rotation
plan live. Any code that constructs a launch-time Safe (notably
lux/standard/contracts/script/DeploySafe.s.sol) reads these constants
back from this doc — when they change, the doc changes first.
Launch-time signer set
| Slot | Derivation path (BIP44, ETH cointype 60) | Role |
|---|---|---|
| 0 | m/44'/60'/0'/0/0 (LUX_MNEMONIC) |
Founder operator (deployer) |
| 1 | m/44'/60'/0'/0/1 (LUX_MNEMONIC) |
Co-founder operator |
| 2 | m/44'/60'/0'/0/2 (LUX_MNEMONIC) |
Security lead |
| 3 | m/44'/60'/0'/0/3 (LUX_MNEMONIC) |
Treasury / operations lead |
| 4 | m/44'/60'/0'/0/4 (LUX_MNEMONIC) |
Engineering lead |
All five slots derive from the same LUX_MNEMONIC seed for launch.
This is intentional and time-bounded: launch-time signers exist to
deploy the rest of the stack and immediately hand off control. Each
hardware-key holder will replace their derived slot via the standard
Safe owner-management flow (addOwnerWithThreshold → removeOwner or
swapOwner) once the deploy completes.
Threshold
- Production multisigs (mainnet): 3 of 5 (default)
- Testnet / devnet multisigs: 2 of 5 is acceptable, set via
SAFE_THRESHOLD=2at deploy time. - Hard ceiling: threshold ≤ owner count (
SIGNER_COUNT = 5). - Threshold ≥ 1.
DeploySafe.s.sol reads SAFE_THRESHOLD from env; default is 3.
Deterministic predictability
The Safe proxy address per (brand, env, chainId) is deterministic by construction:
saltNonce = uint256(keccak256(abi.encode(BRAND, SAFE_ENV, block.chainid)))
proxy = SafeProxyFactory.createProxyWithNonce(singleton, setupCalldata, saltNonce)
so re-running the deploy script against the same (brand, env, chainId) produces the same proxy address (the second call will
revert with the upstream Safe "creation failed" error rather than
overwriting). This is the intended behaviour — it gives reviewers a
way to pre-compute proxy addresses for spec docs without trusting any
live broadcast.
Rotation plan (post-launch, before mainnet bridge activation)
- Each of the five hardware-key holders signs a registration message
committing their hardware-derived address to a public proof file at
~/work/lux/dao/signers/<slot>.proof. - The Safe is reconfigured per-brand: each brand's Safe receives a per-brand owner set drawn from the same five hardware keys, possibly with brand-specific advisor seats added.
- After every brand's Safe is rotated, the launch-time derived
addresses are removed via
removeOwner. The launch-time mnemonic key chain is then no longer authoritative for any Safe.
Rotation must happen before the BridgeV4 activation timestamp on
mainnet (≥ 2026-08-08 per ~/work/lux/genesis/configs/mainnet/upgrade.json).
If the rotation is not complete, BridgeV4 activation must be deferred —
a five-launch-key Safe is fine for protocol bringup but not for
holding bridge fee receivership in production steady-state.
Per-chain Safe deployment manifest
Each (brand, env) Safe address is recorded in:
~/work/lux/standard/deployments/brand-l1-<env>/<brand>.json
.contracts.Safe
The same Safe is used as the protocolFeeController in
~/work/lux/genesis/configs/mainnet/upgrade.json once activation lands.
Why not Snowdrop-style governance now
A Snowdrop-style on-chain proposal flow with Timelock + vLUX + GaugeController
already exists in the Standard contract set (DeployMultiNetwork.s.sol
Phase 5) and is intentionally deployed alongside the Safe. The Safe
is the operational signer for protocol upgrades during the rotation
window; the Timelock + vLUX path becomes the authoritative governance
route once token distribution lands and quorum is reachable. Until
quorum is reachable, a 3-of-5 Safe is the right amount of friction.
Anti-patterns explicitly avoided
- Single-key admin — never write
admin = deployerin production contracts. Always rotate to the Safe immediately after deploy. - Threshold < ⌈n/2⌉ — minority threshold is collusion-friendly. We default 3-of-5 and refuse to encode 2-of-5 in production code.
- Per-app mnemonics — one
LUX_MNEMONICfor launch derivation. No per-app seeds (those just multiply the number of single-key compromise surfaces). - Hot-wallet signers on mainnet — launch-time derived slots get removed before bridge mainnet activation.
Verification
Anyone with LUX_MNEMONIC and forge can independently reproduce all
five launch-time signers:
for i in 0 1 2 3 4; do
cast wallet address \
--mnemonic "$LUX_MNEMONIC" \
--mnemonic-index "$i"
done
These addresses must match what is logged by DeploySafe.s.sol.