Skip to content

feat: store and display resources by HTTP method#947

Merged
onchainlu merged 11 commits into
mainfrom
worktree-fix-duplicate-resources
Jun 3, 2026
Merged

feat: store and display resources by HTTP method#947
onchainlu merged 11 commits into
mainfrom
worktree-fix-duplicate-resources

Conversation

@onchainlu

@onchainlu onchainlu commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Same URL with different HTTP methods (e.g. GET /agent/credits vs POST /agent/credits) are now stored and displayed as distinct resources, matching how mppscan handles this.

DB

  • Added method column to Resources with @@unique([resource, method])
  • Migration defaults existing rows to "" (empty string) — no display change for existing resources, no backfill needed
  • New registrations store the actual method from discovery (e.g. "GET", "POST")
  • upsertResource, registerResource, registerSiwxResource all use compound key (resource, method)

Registration

  • Method source is discovery (OpenAPI spec), not the probe. The probe prefers POST when multiple methods return 402 (most x402 middleware fires before method routing), and while it does respect a preferredMethod param, the probe-determined method was being stored directly. Now we explicitly pass the discovery method through options.method and use that for DB storage.
  • When method is unknown (single-endpoint registration, facilitator sync), stores "" — preserves legacy behavior

Deprecation

  • deprecateStaleResources refactored: simple indexed query + application-side comparison instead of complex Prisma NOT:[array] pattern
  • Fetches all active resources for the origin, compares method::url keys in code, updates stale IDs
  • Active list is built from actual registration results (with method), not the full discovery input — prevents unprotected endpoints that share a URL with a registered endpoint (e.g. GET /campaigns unprotected + POST /campaigns paid) from shielding stale rows
  • Old method="" rows are deprecated when an origin is re-registered with method-specific rows

UI

  • use-discovery.ts returns DiscoveredResource[] (with method) instead of string[]
  • Registration page shows method badges (e.g. POST /campaigns, GET /agent/credits) when duplicate URLs exist
  • RegisterModeResourceList shows a Method column when needed
  • Resource listing page uses DB method when set, falls back to schema inference for legacy method="" rows
  • All internal keying uses composite METHOD::url keys via resourceKey() utility

Other

  • checkRegistered tRPC route accepts {url, method}[] for compound-key matching
  • FailedResource type includes optional method

Test plan

  • Enter api.iris-ai.dev on register page — shows 3 distinct resources with method badges
  • Register the origin — creates 3 method-specific rows, deprecates old method="" rows
  • Resource listing page shows correct methods (POST /agent/credits, GET /agent/credits, POST /campaigns)
  • Existing prod resources (no method) display unchanged (inferred from schema)
  • Verified on preview DB: deprecation works, old rows marked deprecated, 3 active resources
  • pnpm format && pnpm lint pass
  • 133 tests pass including new tests for resourceKey, upsertResourceSchema method handling, and getBazaarMethod inference

Same URL with different HTTP methods (e.g. GET /agent/credits vs POST
/agent/credits) are now distinct resources throughout the system.

DB: add `method` column to Resources with @@unique([resource, method]).
Registration: thread method through registerResource, registerSiwxResource,
and deprecateStaleResources.
UI: pass DiscoveredResource[] (with method) through the discovery hook
and show method badges when duplicate URLs appear.
@vercel

vercel Bot commented Jun 2, 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 Jun 3, 2026 2:07pm

Request Review

The x402 probe always returns POST (payment protocol), but the OpenAPI
spec has the correct method (GET vs POST). Prefer discovery method when
registering, and use the DB method column on the resource listing page
instead of inferring from the schema.
onchainlu added 2 commits June 2, 2026 15:57
NOT: [{cond1}, {cond2}] (array) is the standard Prisma way to express
"NOT cond1 AND NOT cond2", equivalent to "NOT (cond1 OR cond2)". The
previous NOT: { OR: [...] } nested syntax may not generate correct SQL.
The x402 probe always returns POST (payment protocol), which is not the
endpoint's actual HTTP method. Only use the discovery method or default
to GET.
The active resources list for deprecation was built from the full
discovery input (68 endpoints for iris), including 65 unprotected
endpoints that were skipped during registration. This prevented old
stale rows (like GET /campaigns defaulted from migration) from being
deprecated, since they appeared in the "don't deprecate" list.

Now only resources that were actually registered (paid + siwx) are
included in the active list.
The deprecation active list was filtering discovery resources by URL,
but the same URL can appear with different methods (e.g. GET /campaigns
is unprotected, POST /campaigns is paid). Matching by URL alone included
the unprotected GET version in the active list, preventing deprecation
of stale GET rows from the migration.

Now successfulResults and siwxResults carry the method that was actually
registered, so the active list is built directly from results without
re-deriving from discovery input.
Existing resources should not change display behavior. Empty string
means "legacy — infer method from schema" (preserves current behavior).
Non-empty values (GET, POST, etc.) are set by new discovery-based
registrations going forward. No backfill needed.

Also removes debug logging from previous commit.
Replace complex Prisma NOT:[{cond1},{cond2},...] pattern with a simple
indexed query + application-side comparison. Fetch all active resources
for the origin, compare keys in code, update stale ones by ID.
…arMethod

- resourceKey: composite keys, empty string handling, distinct keys
  for different methods on same URL
- upsertResourceSchema: method defaults to empty string, preserves
  explicit method
- getBazaarMethod: method inference from schema (explicit, body→POST,
  queryParams→GET, null→GET fallback)
@onchainlu onchainlu merged commit 12cf0b1 into main Jun 3, 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.

2 participants