Skip to content

feat(ra): AI Catalog generation, FQDN exclusivity, and seal-before-success activation#47

Draft
csnitker-godaddy wants to merge 1 commit into
mainfrom
claude/competent-johnson-ffac28
Draft

feat(ra): AI Catalog generation, FQDN exclusivity, and seal-before-success activation#47
csnitker-godaddy wants to merge 1 commit into
mainfrom
claude/competent-johnson-ffac28

Conversation

@csnitker-godaddy

@csnitker-godaddy csnitker-godaddy commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds producer-side AI Catalog artifact generation to the RA, plus the two invariants it depends on. Every byte is derived from the registration aggregate — nothing crawled, nothing fetched — and the registration request and 202 response are untouched.

What's in this PR

1. AI Catalog generation (IMPL slices 1–2)

  • internal/catalog (pure, no I/O): the per-agent CatalogEntry and the host-complete ai-catalog.json document. Eligibility gates (versioned, ACTIVE, an A2A/MCP endpoint with a policy-passing metaDataUrl), single vs nested entries, leftmost-DNS-label URN, Cc/Cf text sanitization, and an emitted-URL policy (absolute https, host == agentHost, no userinfo/query/fragment).
  • Routes (owner-scoped via ReadOwnership):
    • GET /v2/ans/agents/{agentId}/catalog-entry — the bare entry (application/json).
    • GET /v2/ans/agents/{agentId}/ai-catalog — the host-complete document (application/ai-catalog+json) with a strong ETag + If-None-Match/304. This is the literal ai-catalog.json an Agent-Host Provider republishes at https://{agentHost}/.well-known/ai-catalog.json.
  • spec/api-spec-v2.yaml and the embedded docsui copy updated.

2. FQDN exclusivity (one-host-one-owner)

Once a registration is live (ACTIVE/DEPRECATED) on an FQDN, a different owner may not register or activate on it — checked at register, verify-acme, and verify-dns. On activation the winner cancels losing pending registrations (no TL event, ANS-1 §4.4).

3. Seal-before-success activation (ANS-1 §12.3)

verify-dns seals the single terminal AGENT_REGISTERED event inline and reports ACTIVE only after the TL acknowledges. A failing or unconfigured sealer fails closed: 503 TL_UNAVAILABLE, the agent stays PENDING_DNS, nothing is committed, no outbox row. Revocation still rides the outbox. This is what guarantees a catalogued agent's SCITT-receipt and badge links point at TL records that actually exist.

The ai-catalog.json (the deliverable)

scripts/demo/ai-catalog.sh builds one host with a mixed population and saves the validated document to data/demo/ai-catalog.json:

agent status in document
v1.0.0 MCP + metaDataUrl ACTIVE, eligible
v2.0.0 A2A + metaDataUrl ACTIVE, eligible
v3.0.0 HTTP-API (no card) ACTIVE, ineligible
v4.0.0 MCP PENDING
eligible agent on a different host ACTIVE

It asserts host-completeness, the host object, sorted-by-version, per-card mediaType, the agent-scoped alias (byte-identical via any agentId), a deterministic ETag, and 304.

Quality gates

  • make check green at 90.2% coverage (internal/catalog at 100%); go test -race clean on the affected packages.
  • Demos pass end-to-end: run-lifecycle.sh (V2), run-lifecycle-v1.sh (V1), catalog.sh (per-agent entry), ai-catalog.sh (host document).
  • Pre-push adversarial review across five dimensions (shape/contract, correctness, security, quality-bar, tests) with each finding independently verified; confirmed findings addressed in this PR (mostly comment/test accuracy), refuted findings documented.

Known limitations / follow-ups

  • FQDN exclusivity is best-effort under concurrency. Two different owners each holding a PENDING_DNS registration on the same FQDN can race verify-dns and both reach ACTIVE (the check runs before the inline seal and outside the activation tx). An in-tx re-check would trade this for a sealed-but-not-activated orphan under seal-before-success, so the proper fix is a pre-seal host claim (mirroring the identity lane's nonce claim) — deferred. Documented in preflightRegistrationConflicts. The owner-scoped catalog read contains the blast radius.
  • Public discovery route not built. IMPL §6 models the catalog routes as public/unauthenticated; per an earlier maintainer call these are owner-scoped behind auth. The genuinely public host-discovery route GET /v2/ans/catalog?agentHost= (IMPL slice 3) is unbuilt — that's the surface an AHP fetches without owner credentials.
  • Catalog updatedAt uses registration/last-renewal time as a documented proxy for the spec's "activation/latest seal timestamp" (the RA persists no activation timestamp; the field is optional).

🤖 Generated with Claude Code

…s activation

Adds producer-side AI Catalog artifact generation to the RA, plus the two
invariants it leans on. Everything is derived from the registration
aggregate — nothing crawled, nothing fetched, and the registration request
and 202 response are untouched.

AI Catalog (IMPL slices 1-2)
- internal/catalog: pure generation of the per-agent CatalogEntry and the
  host-complete ai-catalog.json document. Eligibility gates (versioned,
  ACTIVE, A2A/MCP endpoint with a policy-passing metaDataUrl), single vs
  nested entries, leftmost-DNS-label URN, Cc/Cf text sanitization, and an
  emitted-URL policy (absolute https, host==agentHost, no
  userinfo/query/fragment).
- GET /v2/ans/agents/{agentId}/catalog-entry (bare entry) and
  GET /v2/ans/agents/{agentId}/ai-catalog (host-complete document with a
  strong ETag + If-None-Match/304). Owner-scoped via ReadOwnership.
- spec/api-spec-v2.yaml and the embedded docsui copy updated.

FQDN exclusivity (one-host-one-owner)
- Once a registration is live (ACTIVE/DEPRECATED) on an FQDN, a different
  owner may not register or activate on it; checked at register,
  verify-acme, and verify-dns. On activation the winner cancels losing
  pending registrations (no TL event, ANS-1 §4.4). The check is
  best-effort against a concurrent pending-window race (documented; a
  pre-seal host claim is the follow-up).

Seal-before-success activation (ANS-1 §12.3)
- verify-dns seals the single terminal AGENT_REGISTERED event INLINE and
  reports ACTIVE only after the TL acknowledges. A failing or unconfigured
  sealer fails closed: TL_UNAVAILABLE (503), the agent stays PENDING_DNS,
  nothing is committed, and no outbox row is written. Revocation still
  rides the outbox. This is what lets a catalogued agent's SCITT-receipt
  and badge links point at TL records that actually exist.

Tests and demos
- Unit coverage for catalog generation (100%), fail-closed activation
  (sealer error + nil sealer), and FQDN exclusivity. make check green at
  90.2%; race detector clean.
- scripts/demo/ai-catalog.sh produces and validates the host-complete
  ai-catalog.json (multi-agent host, ineligible/pending/other-host
  excluded, agent-scoped alias, deterministic ETag, 304). catalog.sh
  covers the per-agent entry scenarios. Shared demo helpers factored into
  common.sh.

Signed-off-by: Connor Snitker <csnitker@godaddy.com>
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