Skip to content

feat: register and render the full API catalog (public + API-key endpoints)#1020

Merged
onchainlu merged 5 commits into
mainfrom
public-endpoint-notice
Jul 10, 2026
Merged

feat: register and render the full API catalog (public + API-key endpoints)#1020
onchainlu merged 5 commits into
mainfrom
public-endpoint-notice

Conversation

@onchainlu

@onchainlu onchainlu commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What

Endpoints declared in a merchant's openapi.json now register and render as part of the full API catalog, tagged by auth mode:

  • security: [] operations → registered, tagged Public (sky)
  • apiKey security-scheme operations → registered, tagged API key (muted)
  • SIWX keeps its existing Free tag; paid endpoints unchanged

Started as a copy fix for Telemost's report (/v1/catalog showed a "1 unprotected endpoint skipped — add security: []" warning they had already followed); expanded per team decision (rsproule: "can show and indicate that its apikey auth — openapi should be pretty standard around this") to render the whole catalog. Earlier commits on this branch implemented a neutral disclosure for skipped public endpoints; the final commit supersedes it — those endpoints now actually register.

How it works

  • Provenance gate: only the openapi discovery source produces catalog rows (lib/discovery/catalog-auth.ts, shared server+client). In @agentcash/discovery 1.7.5 the openapi source assigns unprotected solely for an explicit security: [] and apiKey solely for a declared security scheme — verified in the package source; discoverOriginSchema's openapi path does no probe augmentation. unprotected/apiKey from other sources still skip with the yellow warning, whose "declare security: []" advice now results in registration.
  • Storage: no schema change. Free rows are Resource records with zero Accepts, discriminated by metadata.authMode (siwx | unprotected | apiKey) — the existing SIWX pattern (registerSiwxResource generalized to registerFreeResource).
  • Orphan guard: catalog rows register in a second pass only when their origin gained ≥1 paid/SIWX resource in the same batch (per-origin, not global). Deliberately no "or the origin already has resources" arm: adversarial review showed that arm let a batch whose paid probes all failed transiently register catalog rows, set originId, and deprecate every existing paid row — with the catalog rows then keeping the origin alive as a self-sustaining catalog-only listing. The client mirrors the gate (catalog-only origins show their endpoints as skipped), the skip panel shows per-row reasons, and a regression test covers the transient-failure path. The tRPC noValidResources gate is unchanged.
  • Stale-tag defense: upsertResource (paid path) strips a pre-existing free authMode from metadata on merge, so a row that later turns paid stops matching free filters; listOriginsWithResources additionally guards with !resource.response.

Surfaces updated

  • Read filters: origin lists (displayableResourceWhere, listOrigins, listOriginsWithResources) and searchResources OR-in the three free auth modes (chain-filtered queries still exclude them — no chain to filter on).
  • Server pages: resource-card.tsx badges; origin-resources.tsx includes free rows.
  • Registration UI: pre-registration list shows Public/API key rows like SIWX (no strikethrough); the bulk-result banner counts them as registered; toast reads e.g. "20 registered, 1 public"; RegisterModeResourceList gets Public/API key badges with tooltips.
  • REST registry-register-origin response adds public/apiKey counts + details (additive; siwx fields unchanged).
  • Deprecation includes catalog rows in the active list, so an endpoint that drops security: [] deprecates on re-registration.

Not affected (checked)

Bazaar/agent feeds are accepts/transfer-driven and can't include free rows; chain-filtered listings exclude them; /api/x402/resources serializes them with accepts: [] exactly like SIWX today.

onchainlu added 2 commits July 9, 2026 16:55
The openapi discovery source only classifies an endpoint 'unprotected'
when its operation explicitly declares security: [] — the merchant
already opted it out. Both registration surfaces still lumped these
into the yellow 'unprotected endpoints skipped' warning whose copy
tells the merchant to add security: [] to suppress the notice, which
they had already done (reported by Telemost for /v1/catalog).

registerResourcesFromDiscovery now tags such skips explicitlyPublic,
and both the pre-registration form and the bulk-result panel render
them as a neutral 'N public endpoints not registered' disclosure,
reserving the warning for probe-inferred unprotected and apiKey
endpoints.
…nd client

The 'authMode unprotected + openapi source' provenance check was encoded
independently in register-origin.ts and the register form's skipped-endpoint
partition — extract it to lib/discovery/explicitly-public.ts so the two
surfaces can't drift. Also rename a test whose name said probe-inferred but
exercised the well-known source.
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
x402scan Ready Ready Preview, Comment Jul 10, 2026 5:20pm

Request Review

…oints)

Endpoints declared in openapi.json now register as free catalog rows
instead of being skipped: security: [] operations get a Public tag,
apiKey-scheme operations get an API key tag (SIWX keeps Free). The
discovery source gates this — unprotected/apiKey from non-openapi
sources still skip with the actionable warning, whose advice (declare
security: []) now actually results in registration.

Catalog rows never make an origin registrable on their own: they only
register alongside at least one paid/SIWX resource or into an origin
that already has resources.

registerSiwxResource generalizes to registerFreeResource(authMode);
rows are Resource records with zero Accepts discriminated by
metadata.authMode. upsertResource now strips a stale free authMode
when a row re-registers as paid. Read filters (origin lists, search),
server-page badges, registration UI, counts, and toasts all understand
the two new modes. This supersedes the earlier neutral 'public
endpoints not registered' disclosure, which is removed.
@onchainlu onchainlu changed the title fix: don't warn on endpoints explicitly public via OpenAPI security: [] feat: register and render the full API catalog (public + API-key endpoints) Jul 10, 2026
mainSucceeded was a global flag — in a multi-origin batch a paid success
for one origin would have let catalog rows register for another. Build
the set of origins that gained a paid/SIWX resource instead. Also adds
tests for the existing-origin gate arm and for a failed free
registration landing in failedDetails.
The 'origin already has resources' gate arm was dangerous: an existing
origin whose paid probes all failed transiently would still register
catalog rows, which set originId and let deprecation wipe every paid
row — and since getOriginResourceCount counts free rows, the catalog
rows then kept the origin alive as a catalog-only listing forever. The
gate is now strictly 'this batch registered a paid/SIWX resource for
this origin', with a regression test for the transient-failure path.

Also mirrors the gate client-side (a catalog-only origin's endpoints
now show as skipped in the register form instead of promising a
registration the server refuses) and renders the per-row skip reason
in the results panel — skips have three distinct causes now.
@onchainlu
onchainlu merged commit 24f0f31 into main Jul 10, 2026
3 checks passed
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.

1 participant