Skip to content

feat(adagents): ads.txt MANAGERDOMAIN fallback discovery (#704)#705

Merged
bokelley merged 2 commits into
mainfrom
claude/issue-704-managerdomain-fallback
May 12, 2026
Merged

feat(adagents): ads.txt MANAGERDOMAIN fallback discovery (#704)#705
bokelley merged 2 commits into
mainfrom
claude/issue-704-managerdomain-fallback

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Closes #704.

Summary

  • Implements RFC 4175 one-hop ads.txt MANAGERDOMAIN fallback for adagents.json discovery. When /.well-known/adagents.json returns 404, consults /ads.txt for a MANAGERDOMAIN= directive and attempts discovery on the manager.
  • Adds AdAgentsValidationResult and the DiscoveryMethod literal ('direct' | 'authoritative_location' | 'ads_txt_managerdomain') to the public API, with a new validate_adagents_domain() that returns provenance (discovery_method, manager_domain). fetch_adagents transparently benefits from the fallback while keeping its existing dict return shape.

Behavior

  • MANAGERDOMAIN= directive form only — # managerdomain= comment lines are rejected.
  • Duplicate MANAGERDOMAIN lines: last-wins (IAB resolution per adcp#4173).
  • One-hop only; no further fallback from the manager.
  • Manager-domain 404 → terminal failure with discovery_method and manager_domain preserved for diagnostics (not a silent pass).
  • Cycle to source publisher → no hop attempted; recorded in errors.
  • Manager fetch uses a fresh client to avoid leaking caller credentials cross-origin.

Test plan

  • tests/test_adagents.py::TestParseManagerdomains — directive parsing, case-insensitivity, comment rejection, inline-comment handling, order preservation.
  • tests/test_adagents.py::TestValidateAdagentsDomain — direct, authoritative_location, ads_txt_managerdomain, duplicate-last-wins, manager 404 terminal failure, cycle detection, comment-form not followed.
  • tests/test_public_api.py::test_public_api_surface_matches_snapshot — snapshot regenerated for the three new exports.
  • ruff / mypy / black clean.

Refs: adcontextprotocol/adcp#4173, adcontextprotocol/adcp#4175

🤖 Generated with Claude Code

bokelley added a commit that referenced this pull request May 12, 2026
… fallback

Reviewer follow-ups on #705 / #704:

- SSRF: a publisher-controlled MANAGERDOMAIN=169.254.169.254 (or any
  loopback / private / link-local literal) would have passed
  `_validate_publisher_domain` and forced the SDK into a cross-origin
  HTTPS GET against internal services. The fallback now routes every
  manager domain through `_ensure_safe_manager_domain`, which composes
  the existing `_validate_redirect_url` SSRF gate with the publisher
  domain validator.
- ads.txt size: the body was passed to `_parse_managerdomains` with no
  cap, so a hostile publisher could force the SDK to buffer arbitrary
  data. Bodies over MAX_ADS_TXT_BYTES (1 MiB) are now discarded.
- Redirect-target 404: a 404 from a followed authoritative_location
  target previously bubbled up as AdagentsNotFoundError and triggered
  the ads.txt MANAGERDOMAIN fallback on the original publisher, which
  conflates a broken pointer with a missing manifest. It now surfaces
  as AdagentsValidationError so the fallback only fires on the
  publisher's own direct 404.

Also documents the publisher-scope caveat on `validate_adagents_domain`
— callers wiring `ads_txt_managerdomain` results into authorization
decisions must verify the manager's adagents.json explicitly scopes
the source publisher (per the TS reference's `hasExplicitPublisherScope`
gate). Adding that gate as a default is tracked as a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley and others added 2 commits May 12, 2026 14:30
Implements RFC 4175: when /.well-known/adagents.json returns 404 on a
publisher, consult /ads.txt for a MANAGERDOMAIN= directive and attempt
discovery one hop later on the manager. The fallback only kicks in for
the directive form — pure-comment lines like `# managerdomain=` are
rejected — and follows IAB last-wins resolution when a file declares
multiple MANAGERDOMAIN entries. Manager-domain 404 is a terminal
failure, not a silent pass.

`AdAgentsValidationResult` and the `DiscoveryMethod` literal are added
to the public API, exposing which path produced the data (direct,
authoritative_location, or ads_txt_managerdomain) plus the resolved
`manager_domain` for diagnostics. `fetch_adagents` transparently
benefits from the fallback while keeping its dict return shape; callers
who need provenance use the new `validate_adagents_domain`.

Refs: adcontextprotocol/adcp#4173, adcontextprotocol/adcp#4175

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… fallback

Reviewer follow-ups on #705 / #704:

- SSRF: a publisher-controlled MANAGERDOMAIN=169.254.169.254 (or any
  loopback / private / link-local literal) would have passed
  `_validate_publisher_domain` and forced the SDK into a cross-origin
  HTTPS GET against internal services. The fallback now routes every
  manager domain through `_ensure_safe_manager_domain`, which composes
  the existing `_validate_redirect_url` SSRF gate with the publisher
  domain validator.
- ads.txt size: the body was passed to `_parse_managerdomains` with no
  cap, so a hostile publisher could force the SDK to buffer arbitrary
  data. Bodies over MAX_ADS_TXT_BYTES (1 MiB) are now discarded.
- Redirect-target 404: a 404 from a followed authoritative_location
  target previously bubbled up as AdagentsNotFoundError and triggered
  the ads.txt MANAGERDOMAIN fallback on the original publisher, which
  conflates a broken pointer with a missing manifest. It now surfaces
  as AdagentsValidationError so the fallback only fires on the
  publisher's own direct 404.

Also documents the publisher-scope caveat on `validate_adagents_domain`
— callers wiring `ads_txt_managerdomain` results into authorization
decisions must verify the manager's adagents.json explicitly scopes
the source publisher (per the TS reference's `hasExplicitPublisherScope`
gate). Adding that gate as a default is tracked as a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley force-pushed the claude/issue-704-managerdomain-fallback branch from d67c7b3 to 9e0ab34 Compare May 12, 2026 18:30
@bokelley bokelley merged commit fddea1a into main May 12, 2026
16 checks passed
@bokelley bokelley deleted the claude/issue-704-managerdomain-fallback branch May 12, 2026 18:36
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.

support manager domain fallback

1 participant