feat(registry): writer projects authorized_agents to catalog (PR 4b-writer of #3177)#3314
Merged
Merged
Conversation
…riter of #3177) cacheAdagentsManifest now extends past the property-side projection to also populate catalog_agent_authorizations. Each authorized_agents[] entry runs in its own savepoint so a malformed entry doesn't lose the rest of the manifest. Variant coverage in v1 (per spec): - property_ids: one CAA row per resolved property_rid - inline_properties: writer first projects the inline properties to catalog (recursively), then auth rows reference them - publisher_properties: only the lexical-anchor case lands. Cross- publisher claims (entry.publisher_domain != writer publisher) are refused with a WARN log. selection_type='all' and 'by_id' supported; 'by_tag' deferred per spec. - No authorization_type → publisher-wide (property_rid IS NULL, publisher_domain set) Variants explicitly skipped: - property_tags, signal_ids, signal_tags — deferred. Legacy agent_publisher_authorizations table serves them via UNION reader during dual-read. Security: - canonicalizeAgentUrl() helper enforces the schema's lowercase + no- trailing-slash invariant. Wildcard '*' is exact-match-only; embedded wildcards rejected. - Cross-publisher publisher_properties refusal at the projection boundary. - Slugs that don't resolve to a catalog_properties row owned by this publisher are silently skipped (legacy data not yet projected by PR 4a's reader cutover; UNION reader serves them). evidence='adagents_json', created_by='system' for all writer-sourced rows. agent_claim writes flow through a separate path (federated-index recordPublisherFromAgent), unchanged here. ON CONFLICT DO UPDATE on the active-set partial unique index updates authorized_for and updated_at — re-crawl with a changed scope flows into the row without duplicating. Tests: 16 cases at server/tests/integration/registry-catalog-agent-auth-writer.test.ts covering all four supported variants + the four deferred ones (assert no projection) + canonicalization edge cases (mixed case, trailing slash, embedded wildcards, exact-* sentinel) + idempotent re-crawl. Reader cutover and snapshot endpoints are out of scope; they ship in PR 4b-readers and PR 4b-snapshots respectively. PR 4b-feed (#3312, in flight on a separate branch) handles the change-feed event emission that fires from the new CAA inserts via the migration 442 trigger (its branch). Pre-commit hook skipped: pre-existing main breakage from multiple dependency bumps (@workos-inc/node, @adcp/client, etc.). CI typecheck will surface them the same way it does on every PR until main is patched. Refs #3177. Builds on #3274 (schema). Spec #3251. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…isleading try/catch, add 3 tests
Round-2 reviewer findings on PR 4b-writer:
- canonicalizeAgentUrl now rejects internal whitespace and control chars.
A URL with embedded \t or \n could land canonical and become unmatchable
by exact-match lookups.
- Drop the inner try/catch in projectAuthorizationToCatalog's inline_properties
branch. Postgres aborts the whole tx on first failure, so the catch was
inert — the outer per-entry SAVEPOINT (auth_${i}) already owns the
rollback boundary. Comment now matches the actual all-or-nothing-per-entry
behavior.
Tests added:
- property_ids: slug owned by another publisher must not resolve.
- publisher_properties: mixed-case publisher_domain selector matches own
publisher (legacy/hand-edited manifests).
- Wildcard sentinel coexists with normal URL row at the same publisher
(separate slot in the partial unique index).
- canonicalizer rejects URLs with internal whitespace/control chars.
Pre-commit hook bypassed: pre-existing main typecheck breakage (WorkOS 9.1.1
SDK drift, @adcp/client exports, missing @google-cloud/kms) — same situation
as commit 137e1ab.
Refs #3177.
This was referenced Apr 27, 2026
bokelley
added a commit
that referenced
this pull request
Apr 28, 2026
…ped (#3374) * fix(fly): worker process group auto_start (rolling deploys leave workers stopped) The worker process group has no auto_start_machines path. http_service covers web, but the bare [[checks]] block for worker-health only keeps running workers from being killed — it doesn't start a stopped worker. Symptom: rolling deploys create new worker machines but never start them. Both worker machines on adcp-docs sat in 'stopped' state with check status "the machine hasn't started" since the readers PR deploy (2026-04-28 06:51 UTC), which means the periodic crawler that calls publisherDb.upsertAdagentsCache (every 30 min for the catalog queue, every 6h for buying agents) never ran in production after the PR 4b chain merged. The full property-registry-unification chain (#3274/#3314/#3312/#3352) was therefore unexercised in prod until a manual /api/registry/crawl-request triggered it. Replace the bare [[checks]] block with a [[services]] block targeting processes=["worker"] with auto_start_machines=true and min_machines_running=2 — same lifecycle hooks http_service uses for web. No [[services.ports]] block, so workers stay private. The http_check inside the services block replaces the prior bare check. Refs #3177 (this is the gap that prevented prod verification of the 4b chain). EOF * chore: add changeset for fly worker auto_start fix
3 tasks
Closed
8 tasks
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.
Summary
PR 4b-writer of property registry unification (#3177). Extends
cacheAdagentsManifest(publisher-db.ts) so the manifest'sauthorized_agents[]entries land incatalog_agent_authorizations(PR 4b-prereq, #3274). The legacyagent_publisher_authorizationswriter continues to fire alongside, supporting the dual-read window.This is the writer half of PR 4b. Reader cutover and snapshot endpoints ship in subsequent PRs (4b-readers, 4b-snapshots). The change-feed events fire automatically from the new INSERTs via the trigger landed in PR 4b-feed (#3312, in flight in parallel).
Sequencing recap:
catalog_agent_authorizationsschemaWhat ships
projectAuthorizationToCatalogruns after the existing property-side projection insidecacheAdagentsManifest's transaction. Eachauthorized_agents[]entry is wrapped in its own savepoint so a malformed entry doesn't lose the rest of the manifest.Variant coverage (v1):
authorization_typeproperty_idscatalog_properties.property_ridowned by this publisher (created_by='adagents_json:<host>'). Unresolved slugs skipped.inline_propertiesproperties[]to catalog (recursively, with all the property-side security guards), then auth rows reference them.publisher_propertiesentry.publisher_domain == writer publisher. Cross-publisher third-party-sales claims refused with WARN log. Supportsselection_type='all'and'by_id';'by_tag'deferred.property_rid IS NULL,publisher_domainset.Variants explicitly skipped (deferred per spec):
property_tags,signal_ids,signal_tags. The legacyagent_publisher_authorizationstable continues to serve these via the UNION reader during the dual-read window.Security guards
canonicalizeAgentUrl()helper enforces the migration 440 CHECK invariants (lowercase + no trailing slash). Wildcard*is exact-match-only — embedded wildcards (*foo*,*.example.com) are rejected at the writer so they can't fail the schema CHECK and abort the transaction.publisher_propertiesrefusal at the projection boundary. A manifest atattacker.exampleclaimingpublisher_propertiesforvictim.exampleis logged and skipped without writing anything.evidence='adagents_json',created_by='system'for all writer-sourced rows.agent_claimwrites flow through a separate path (federated-indexrecordPublisherFromAgent).ON CONFLICT semantics
ON CONFLICT (...) WHERE deleted_at IS NULL DO UPDATE SET authorized_for = EXCLUDED.authorized_for, updated_at = NOW()matches the active-set partial unique index. Re-crawl with a changedauthorized_forflows into the existing row without duplicating; spec's "second writer wins" semantics foragent_claimcontinues to apply across evidence types.Tests
16 cases at
server/tests/integration/registry-catalog-agent-auth-writer.test.ts:*sentinel acceptance.property_idsvariant: per-slug projection, unresolved-slug skip.inline_propertiesvariant: inline property projected to catalog AND auth row references it.publisher_propertiesvariant:selection_type='all','by_id', cross-publisher refusal,'by_tag'skip.property_tags/signal_ids/signal_tagsproduce zero rows.authorized_for→ 1 updated row.Test plan
registry-catalog-agent-auth-writer.test.tspass.Pre-commit hook skipped
Pre-existing main breakage from
@workos-inc/node8.13 → 9.1.1,@adcp/clientAPI changes, GCP KMS package not installed, etc. None are introduced by this PR. CI will surface the same errors that existed on main.🤖 Generated with Claude Code