diff --git a/PLAYBOOK.md b/PLAYBOOK.md index 3674e1c..ebe3878 100644 --- a/PLAYBOOK.md +++ b/PLAYBOOK.md @@ -686,7 +686,7 @@ Claude Code makes, all of which are POSTs. The symptom — every call failing on freshly installed provider — reads as "the credential is wrong", and the credential is fine. -**Three things belong in the allowlist that are not in `hosts`:** +**Four things belong in the allowlist that are not in `hosts`:** - Hosts the agent must reach but must never be credentialed — `github.com` for git push/pull, which `010_github.py` deliberately does not match. @@ -696,6 +696,29 @@ credential is fine. - Multi-tenant suffixes that are fine to reach and unsafe to inject for — `*.workers.dev`. Reachable and credentialed are different lists, and this is the direction where conflating them is dangerous. +- **The client's own startup checks, which are rarely on the API host.** Claude + Code calls `GET platform.claude.com/v1/oauth/hello` before it will run and + treats failure as fatal, so an `anthropic` entry listing only + `api.anthropic.com` installs cleanly, injects correctly, and produces an + agent that refuses to start. The endpoint needs no credential — it answers + 200 unauthenticated — so it is allowlist-only by the first bullet's rule. + Find these the same way you find the methods: run the client once against a + real allowlist and read the `blocked` lines out of the trail. Do not reason + about them from the vendor's API documentation, which describes the API and + not the client. + + This is the category most likely to be missed, because the failure does not + look like an egress failure. The message the user sees names the *proxy*, and + reads as a credential or TLS problem: + + ``` + Unable to connect to Anthropic services + Failed to connect to platform.claude.com: Status 403 + ``` + + That 403 is `001_allowlist.py` refusing, and the matching + `{"event":"blocked","reason":"allowlist",...}` line in the trail is what says + so. Check there first when a freshly installed provider will not start. **Ship optional hosts commented out**, with the reason on the line. Telemetry, error reporting, CDN mirrors, anything the provider works without. The default diff --git a/bank/anthropic/allowlist b/bank/anthropic/allowlist index 6852c0a..401f3a7 100644 --- a/bank/anthropic/allowlist +++ b/bank/anthropic/allowlist @@ -9,6 +9,7 @@ # `api.anthropic.com` line blocks the provider completely while looking right. api.anthropic.com GET,POST +platform.claude.com GET # GET is here for model listing (GET /v1/models), which some clients call at # startup; POST carries /v1/messages and /v1/messages/count_tokens. Narrowing @@ -16,6 +17,26 @@ api.anthropic.com GET,POST # know your client never lists models. Widening past these two is not needed by # anything: the Admin API under /v1/organizations is refused by the addon # regardless of what this file says. +# +# platform.claude.com is the client's startup connectivity check (Claude Code +# 2.x calls GET /v1/oauth/hello) and failing it is FATAL — the agent installs +# cleanly, gets its credential injected correctly, and still refuses to start. +# It is the second line rather than an optional one for that reason. Leaving it +# out is easy to misdiagnose: what the user sees names a proxy, not an +# allowlist, so it reads as a credential or TLS problem rather than an egress +# one. +# +# Unable to connect to Anthropic services +# Failed to connect to platform.claude.com: Status 403 +# +# That 403 is the proxy's own refusal. The endpoint needs no credential — it +# answers 200 unauthenticated — which is why it belongs here and NOT in the +# entry's `hosts`, on the same reasoning as the OPTIONAL block below. +# +# Editing this file is not enough on its own: the proxy reads the allowlist +# once, at startup, and the file is a bind mount, so `docker compose up -d` +# sees no config change and the container keeps the ruleset it already has. +# Restart the proxy. # --------------------------------------------------------------------------- # OPTIONAL — nothing below is needed for the provider to work.