Skip to content

Commit 242122f

Browse files
onspeedhpclaude
andcommitted
docs(deploy): mainnet deploy runbook + audit-frozen tag checklist
Two operational documents covering the path from audit submission to production deploy: docs/MAINNET_DEPLOY.md — runbook for the foundation deploy and the binary swap to lazorkit-protocol at contract end. Both binaries occupy the same mainnet slot (LazorjRF…) at different times. Covers initial deploy, routine upgrades, binary swap, rollback, pre-existing wallet account behavior across the swap, and final upgrade-authority lock-down. docs/AUDIT_PREP.md — pre-tag checklist run before submitting a revision to Accretion. Covers code state hygiene, dual-feature build verification, test suite, fee-surface invariant (check-no-fee), documentation alignment, diff bounding, and the audit-packet contents to deliver. Also defines the audit-frozen-vN tag naming convention and the post-audit fix-up flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 322a06d commit 242122f

2 files changed

Lines changed: 346 additions & 0 deletions

File tree

docs/AUDIT_PREP.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Audit-Frozen Tag Checklist
2+
3+
Before submitting a `program-v2` revision to Accretion (or any auditor) for
4+
review, walk this checklist and tag the resulting state. The tag becomes the
5+
exact source the audit report references — a deployment that doesn't match
6+
the tag falls outside the audit's scope.
7+
8+
## Naming convention
9+
10+
Tags use the format `audit-frozen-vN` where `N` increments per audit cycle.
11+
Example: the first delta audit after the initial Accretion review is
12+
`audit-frozen-v2`. The audit report should cite the tag in its scope section.
13+
14+
## Pre-tag checklist
15+
16+
### 1. Code state
17+
18+
- [ ] Working tree clean (`git status` empty).
19+
- [ ] `git log --oneline main..HEAD` only contains commits intended for this
20+
audit. No experimental work, no half-finished refactors.
21+
- [ ] No `TODO`, `FIXME`, `XXX`, or `unimplemented!` markers in `program/src/`
22+
or `assertions/src/`. Anything genuinely unfinished should be reverted
23+
from the audit branch.
24+
- [ ] No `dbg!`, `println!`, `eprintln!`, or commented-out code in
25+
`program/src/` or `assertions/src/`.
26+
27+
```bash
28+
git grep -nE "TODO|FIXME|XXX|unimplemented!|dbg!|println!|eprintln!" -- program/src assertions/src
29+
```
30+
31+
### 2. Build verification
32+
33+
- [ ] `cargo build-sbf --features mainnet` succeeds. Record the SHA-256 of
34+
`target/deploy/lazorkit_program.so`.
35+
- [ ] `cargo build-sbf --features devnet` succeeds. Record the SHA-256.
36+
- [ ] The two hashes differ (verifies the dual-cluster mechanism is intact).
37+
- [ ] `cargo build-sbf` (no features) fails with the expected
38+
`compile_error!` message containing "pick exactly one cluster".
39+
- [ ] The CI `sbf-cluster-check` job is green for the tagged commit.
40+
41+
### 3. Test suite
42+
43+
- [ ] `cargo test --features devnet` — all unit + litesvm integration tests
44+
pass.
45+
- [ ] `cd tests-sdk && npm test` (with the validator running and the
46+
devnet-built `.so` loaded) — all integration tests pass.
47+
- [ ] No tests are skipped, ignored (`#[ignore]`), or commented out without
48+
a tracking issue.
49+
50+
```bash
51+
git grep -nE "#\[ignore\]|it\.skip|xit\(|describe\.skip" -- program tests-sdk
52+
```
53+
54+
### 4. Fee surface invariant (foundation build)
55+
56+
- [ ] `bash scripts/check-no-fee.sh` exits 0. The CI `check-no-fee` job is
57+
green for the tagged commit.
58+
- [ ] Diff against the previous audit-frozen tag has no new files matching
59+
paths in `scripts/fee-paths.txt`.
60+
61+
```bash
62+
git diff audit-frozen-v$(N-1)..HEAD -- 'program/src/state/protocol_config.rs' \
63+
'program/src/state/treasury_shard.rs' \
64+
'program/src/state/integrator_record.rs' \
65+
'program/src/processor/protocol/'
66+
# Should print nothing.
67+
```
68+
69+
### 5. Documentation alignment
70+
71+
- [ ] `CHANGELOG.md` has an entry under `[Unreleased]` describing every
72+
observable behavior change since the last tag.
73+
- [ ] `docs/Architecture.md` reflects new state accounts / instructions.
74+
- [ ] `docs/Costs.md` benchmarks have been re-measured if any instruction
75+
changed CU usage.
76+
- [ ] `SECURITY.md` references the auditor that's about to look at this
77+
revision (or notes the audit is in progress).
78+
- [ ] `program/src/lib.rs` `security_txt!` block — `auditors:` field still
79+
reflects the most recent finalised audit until this one ships.
80+
81+
### 6. Diff bounded
82+
83+
- [ ] Touched files are listed in the `Unreleased` CHANGELOG.
84+
- [ ] Each touched file has a clear reason in the commit message that
85+
introduced the change.
86+
87+
```bash
88+
git diff --name-only audit-frozen-v$(N-1)..HEAD | sort
89+
```
90+
91+
### 7. Audit packet contents
92+
93+
When you push the tag, also produce the audit packet:
94+
95+
- [ ] PDF / Markdown summary of the changes since the last tag (1–2 pages).
96+
- [ ] List of touched files with line counts.
97+
- [ ] CHANGELOG diff.
98+
- [ ] SHA-256 hashes of the `mainnet` and `devnet` SBF binaries.
99+
- [ ] Output of `solana-verify get-program-hash` against the binaries (so
100+
the auditor can verify they correspond to the source).
101+
- [ ] The two binaries themselves, if the auditor wants byte-level review.
102+
- [ ] Note any out-of-scope changes (typos, README edits, dependency bumps
103+
that don't affect program logic).
104+
105+
## Tagging
106+
107+
When every box is checked:
108+
109+
```bash
110+
git tag -a audit-frozen-vN -m "audit-frozen state submitted for Accretion review N"
111+
git push origin audit-frozen-vN
112+
```
113+
114+
The push to origin triggers `.github/workflows/release.yml`, which builds
115+
both feature variants, attaches the binaries + their hashes to a GitHub
116+
Release, and emits the `solana-verify` build attestation for reproducibility.
117+
118+
## Post-audit
119+
120+
When the audit comes back with findings:
121+
122+
1. Branch off the tagged state: `git checkout -b fix/audit-vN audit-frozen-vN`.
123+
2. Land each fix as its own commit with a `Fixes: <auditor-finding-id>` line.
124+
3. After all findings are addressed, repeat this checklist and tag
125+
`audit-frozen-vN.1` (or `vN+1` if the changes are substantial).
126+
4. Update `program/src/lib.rs` `security_txt!` `auditors:` field to reference
127+
the finalised audit report URL once the auditor publishes.

docs/MAINNET_DEPLOY.md

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# Mainnet Deployment Runbook (Foundation Build)
2+
3+
This runbook covers deploying `program-v2` (the no-fee foundation build) to the
4+
LazorKit mainnet program slot, and the binary swap back to `lazorkit-protocol`
5+
(the commercial build) at the end of the foundation contract.
6+
7+
The same on-chain slot — `LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi` — hosts
8+
both binaries at different times. dApp integrators keep using one stable
9+
program ID through the transition; only the on-chain behavior changes when
10+
the binary is swapped.
11+
12+
## Why this scheme
13+
14+
- **Foundation contract** requires a no-profit deploy (no admin, no protocol
15+
fees). `program-v2` ships exactly that.
16+
- **Brand continuity** — the mainnet program ID was published before the
17+
foundation contract. Switching IDs would force every integrating dApp to
18+
redeploy and migrate their on-chain references.
19+
- **Reversibility** — Solana program upgrades replace the binary at a slot.
20+
The same upgrade authority can swap from `program-v2``lazorkit-protocol`
21+
in one transaction once the foundation contract ends.
22+
23+
## Prerequisites
24+
25+
- Solana CLI 3.0.4 (pinned in `Cargo.toml [workspace.metadata.cli]`).
26+
- The mainnet program **upgrade authority** — a multisig keypair held jointly
27+
by the foundation and the lazorkit team. (If a single key holds the
28+
authority, transition to multisig before deploy — losing it locks the slot.)
29+
- The mainnet program **address keypair** for `LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi`
30+
(only needed for the *initial* deploy; subsequent upgrades use the upgrade
31+
authority).
32+
- A funded mainnet wallet for rent + transaction fees (~5 SOL leaves room).
33+
- Audit sign-off on the exact source revision being deployed (see
34+
`docs/AUDIT_PREP.md` for the pre-tag checklist).
35+
36+
## Initial deploy (program-v2 → mainnet slot)
37+
38+
1. Verify the source matches the audit-frozen tag:
39+
40+
```bash
41+
git fetch --tags
42+
git checkout audit-frozen-vN # whatever tag was audited
43+
git status # must be clean
44+
```
45+
46+
2. Confirm the workspace is on the right toolchain:
47+
48+
```bash
49+
cat Cargo.toml | grep -A1 'metadata.cli' # → solana = "3.0.4"
50+
solana --version # → must be 3.0.4
51+
rustup show active-toolchain # matches rust-toolchain.toml
52+
```
53+
54+
3. Build the mainnet binary:
55+
56+
```bash
57+
cd program
58+
cargo build-sbf --features mainnet
59+
cd ..
60+
```
61+
62+
4. Verify the embedded program ID:
63+
64+
```bash
65+
solana-keygen pubkey target/deploy/lazorkit_program-keypair.json
66+
# → must print LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi
67+
```
68+
69+
If the printed pubkey is anything else, the build picked up the wrong
70+
keypair. Replace `target/deploy/lazorkit_program-keypair.json` with the
71+
keypair file for `LazorjRF…` before deploying. **Do not deploy with a
72+
mismatched ID** — the binary's compile-time `crate::ID` reads from
73+
`assertions/src/lib.rs` (the `LazorjRF…` constant under the `mainnet`
74+
feature) and will fail every PDA derivation if loaded into a different
75+
slot.
76+
77+
5. Record the binary hash for the release artifact:
78+
79+
```bash
80+
shasum -a 256 target/deploy/lazorkit_program.so
81+
# Compare against the hash published in the GitHub Release for this tag.
82+
```
83+
84+
6. Deploy:
85+
86+
```bash
87+
solana program deploy target/deploy/lazorkit_program.so -u m \
88+
--program-id <path/to/LazorjRF-keypair.json> \
89+
--upgrade-authority <path/to/upgrade-authority.json>
90+
```
91+
92+
7. Verify the deployed program:
93+
94+
```bash
95+
solana program show LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi -u m
96+
# Look for: ProgramData Address, Authority, Last Deployed In Slot, Data Len
97+
```
98+
99+
The `Authority` line must match the multisig pubkey that should retain
100+
upgrade control.
101+
102+
8. Smoke-test on-chain:
103+
104+
- Create a wallet via the SDK pointing at the foundation flavor.
105+
- Verify no fee transfer occurs (compare lamport balances pre/post).
106+
- Run `solana account <walletPda>` and confirm the discriminator is `1`.
107+
108+
## Subsequent upgrades (program-v2 patch deploy)
109+
110+
For a routine upgrade of the foundation build (e.g., a security fix):
111+
112+
```bash
113+
git checkout <new-audit-frozen-tag>
114+
cd program && cargo build-sbf --features mainnet && cd ..
115+
solana program deploy target/deploy/lazorkit_program.so -u m \
116+
--program-id LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi \
117+
--upgrade-authority <path/to/upgrade-authority.json>
118+
```
119+
120+
Document each upgrade in `CHANGELOG.md` and update the on-chain `security.txt`
121+
metadata via the next build (the macro embeds `GITHUB_SHA` automatically when
122+
built in CI).
123+
124+
## Binary swap at contract end (program-v2 → lazorkit-protocol)
125+
126+
When the foundation contract ends and the slot needs to host the commercial
127+
build again:
128+
129+
1. In the **`lazorkit-protocol`** repo, check out the audit-frozen tag for
130+
the commercial build:
131+
132+
```bash
133+
cd ../lazorkit-protocol
134+
git checkout audit-frozen-commercial-vN
135+
```
136+
137+
2. Build the commercial mainnet binary:
138+
139+
```bash
140+
cd program && cargo build-sbf --features mainnet && cd ..
141+
```
142+
143+
3. Verify it embeds the same program ID (`LazorjRF…`):
144+
145+
```bash
146+
solana-keygen pubkey target/deploy/lazorkit_program-keypair.json
147+
```
148+
149+
4. Deploy the upgrade. **No `--program-id` flag** — the slot already exists,
150+
we're upgrading it:
151+
152+
```bash
153+
solana program deploy target/deploy/lazorkit_program.so -u m \
154+
--program-id LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi \
155+
--upgrade-authority <path/to/upgrade-authority.json>
156+
```
157+
158+
5. Initialise the protocol config (commercial-only instruction; no equivalent
159+
exists in the foundation build, so this is a fresh state):
160+
161+
```bash
162+
# Use the lazorkit-protocol SDK to send InitializeProtocol + InitializeTreasuryShard.
163+
# This step is only needed the first time the commercial build is on this
164+
# slot; subsequent upgrades preserve the existing ProtocolConfig PDA.
165+
```
166+
167+
6. Smoke-test:
168+
169+
- Create a wallet — the fee transfer to the treasury shard should succeed.
170+
- Verify the SDK consumer receives `protocolConfigPda` etc. without error.
171+
172+
## Pre-existing wallet accounts across the swap
173+
174+
Wallet, Vault, Authority, Session, and DeferredExec PDAs created under the
175+
foundation build remain valid and accessible under the commercial build —
176+
they all derive from the same program ID and the same seed schema, and the
177+
account-discriminator layout is identical between the two builds.
178+
179+
What changes:
180+
181+
- Fee accounts (`ProtocolConfig`, `FeeRecord`, `TreasuryShard`) start
182+
appearing once the commercial binary is live. Existing wallets continue to
183+
work; the fee is charged on subsequent `CreateWallet` / `Execute` /
184+
`ExecuteDeferred` calls if the SDK appends fee accounts.
185+
- Sessions created with action permissions under the foundation build remain
186+
enforced under the commercial build — action handling is identical.
187+
- The on-chain `security.txt` updates to advertise `lazorkit-protocol` as the
188+
source repo. Integrators querying it should re-fetch.
189+
190+
## Rollback
191+
192+
If a deployed upgrade misbehaves:
193+
194+
```bash
195+
solana program deploy target/deploy/<previous-good-binary>.so -u m \
196+
--program-id LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi \
197+
--upgrade-authority <path/to/upgrade-authority.json>
198+
```
199+
200+
Solana program slots are upgrade-replaceable (so long as the upgrade
201+
authority hasn't been frozen). Keep the previous mainnet binary archived
202+
locally + in the GitHub Release for at least one audit cycle so a rollback
203+
is one command, not a rebuild.
204+
205+
## Locking the upgrade authority
206+
207+
After the post-contract swap to `lazorkit-protocol` is stable and you no
208+
longer want any further upgrades:
209+
210+
```bash
211+
solana program set-upgrade-authority \
212+
LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi \
213+
--upgrade-authority <path/to/current-upgrade-authority.json> \
214+
--final
215+
```
216+
217+
This is **irreversible**. Only do this after a long stability window and
218+
explicit foundation/team agreement — once finalised, the binary cannot be
219+
patched, security advisories included.

0 commit comments

Comments
 (0)