sdk,cli: send the per-pass-type access pass removal instructions - #4268
Open
bgm-malbeclabs wants to merge 10 commits into
Open
sdk,cli: send the per-pass-type access pass removal instructions#4268bgm-malbeclabs wants to merge 10 commits into
bgm-malbeclabs wants to merge 10 commits into
Conversation
bgm-malbeclabs
force-pushed
the
access-pass-callers
branch
2 times, most recently
from
September 2, 2026 02:20
140b0cb to
9eb9e9c
Compare
bgm-malbeclabs
force-pushed
the
access-pass-program
branch
from
September 2, 2026 04:02
1d689b9 to
7cd6c0c
Compare
bgm-malbeclabs
force-pushed
the
access-pass-callers
branch
2 times, most recently
from
September 3, 2026 01:14
49bb9b1 to
c504dbe
Compare
user delete and access-pass close now require a --access-pass-type/--type flag naming the access pass kind (malbeclabs/infra#2470). Add the flag at every e2e call site, using the prepaid kind each test's own setup creates, and mark the four affected compatibility-matrix steps as known-incompatible below 0.39.0 since old CLI binaries send the now-deprecated instruction variants and lack the new flag.
bgm-malbeclabs
force-pushed
the
access-pass-program
branch
from
September 3, 2026 01:43
cbd95b6 to
af05027
Compare
bgm-malbeclabs
force-pushed
the
access-pass-callers
branch
from
September 3, 2026 01:43
c504dbe to
a361c76
Compare
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.
Part of malbeclabs/infra#2470. Child of malbeclabs/infra#2385.
Stacked on #4267. Merge that one first — this branch targets it, not
main.#4267 added one removal instruction per
AccessPassTypewithout changing anything for existing callers. This PR flips the switch: it deprecatesCloseAccessPass(variant 69) andDeleteUser(variant 42), and moves every caller in the repository onto the new instructions — the builders, both SDKs, the CLI, the client daemon, the stress tools and the e2e suite.The deprecation and the migration have to land together. CI proved it: splitting them left a branch whose own CLI could not delete a user.
Summary of Changes
doublezero access-pass closerequires a new--typeflag anddoublezero user deleterequires--access-pass-type, both with valuesprepaid,solana-validator,solana-rpc,others,edge-seat. Neither has a default, deliberately. A default would pick a kind for an operator who did not state one, which is the exact failure this change exists to prevent.close_access_passanddelete_userin the instruction crate now pick their variant from anAccessPassKindthe caller supplies.CloseAccessPassCommandandDeleteUserCommandcarry a requiredkindfield.Executor.DeleteUsertakes a kind. Its zero value isAccessPassKindUnspecifiedand is rejected, rather than silently meaningPrepaid— Go cannot make a parameter required, so the zero value has to fail loudly.CliAccessPassTypemoves out ofaccesspass/set.rsintoaccesspass/types.rssouser deletecan share it. No second enum was introduced.user deletetable insmartcontract/cli/README.mdgains the new flag.Where the declared kind comes from, and where it does not
The program's refusal only bites when the kind carries the caller's intent. A caller that reads the pass and picks the instruction from what it read has chosen the instruction from the very byte the program then checks, so the two always agree.
The guard is live on the two paths the issue is about: both CLI verbs, where an operator types the kind and nothing in between re-reads the pass.
Three callers deliberately read the kind back, because no operator exists to declare one. Each carries a comment saying so, and saying that the refusal cannot fire there:
doublezero disconnect— a self delete; the handler already checks the owner and the client IP.DeleteTenantCommandand its CLI equivalent — a cascade that sweeps users across mixed kinds. Forcing one declared kind would turn "delete every user under this tenant" into "delete only users of one kind", which strands the tenant record while it waits forreference_countto reach 0.That list is meant to stay at three. A fourth read-back is how the feature quietly becomes decoration.
Breaking
Old CLI binaries break, not only the oracle. A user on a pre-0.39.0
doublezero disconnectsends variant 42 and getsDeprecateduntil they upgrade.e2e/compatibility_test.gorecords this as fourknownIncompatibilitiesentries withbefore("0.39.0")— if the release lands as something other than 0.39.0 those entries silently stop matching and need one edit.One error-shape change worth knowing: the kind check runs before the existing authorisation checks, so a caller that used to get
NotAllowedorUnauthorizedmay now getAccessPassTypeMismatchfor the same transaction. That ordering is intentional, so a wrong-kind call fails for the reason it actually failed. Nothing in this repository branches on those codes.Diff Breakdown
The Code row overstates the production change: most of the growth in the Rust files is inline
#[cfg(test)]modules, which the categoriser cannot split out.disconnect.rs(+149) andsdk/rs/commands/user/delete.rs(+139) are the two largest, and both are mostly new tests.Key files (click to expand)
crates/doublezero-daemon-cli/src/disconnect.rs— derives the kind from the pass via the trait's existingget_accesspass, plus a test that seeds aSolanaValidatorpass and asserts that exact kind reachesdelete_user.smartcontract/sdk/rs/src/commands/user/delete.rs— thekindfield, and a second test using a distinct kind so a hardcoded value could not pass.crates/doublezero-serviceability-instruction/src/user.rs— variant selection, and the restoredmulticast_publisher_countwrite-back.smartcontract/sdk/go/serviceability/executor.go— the GoAccessPassKind, the selector, and the rejected zero value.smartcontract/cli/src/accesspass/close.rsandsrc/user/delete.rs— the two required flags.smartcontract/cli/src/accesspass/types.rs—CliAccessPassTypeand its conversion, moved here so both verbs share one enum.e2e/compatibility_test.go— the fourknownIncompatibilitiesentries, plus the flag on every invocation.Testing Verification
try_parse_fromtest: omitting the flag is an error, not a silent default.kindfield and hardcoded that literal would have passed.Prepaidpass and asserts the matching kind reachesdelete_user. Without it, a derivation that always returnedPrepaidwould breakdisconnectfor every non-prepaid user with no test noticing.access-pass set --accesspass-typecall in their own test before choosing a value.make rust-lintclean;cd smartcontract && make test-programs78 binaries green;rust-program-accounts-compatpass; Go serviceability andtools/stressgreen;go vet -tags e2eandgo build -tags e2eclean.TestE2E_BackwardCompatibilityneededcascadeKnownFail: trueonuser_delete. An older CLI cannot send the required flag, and every later delete phase depends on that user being gone, so without cascading each dependent step failed on its own for a reason already recorded. CI caught this on the first run.knownIncompatibilitiesentries can only be exercised against real older CLI images in CI.