feat(P1+P2+P4): session action permissions + dual-cluster + release infra - #51
Merged
onspeedhp merged 11 commits intoMay 6, 2026
Merged
Conversation
Port the 8 session action types (SolLimit, SolRecurringLimit, SolMaxPerTx, TokenLimit, TokenRecurringLimit, TokenMaxPerTx, ProgramWhitelist, ProgramBlacklist) plus parser, validator, and 30 unit tests verbatim from lazorkit-protocol. Type discriminators (1, 2, 3, 4, 5, 6, 10, 11) and the 11-byte header layout are preserved so the unified SDK can encode actions identically for both programs. Foundation only — does not yet wire actions into CreateSession or enforce them at Execute. Those land in follow-up commits (P1b, P1c). Notes: - action.rs is byte-identical to lazorkit-protocol@HEAD for audit comparability; existing harmless `unused_mut` warning ported as-is. - AuthError numeric codes 3020-3029 mirror lazorkit-protocol so the unified SDK can decode action errors uniformly.
…reation Port from lazorkit-protocol the variable-size SessionAccount and the ParsedCreateSessionArgs flow that lets a CreateSession instruction carry an optional 8-action permission buffer. state/session.rs is now byte-identical to upstream: - exposes SESSION_HEADER_SIZE (80) constant - adds has_actions() and actions_slice() helpers - documents that optional actions follow the fixed header processor/create_session.rs: - replaces CreateSessionArgs with ParsedCreateSessionArgs that parses [session_key(32)][expires_at(8)][actions_len(2)?][actions(N)?] - caps actions_len at 2048 to prevent BPF heap exhaustion - runs validate_actions_buffer() at creation time - session PDA size is now SESSION_HEADER_SIZE + actions_bytes.len() - ed25519 + secp256r1 signed payloads include actions_bytes so the permission set is bound to the signature Wire-up only: actions are stored on-chain but not yet enforced at Execute time. P1c will port execute/actions.rs to add enforcement. Backwards compatibility note: clients still sending exactly 40 bytes (no actions_len header) continue to work via the legacy parser branch, matching upstream behavior.
…ream
Adds CompactInstructionRef::{from_bytes, decompress}, DecompressedInstructionRef,
and parse_compact_instructions_ref_with_len from lazorkit-protocol. Lets the
Execute hot path parse and decompress without per-instruction Vec<u8> allocs
for account-index bytes or instruction data.
Existing parse_compact_instructions / serialize_compact_instructions remain
for callers (execute_deferred) that still need owned copies. File is now
byte-identical to upstream compact.rs for audit comparability.
Wires the 8 session action types (ported in earlier commits) into the
Execute instruction. Sessions with attached actions now have spending
limits, recurring caps, per-tx maxes, and program whitelist/blacklist
rules enforced around the CPI loop.
Files:
- processor/execute_actions.rs (NEW, 1644 lines, byte-identical to upstream
processor/execute/actions.rs): the enforcement engine — pre-CPI program
whitelist/blacklist checks, token-balance + token-authority snapshots,
post-CPI delta computation, SOL/token cap enforcement with saturating
arithmetic, recurring window resets aligned to slot boundaries.
- processor/execute.rs (REPLACED with upstream processor/execute/immediate.rs):
integrates pre/post action evaluation around the CPI loop, snapshots
vault metadata + token authorities for invariant checks, tracks gross
SOL outflow per CPI for SolMaxPerTx. Adds L5 anti-CPI guard for
session-authenticated Execute (stack height must be 1).
- error.rs: adds SessionVaultOwnerChanged (3030), SessionVaultDataLenChanged
(3031), SessionTokenAuthorityChanged (3032) for the vault-invariant
defenses against System::Assign and SetAuthority escapes.
- processor/mod.rs: wires execute_actions module.
Verification:
- 111 unit tests pass (action validator + execute_actions helpers)
- 18 litesvm integration tests pass (wallet_lifecycle, sessions, etc.)
- Existing devnet program ID FLb7… still works for backward compatibility
Note on file structure: upstream organizes execute as a subdir
(processor/execute/{actions,immediate,...}.rs); program-v2 keeps the flat
layout for now, putting the helpers in processor/execute_actions.rs and
the immediate-execute logic in processor/execute.rs. Future P2 may
restructure to subdir for closer upstream alignment.
Apply Pattern D from lazorkit-protocol PR #9: the embedded program ID is now chosen by `--features mainnet` or `--features devnet`, with a `compile_error!` if neither (or both) is set. Prevents accidental cross-cluster deploys — a binary compiled with one ID malfunctions if deployed to the other cluster's slot. Mainnet feature embeds LazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi — the SAME program ID as lazorkit-protocol. program-v2 (foundation, no-fee build) occupies that mainnet slot for the duration of the foundation contract; at contract end the upgrade authority swaps the binary at the same slot to lazorkit-protocol's commercial build. dApp integrators keep one stable program ID through the transition. Devnet feature keeps program-v2's existing FLb7fyAtkfA4TSa2uYcAT8QKHd2pkoMHgmqfnXFXo7ao. Build verification: cargo build-sbf --features mainnet → sha differs from devnet cargo build-sbf --features devnet → sha differs from mainnet cargo build-sbf → fails with compile_error cargo build-sbf --features mainnet --features devnet → fails
Add solana-security-txt + default-env deps and embed a `security_txt!` block in program/src/lib.rs so on-chain inspectors (and security researchers) get a self-described pointer to the SECURITY.md, contact endpoints, source repo, and audit report. Identifies the binary as the "Foundation Build" of LazorKit Smart Wallet to distinguish it from the lazorkit-protocol commercial binary that may later occupy the same mainnet slot. source_revision and source_release are populated from GITHUB_SHA / GITHUB_REF_NAME at CI build time. Audit pointer is the existing Accretion Labs report shipped under audits/.
- scripts/build-all.sh now takes a cluster argument (mainnet|devnet) and passes it through to cargo build-sbf. The program ID for IDL generation is derived from the resulting keypair instead of being passed in. - tests-sdk/package.json validator:start now builds with --features devnet before launching solana-test-validator and reads the program ID via solana-keygen pubkey on the keypair file (matching upstream pattern). - DEVELOPMENT.md updated with the new build invocations, --features devnet for cargo test, and a "Mainnet Deploy Strategy" section documenting the slot-sharing arrangement with lazorkit-protocol and the binary swap at contract end. - Cargo.lock regenerated for the security-txt deps added in the previous commit.
Verifies the dual-cluster mechanism stays intact:
1. cargo build-sbf --features mainnet succeeds
2. cargo build-sbf --features devnet succeeds
3. The two .so binaries differ (catches a refactor that neutralises
the cfg gate on declare_id!)
4. cargo build-sbf with no feature fails with the expected
"pick exactly one cluster" compile_error
5. cargo build-sbf with both features fails the same way
Workflow is byte-identical to upstream's. Triggers on PRs touching
program/ or assertions/, and on pushes to main.
Captures everything landed since the last audit cycle: - 8 session action permission types (state + parser + validator + tests) - SessionAccount variable-size + CreateSession action buffer support - Pre-CPI program whitelist/blacklist + post-CPI spending caps - Vault + token-account invariant defenses (3030-3032 errors) - Anti-CPI guard for session-authenticated Execute - Zero-copy CompactInstructionRef parser - Dual-cluster Cargo features (mainnet/devnet) with compile_error guard - security.txt embedded via solana-security-txt - Cherry-pick guardrails (fee-paths.txt + scripts + CI workflow) - sbf-cluster-check CI workflow - build-all.sh refactored for feature-flagged builds; sync-program-id.sh removed
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.
Triggered by audit-frozen-v* and v*.*.* tags. Builds both mainnet and devnet SBF binaries, records SHA-256 hashes, asserts they differ (catches a regression of the dual-cluster mechanism), and uploads the binaries + IDL + a manifest as GitHub Release assets. Manifest captures the build environment (Solana CLI version, rustc version, commit SHA) and the reproduction commands so anyone can locally rebuild and confirm the published hashes match. audit-frozen-v* tags create draft + prerelease releases (the artifact is for the auditor, not for end-users); v*.*.* tags create normal releases. Production deploys must consume binaries from a release — see docs/MAINNET_DEPLOY.md. GITHUB_SHA + GITHUB_REF_NAME are passed through to cargo build-sbf so the embedded security.txt advertises the exact source revision.
7 tasks
onspeedhp
force-pushed
the
chore/cherry-pick-guardrails
branch
from
May 6, 2026 11:52
3fc7a7d to
f932074
Compare
onspeedhp
force-pushed
the
feat/session-action-permissions
branch
from
May 6, 2026 11:52
b68a5eb to
9f77dcd
Compare
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacks P1, P2, and P4 of the slot-share migration plan onto P0 (#50). Brings program-v2 to feature parity with
lazorkit-protocolfor everything except the fee/admin surface.Base:
chore/cherry-pick-guardrails(PR #50). Merge that first.P1 — Session action permissions (delta-audit)
Ports the 8 session action types + enforcement engine from
lazorkit-protocol. The actual feature foundation contract clients will use.4 commits
feat(state)—program/src/state/action.rs(NEW, 697 lines, byte-identical with upstream). Defines 8 action types:SolLimit,SolRecurringLimit,SolMaxPerTx,TokenLimit,TokenRecurringLimit,TokenMaxPerTx,ProgramWhitelist,ProgramBlacklist. Type discriminators (1, 2, 3, 4, 5, 6, 10, 11) and 11-byte header (type:u8 | data_len:u16 LE | expires_at:u64 LE) preserved for SDK compat.feat(session)— extendsSessionAccountwith optional action buffer;ParsedCreateSessionArgsparses[session_key(32)][expires_at(8)][actions_len(2)?][actions(N)?]; legacy 40-byte parser branch keeps backwards-compat for old clients.chore(compact)— ports zero-copyparse_compact_instructions_ref_with_len+DecompressedInstructionReffrom upstream for the Execute hot path.feat(execute)—processor/execute_actions.rs(NEW, 1644 lines, byte-identical with upstream): pre-CPI program whitelist/blacklist, token snapshot, vault metadata snapshot; post-CPI delta computation + SOL/token cap enforcement with saturating arithmetic + recurring window resets aligned to slot boundaries; vault invariant defenses (3030-3032) against System::Assign / SetAuthority escapes.processor/execute.rsreplaced with upstream'sprocessor/execute/immediate.rs(1 import-path line different due to flat-vs-nested layout).P2 — Dual-cluster + security_txt (zero-audit, mechanical)
4 commits
feat(build)— Pattern D feature flags.cargo build-sbf --features mainnetembedsLazorjRFNavitUaBu5m3WaNPjU1maipvSW2rZfAFAKi(slot shared withlazorkit-protocolper slot-share strategy).--features devnetembedsFLb7…. No-feature build →compile_error!.feat(program)— embedssecurity_txt!with program-v2-specific URLs + Accretion audit pointer.chore(build)— adaptsscripts/build-all.sh,tests-sdk/package.json,DEVELOPMENT.mdto dual-cluster.ci—sbf-cluster-checkworkflow verifies mainnet ≠ devnet binaries + no-feature build fails as expected.P4 — Release infrastructure (no audit)
3 commits
docs(changelog)— records P1 + P2 additions under `[Unreleased]`.docs(deploy)— mainnet deploy runbook + audit-frozen tag checklist (indocs/MAINNET_DEPLOY_RUNBOOK.md).ci(release)— tagged-release workflow with verified-build hashes for both feature builds.Test plan
feat/use-sdk-legacy) which depends on thisAudit context
This is the largest delta-audit ask in the plan. Action enforcement engine + execute integration are new code paths. See `docs/audit/DELTA_BRIEF.md` (added in next PR) for structured questions to Accretion. Byte-identity with already-audited `lazorkit-protocol` is documented per-file.
🤖 Generated with Claude Code