serviceability: a permissionless flag on Feed, declared not enforced - #4246
Open
Jared-dz wants to merge 1 commit into
Open
serviceability: a permissionless flag on Feed, declared not enforced#4246Jared-dz wants to merge 1 commit into
Jared-dz wants to merge 1 commit into
Conversation
RFC-1 (malbeclabs/infra#2390) needs the storefront to know whether a feed is offered without an access grant. Today that lives in a hand-maintained file in the website repo, so a feed and the claim about it are edited in two places and drift silently. This puts the claim on the feed. The flag is DECLARATIVE. No instruction reads it, no gate consults it, and the paid gate is still the EdgeSeat FeedSeat. That is not a shortcut: this program has no purchase path to gate. The only check a serviceability flag could have short-circuited is check_feed_metro_coverage, which is the check that a connection holds a paid seat -- turning that off would make a feed free, not self-serve. Gating a purchase belongs on the shreds side, where the purchase is (malbeclabs/infra#1704, closed not-planned). No migration, no backfill, verified rather than assumed: - Accounts. Feed::try_from is hand-written and deserializes field by field with unwrap_or_default(), so the 151 accounts already on mainnet read false at EOF. Pinned by a LegacyFeed encode/decode test that also asserts the fields BEFORE the new one still land correctly -- a decoder that mis-read the missing byte would corrupt those, not just the flag. - Instructions. borsh-incremental substitutes the default only when a field consumes zero bytes, which a trailing bool either does entirely or not at all, so it can never hit the partial-read error branch. Pinned by a test that serializes CreateFeed, pops the trailing byte to get exactly what an old client emits, submits the raw bytes, and asserts the feed is created reading false. FeedUpdateArgs.permissionless is Option<bool>, not bool, because the processor rejects an args value equal to the default as a no-op: a bare false would be indistinguishable from one, leaving no way to turn the flag back off. A test flips it on at create, off, and on again. The CLI mirrors that split -- `feed create --permissionless` is a presence flag, `feed update --permissionless true|false` takes a value -- and `feed list` gains a permissionless column in the table and both JSON forms. The golden table string is rebuilt by hand. All four decoders move together, since there is no IDL: Rust, Go (ReadU8() != 0, the Tenant idiom -- there is no ReadBool), Python and TypeScript. Each is EOF-safe already, and each gains a truncation test asserting a pre-flag account reads false. feed.bin goes 154 -> 155 bytes with permissionless: true, so a decoder that skips the byte is caught rather than passing by coincidence. Also corrected while these structs were open: the four Feed doc comments called one account a SKU. A SKU is every Feed sharing a code (malbeclabs/infra#2390); one account is a feed, and its pubkey is the feed_key. Same correction as doublezerofoundation/doublezero.xyz-new#1015. Tests: 322 program lib, 9 feed integration (2 new), 437 CLI, 200 SDK, plus Go, Python and TypeScript fixture suites. cargo fmt, gofmt and clippy clean on every touched crate.
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.
RFC-1 (malbeclabs/infra#2390) needs the storefront to know whether a feed is offered without an access grant. Today that claim lives in a hand-maintained file in the website repo, so a feed and the claim about it are edited in two places and drift silently. This puts the claim on the feed. It is PR 16 of RFC-1.
The flag is declarative, and that is the design
No instruction reads it. No gate consults it. The paid gate for a feed is still the EdgeSeat
FeedSeat.That is not a shortcut. This program has no purchase path to gate. The only check a serviceability flag could have short-circuited is
check_feed_metro_coverage, which is the check that a connection holds a paid seat — turning that off would make a feed free, not self-serve. Gating a purchase belongs where the purchase is, on the shreds side; malbeclabs/infra#1704 designed exactly that and was closed not-planned. If it is ever wanted, that is the account and that is the design. A reader who assumes this flag protects revenue is wrong, and the doc comment says so.No migration, no backfill — verified, not assumed
Accounts.
Feed::try_fromis hand-written and deserializes field by field withunwrap_or_default(), so the 151 accounts already on mainnet readfalseat EOF. Pinned by aLegacyFeedencode/decode test that also asserts the fields before the new one still land correctly — a decoder that mis-read the missing byte would corrupt those, not merely the flag.Instructions.
borsh-incrementalsubstitutes the default only when a field consumes zero bytes, and a trailingbooleither does that entirely or not at all, so it can never reach the partial-read error branch. Pinned by a test that serializesCreateFeed, pops the trailing byte to get exactly what an old client emits, submits the raw bytes, and asserts the feed is created readingfalse.Deploy ordering (RFC-1). The program must reach every cluster before any client that emits the flag. The failure in the wrong order is quiet rather than loud: an old program ignores trailing bytes, so a new CLI against an old program would create a feed with the flag silently dropped. Stated in the CHANGELOG, along with the note that an existing feed grows one byte on its first
UpdateFeed— including a--name-only update — with the rent delta charged to the signer.Option<bool>on update is load-bearingprocess_update_feedrejects an args value equal toFeedUpdateArgs::default()as a no-op. With a barebool, an update carrying onlypermissionless: falsewould be that default and be refused — the flag could never be turned off.Option<bool>keeps "set it false" distinct from "leave it alone", the same reasonTenantUpdateArgsuses it. A test flips it on at create, off, and on again.The CLI mirrors the split:
feed create --permissionlessis a presence flag,feed update --permissionless true|falsetakes a value.All four decoders, because there is no IDL
Rust, Go, Python and TypeScript move together. Go has no
ReadBool, so it uses the(reader.ReadU8() != 0)idiom the Tenant decoder already uses. Each is EOF-safe already, and each gains a truncation test asserting a pre-flag account readsfalse.feed.bingoes 154 → 155 bytes and the fixture setspermissionless: true, so a decoder that skips the byte is caught rather than passing by coincidence.make check-fixturesis clean.feed listgains apermissionlesscolumn, in the table and both JSON forms; the golden table string is rebuilt by hand.Also corrected
The four
Feeddoc comments called one account a SKU. Under malbeclabs/infra#2390 a SKU is everyFeedsharing acode— one account is a feed, and its pubkey is thefeed_key. Same correction already made in doublezerofoundation/doublezero.xyz-new#1015. These structs were open anyway.Checks
cargo fmt --all --check,gofmt -l, andcargo clippy --all-targetsclean on every touched crate.make check-fixturesclean.Not carried:
make generate-fixturesalso refreshes three generatorCargo.lockfiles that the v0.38.0 release left on 0.37.0. That churn is unrelated to this change and the Makefile excludes lockfiles from the drift check, so it is reverted here rather than folded in.