Skip to content

serviceability: add Feed catalog account and CRUD#3953

Open
nikw9944 wants to merge 1 commit into
mainfrom
nikw9944/infra-1700-1-feed-catalog
Open

serviceability: add Feed catalog account and CRUD#3953
nikw9944 wants to merge 1 commit into
mainfrom
nikw9944/infra-1700-1-feed-catalog

Conversation

@nikw9944

@nikw9944 nikw9944 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Stack — review/merge bottom-up. Replaces #3952.

  1. serviceability: add Feed catalog account and CRUD #3953 — Feed catalog account + CRUD
  2. serviceability: EdgeSeat feed-seat data model and SetAccessPassFeeds #3954 — EdgeSeat data model + SetAccessPassFeeds
  3. serviceability: enforce EdgeSeat feed metro gate at connect #3955 — metro gate enforcement at connect
  4. sdk: Feed and EdgeSeat read support for Go, Python, and TypeScript #3956 — Go/Python/TS SDK read support + fixtures

---## Summary

  • Add a serviceability Feed account: a metro(exchange) → group-set catalog, with create/update/delete managed by a catalog admin (FEED_AUTHORITY Permission or FOUNDATION). A feed with an empty metro map imposes no restriction.
  • Add Rust SDK command builders and a doublezero feed CLI (create/update/get/list/delete).
  • Part 1 of 4 of the serviceability feeds work (malbeclabs/infra#1700). No AccessPass/EdgeSeat changes here.

Testing Verification

  • Feed::groups_for unit tests (covered / not-covered / unrestricted) and borsh round-trip; SDK command-builder tests; CLI --metro EXCHANGE=GROUP,GROUP parsing.

Add a Feed account (metro(exchange) -> group-set catalog) with create/update/delete
managed by a catalog admin (FEED_AUTHORITY Permission or FOUNDATION), plus Rust SDK
command builders and a 'doublezero feed' CLI. A feed with no metros imposes no restriction.
@martinsander00

Copy link
Copy Markdown
Contributor

should we add tests/feed_test.rs covering create/update/delete success + all error/edge cases?

pub metros: Vec<(Pubkey, Vec<Pubkey>)>,
}

impl UpdateFeedCliCommand {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add a --clear-metros option?

@martinsander00 martinsander00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few pattern-based suggestions (from Karl's usual review patterns). None are correctness/security blockers — overall this looks good and cleanly scoped as Part 1. Comments inline.

pub name: String, // 4 + len
pub reference_count: u32, // 4 - number of access passes referencing this feed
/// `exchange_pk → group_pks`. Empty ⇒ no metro restriction.
pub metros: Vec<(Pubkey, Vec<Pubkey>)>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong/domain types over raw primitives. Consider a named type instead of the bare tuple, e.g. struct MetroGroups { exchange: Pubkey, groups: Vec<Pubkey> }. This (Pubkey, Vec<Pubkey>) shape is repeated across FeedCreateArgs, this account, the SDK command, and the CLI, and no other serviceability state models a map this way — a named type makes the (_, groups) / (exchange_pk, group_pks) destructures read as domain concepts.

if self.metros.is_empty() {
return FeedMetroMatch::Unrestricted;
}
match self.metros.iter().find(|(ex, _)| ex == exchange) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert on invariant violations rather than silently dedupe. .find() silently resolves a duplicate exchange key to the first entry. If metros ever contains the same exchange twice the underlying data is wrong — better to reject duplicate exchange keys in process_create_feed / process_update_feed (revert) than to silently pick one here.

metros: value.metros.clone(),
};

try_acc_create(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Program logs for state changes. There's no success msg! on create (the Created: log inside try_acc_create is #[cfg(test)]-gated), and update/delete log nothing either. Consider an explicit log for these state changes, and assert the exact log line in the integration tests.

"Invalid GlobalState Account Owner"
);
assert_eq!(
*system_program.unsigned_key(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant/inconsistent check. create asserts the system-program id but update/delete don't — inconsistent within the PR. Per programs/CLAUDE.md this check is unnecessary (the system interface builds its instruction with the system program as program-id and reverts automatically), so dropping it here to match update/delete and leaning on the revert would be more consistent. Minor.

) -> eyre::Result<()> {
let feeds = client.list_feed(ListFeedCommand)?;

let mut displays: Vec<FeedDisplay> = feeds

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: prefer turbofish at the callsite over annotating the binding — .collect::<Vec<FeedDisplay>>() (or just let the following sort_by infer it) rather than let mut displays: Vec<FeedDisplay> =.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants