Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,29 @@ Gates at Step 4 and Step 7 - stop and report if they fail.

- Don't use line numbers (change every build)
- Don't use obfuscated names like `XQ`, `o9`, `yk` in documentation
- Don't infer endpoints from runtime logs
- Don't infer endpoints from runtime logs (using a capture to *check* header
sets already read from the binary is fine, and is what SECTION 43 does)
- Don't guess a method you couldn't read -- use `# PATH-ONLY` instead
- Don't document without `rg` verification

## Validation

Releases ship as a compiled binary, not `cli.js`. Render it first:

```bash
# If this returns nothing, endpoint doesn't exist
rg '/api/oauth/profile' cli.js
scripts/binary-literals.sh <binary> /tmp/literals.txt

# If this returns nothing, the endpoint doesn't exist
rg '/api/oauth/profile' /tmp/literals.txt

# Method + auth mode + timeout + beta flag, per call site
scripts/extract-routes.py <binary> --all extractions/vX.Y.Z/raw/routes.tsv

# Both gates, both specs
scripts/validate-spec.sh --routes extractions/vX.Y.Z/raw/routes.tsv \
/tmp/literals.txt specs/claude-code-api-complete.http
scripts/validate-spec.sh --subset --routes extractions/vX.Y.Z/raw/routes.tsv \
/tmp/literals.txt specs/claude-oauth-api.http
```

## File Types
Expand Down
83 changes: 69 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
> `@anthropic-ai/claude-code` release talks to.

[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
[![version](https://img.shields.io/badge/documented-v2.1.197-success)](extractions/v2.1.197/SUMMARY.md)
[![deps](https://img.shields.io/badge/deps-strings%20%2B%20rg-blue)](scripts/)
[![version](https://img.shields.io/badge/documented-v2.1.234-success)](extractions/v2.1.234/SUMMARY.md)
[![deps](https://img.shields.io/badge/deps-strings%20%2B%20rg%20%2B%20python3-blue)](scripts/)

This repo documents which API paths, beta flags, headers, OAuth scopes, and
model identifiers a given Claude Code release references, by reading the
Expand All @@ -19,26 +19,76 @@ credentials. The output is plain text you can diff across versions.

## Currently Documented

**v2.1.197** — see [extractions/v2.1.197/SUMMARY.md](extractions/v2.1.197/SUMMARY.md).
69 API paths, 45 beta flags. 12 paths and 3 beta flags added since v2.1.170.
**v2.1.234** — see [extractions/v2.1.234/SUMMARY.md](extractions/v2.1.234/SUMMARY.md).
101 API path literals (+33 vs v2.1.197), 53 beta flags (+9), and **242
call-site routes** with method, beta flag, auth mode and timeout.
`scripts/validate-spec.sh` reports 0 undocumented and 0 phantom endpoints for
both specs.

New families in this release: self-hosted runner pools, MCP tunnels, dreams,
deployments, Design consent/grants, ultrareview quota, and a much larger frame
(Artifact) surface.

The binary also embeds a complete, verbatim self-hosted gateway protocol
specification (`CLAUDE_CODE_USE_GATEWAY`) — not inferred, the literal ~9.6KB
Markdown doc the CLI ships internally. Recovered whole to
[extractions/v2.1.197/GATEWAY-PROTOCOL.md](extractions/v2.1.197/GATEWAY-PROTOCOL.md),
specification (`CLAUDE_CODE_USE_GATEWAY`) — not inferred, the literal Markdown
doc the CLI ships internally. It grew from 9,598 bytes in v2.1.197 to 13,246 in
v2.1.234, adding the rate-limit / overage 429 header contract, TLS leaf
certificate pinning, client guarantees, and a section on proxying to Bedrock,
Vertex and Foundry. Recovered whole to
[extractions/v2.1.234/GATEWAY-PROTOCOL.md](extractions/v2.1.234/GATEWAY-PROTOCOL.md),
with a runnable request set at
[specs/claude-code-gateway.http](specs/claude-code-gateway.http).

### Correction: methods and headers ARE recoverable from the binary

Since v2.1.117 the release ships as a Bun-compiled binary rather than a
readable `cli.js`. String literals still live in the binary's constant pool,
so `strings` + `rg` remains the extraction method for paths, beta flags, model
IDs, and env-var names.
readable `cli.js`, and this README used to say:

> Header sets, request bodies, timeouts and retry behaviour are **not**
> recoverable from a binary.

That was wrong. The binary embeds the whole minified JavaScript bundle as
printable text. It reads as one enormous line, so `rg -A/-B` context returns
nothing useful and it looks unrecoverable — but cut a fixed *byte* window and
the call site is right there:

```js
fs.get("/v1/ultrareview/quota", {auth: "teleport-org", timeout: 3000})
```

From v2.1.234 on, methods, header sets, auth modes, timeouts and beta flags are
read from the release being documented rather than carried forward from
v2.1.76. `scripts/extract-routes.py` does it mechanically for every call site;
`scripts/extract-calls.py` cuts the window when you want to read a body by
hand. The same method works retroactively on older binaries.

**Auth modes.** The most useful thing this recovered: axios call sites name
their auth mode, and the mode decides the entire header set.

| `auth:` | Headers |
|---------|---------|
| `teleport-org` | `Authorization`, `Content-Type`, `anthropic-version`, `anthropic-client-platform`, `x-organization-uuid` — and substitutes the literal `:orgUUID` in the path |
| `session-jwt` | `Authorization: Bearer <session access token>` only |
| `claude-ai-oauth` | `Authorization`, `anthropic-beta: oauth-2025-04-20` |
| `none` | no auth headers |
| `async` / unset | resolved OAuth headers, or `x-api-key` under API-key auth |

That also explains why paths in the constant pool contain `:orgUUID` verbatim —
the auth layer substitutes it, not the call site.

The OAuth surface is documented to full depth in
[specs/claude-oauth-api.http](specs/claude-oauth-api.http): per-call header
builders, timeouts, status handling, scope gating, the refresh-lock protocol,
and recorded negatives (there is no revocation endpoint).
`specs/claude-code-api-complete.http` is the breadth index.

## What It Extracts

| Output | File | How |
|--------|------|-----|
| API paths | `extractions/v<ver>/raw/paths.txt` | quoted `"/api/..."` / `"/v1/..."` literals |
| Routes (method + auth) | `extractions/v<ver>/raw/routes.tsv` | `_client.<method>(...)` and axios call sites |
| Generated spec section | `specs/claude-code-api-complete.http` | `scripts/gen-spec-section.py`, idempotent, from `routes.tsv` |
| Beta flags | `extractions/v<ver>/raw/beta_flags.txt` | tokens ending in a dated `YYYY-MM-DD` suffix |
| Call contexts | `extractions/v<ver>/calls/*.txt` | bounded windows around each endpoint literal |
| Headers / scopes / URLs | `extractions/v<ver>/raw/*.txt` | literal header names, `user:`/`org:` scopes, hardcoded URLs |
Expand All @@ -52,10 +102,15 @@ What a literal can and cannot prove:
published version. A documented path/flag/scope is backed by a verifiable
`rg` pattern against the release. This is the bar for everything in
`raw/` and `specs/`.
- **Medium confidence (context-inferred).** Method, headers, and request body
are read from the bounded text window around the literal (`calls/*.txt`).
Minifier variable names in those windows are noise, not facts — they change
every build and are never treated as documentation.
- **High confidence (call site read).** Method, auth mode, timeout and beta
flag come from the call site itself, in the release being documented
(`raw/routes.tsv`). Minifier variable names in those windows are noise, not
facts — they change every build and are never treated as documentation, which
is why the extractors detect identifiers rather than hardcoding them.
- **Declared unknown.** Where a path literal exists but its call site builds
the path through a helper the extractor does not follow, the spec carries a
`# PATH-ONLY` line: the path is verified, the method is not, and the spec
says so rather than guessing. 47 such paths in v2.1.234.
- **Not claimed.** Anything seen only at runtime, anything inferred from logs,
and anything that cannot be reproduced with a literal pattern. If it is not
in a release string, it is not documented.
Expand Down
117 changes: 117 additions & 0 deletions WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@
Sequential runbook for extracting HTTP endpoints from Claude Code CLI.
Agent executes top-to-bottom. Gates require pass before proceeding.

## Reading a compiled release (v2.1.117+)

Releases ship as a Bun-compiled binary, not a readable `cli.js`. The whole
minified JavaScript bundle is still in there as printable text -- it just reads
as one enormous line, so `rg -A/-B` context returns nothing useful and it *looks*
unrecoverable. It is not. Two moves make the binary as readable as the old
bundle:

```bash
# 1. one literal per line, for the rg-based path patterns and the validator
scripts/binary-literals.sh <binary> /tmp/literals.txt

# 2. method + path + beta flag + auth mode + timeout, per call site
scripts/extract-routes.py <binary> --all extractions/vX.Y.Z/raw/routes.tsv

# 3. a printable byte window around each endpoint, for reading bodies/headers
scripts/extract-calls.py <binary> extractions/vX.Y.Z/calls/
```

Header sets, request bodies and timeouts ARE recoverable from a binary. Earlier
revisions of this repo said otherwise and carried them forward from v2.1.76;
that was wrong. Read them from the release you are documenting.

## HTTP Precision Requirements

For each endpoint, document ALL of:
Expand Down Expand Up @@ -103,6 +126,19 @@ rg 'grove_notice_viewed' cli.js -B 10 -A 20 > ../$OUT/calls/api-oauth-grove-noti
rg 'create_api_key' cli.js -B 10 -A 20 > ../$OUT/calls/api-oauth-create-api-key.txt
rg 'claude_cli/roles' cli.js -B 10 -A 20 > ../$OUT/calls/api-oauth-roles.txt
rg 'client_data' cli.js -B 10 -A 20 > ../$OUT/calls/api-oauth-client-data.txt
rg 'api/claude_cli_profile' cli.js -B 10 -A 20 > ../$OUT/calls/api-cli-profile.txt
rg 'admin_requests' cli.js -B 10 -A 20 > ../$OUT/calls/api-oauth-admin-requests.txt
rg 'api/oauth/file_upload' cli.js -B 10 -A 30 > ../$OUT/calls/api-oauth-file-upload.txt
rg 'api/oauth/files' cli.js -B 10 -A 30 > ../$OUT/calls/api-oauth-file-download.txt
rg '/v1/oauth/hello' cli.js -B 10 -A 20 > ../$OUT/calls/api-oauth-hello.txt

# OAuth plumbing that determines headers for ALL of the above.
# These are the highest-value windows: three distinct header builders exist
# and mixing them up is the most common spec error.
rg 'BASE_API_URL: "' cli.js -B 20 -A 30 > ../$OUT/calls/oauth-config.txt
rg 'CLAUDE_CODE_CUSTOM_OAUTH_URL' cli.js -B 5 -A 25 > ../$OUT/calls/oauth-custom-url-allowlist.txt
rg '"anthropic-beta": ' cli.js -B 12 -A 4 > ../$OUT/calls/header-builders.txt
rg 'claude-cli/\$\{|claude-code/\$\{' cli.js -B 12 -A 2 > ../$OUT/calls/user-agent-builders.txt
rg 'api/claude_code_grove' cli.js -B 10 -A 20 > ../$OUT/calls/api-grove-settings.txt
rg 'first_token_date' cli.js -B 10 -A 20 > ../$OUT/calls/api-first-token-date.txt
rg 'sonnet_1m_access' cli.js -B 10 -A 20 > ../$OUT/calls/api-sonnet-1m-access.txt
Expand Down Expand Up @@ -223,6 +259,25 @@ Or use script:

## Step 6: Update .http Files

The bulk of a version bump is mechanical: one request block per new route, with
the header set implied by its auth mode. That part is generated, so it is
reproducible and nobody has to review 1,700 lines by hand:

```bash
scripts/gen-spec-section.py <version> /tmp/literals.txt
```

It rewrites the "NEW IN v<version>" section in place and is idempotent -- run it
twice, get the same bytes. Generated: the request blocks. Authored: the family
grouping and prose, which live in the `FAMILIES` table inside the script. Edit
prose there, not in the .http file, or the next run drops it.

Curated endpoints -- the ones that earn a request body, a response shape, or a
paragraph of behaviour -- are written by hand in the sections above it, as
before.

### Manual edits

For each **added** endpoint in `added_endpoints.txt`:

1. Verify with `rg` pattern (Step 4)
Expand Down Expand Up @@ -259,11 +314,34 @@ Update version in file headers.
./scripts/validate-spec.sh --subset package/cli.js specs/claude-oauth-api.http
```

Pass `--routes` so the call-site table counts as code, alongside the literal
scan. Without it, any path assembled from a template
(`/v1/environments/${id}/work/${w}/ack` -- never a whole literal) reads as a
phantom:

```bash
scripts/binary-literals.sh <binary> /tmp/literals.txt
scripts/validate-spec.sh --routes extractions/vX.Y.Z/raw/routes.tsv \
/tmp/literals.txt specs/claude-code-api-complete.http
scripts/validate-spec.sh --subset --routes extractions/vX.Y.Z/raw/routes.tsv \
/tmp/literals.txt specs/claude-oauth-api.http
```

**GATE**: Script must exit 0.
If fails, STOP and report:
- Undocumented endpoints (in code, not in spec)
- Phantom endpoints (in spec, not in code)

When a path literal exists but its call site builds the path through a helper
the route extractor does not follow, do NOT guess a method. Declare it:

```
# PATH-ONLY {{baseUrl}}/api/frame/contract/latest
```

The validator counts that as documented. It claims the path exists and nothing
else -- no method, no headers, no body.

## Step 8: Prepare Commit (HUMAN REVIEW)

Generate commit message, DO NOT execute:
Expand Down Expand Up @@ -314,6 +392,45 @@ rm -f current_spec_paths.txt new_extracted_paths.txt added_endpoints.txt removed
| Grant types | `rg 'grant_type.*"[^"]*"' cli.js -o` |
| Scopes | `rg '"user:[^"]*"\|"org:[^"]*"' cli.js -o` |

## Known Extraction Blind Spots

The path-literal patterns above key on `"/api/...` or `BASE_API_URL}/api/...`.
Endpoints whose host comes from a *helper call* are invisible to both:

```js
`${ubY()}/api/oauth/file_upload` // host helper, not BASE_API_URL
`${qPz()}/api/oauth/files/${uuid}/content`
```

`scripts/validate-spec.sh` carries explicit rules for these two. When a new
endpoint is built the same way, add a rule there or it will be reported as a
phantom forever. Sweep for the shape with:

```bash
rg -o '\}/api/[a-z_/]+' cli.js | sort -u
```

Header sets used to be guesswork. They are not any more: axios call sites name
their auth mode, and the mode decides the entire header set.

| `auth:` | Emits |
|---------|-------|
| `teleport-org` | `Authorization` + `Content-Type` + `anthropic-version` + `anthropic-client-platform` + `x-organization-uuid`; also substitutes `:orgUUID` in the path |
| `session-jwt` | `Authorization: Bearer <session access token>`, nothing else |
| `claude-ai-oauth` | `Authorization` + `anthropic-beta: oauth-2025-04-20` |
| `none` | no auth headers |
| `async` / unset | resolved OAuth headers, or `x-api-key` under API-key auth |

`scripts/extract-routes.py` records the mode per route. Two pre-flight refusals
apply to every axios call: essential-traffic-only mode, and a non-first-party
provider (`data-residency`).

Stainless SDK resources take auth from the client, not per call, and pin their
own `anthropic-beta` flag per resource.

Still: never copy a header block from a neighbouring endpoint. Read the call
site, or read the `auth` column.

## HTTP Precision Patterns

```bash
Expand Down
56 changes: 56 additions & 0 deletions extractions/v2.1.234/COMPARE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## paths: 101 total, 33 added, 1 removed (v2.1.197 -> v2.1.234)

### Added
- "/api/desktop/
- "/api/directory/
- "/api/event_logging/
- "/api/frame/
- "/api/frame/contract/latest
- "/api/frame/db/agent
- "/api/frame/deploy/prepare
- "/api/frame/frames?limit=200
- "/api/frame/upload
- "/api/oauth/organizations/:orgUUID/mcp/connectors/list
- "/api/oauth/organizations/:orgUUID/mcp/connectors/search
- "/api/oauth/organizations/:orgUUID/mcp/connectors/suggest
- "/api/oauth/organizations/:orgUUID/plugin_ratings
- "/api/oauth/organizations/:orgUUID/plugin_ratings/appearances
- "/api/oauth/organizations/:orgUUID/plugin_ratings/appearances/outcome
- "/api/oauth/organizations/:orgUUID/plugins/search
- "/api/oauth/organizations/:orgUUID/skills/search
- "/api/organizations/:orgUUID/cowork/remote_devices
- "/v1/code/agent-proxy/artifact
- "/v1/code/agent-proxy/frame
- "/v1/code/local/memory/credential
- "/v1/code/local/memory/mounts
- "/v1/code/memory/
- "/v1/code/scm-connectors/{provider}/{id}/tunnel
- "/v1/code/webhook-triggers
- "/v1/deployment_runs?beta=true
- "/v1/deployments?beta=true
- "/v1/design/consent
- "/v1/design/grants
- "/v1/design/mcp
- "/v1/dreams?beta=true
- "/v1/tunnels?beta=true
- "/v1/ultrareview/quota

### Removed
- "/api/claude_code/discovery/team_usage

## beta_flags: 53 total, 9 added, 1 removed (v2.1.197 -> v2.1.234)

### Added
- agent-memory-2026-07-22
- auto-mode-classifier-2026-07-16
- dreaming-2026-04-21
- mcp-tunnels-2026-06-22
- mid-conversation-tool-changes-2026-07-01
- per-turn-control-2026-07-01
- pre-2026-07-28
- prompt-caching-evict-2026-05-12
- server-side-fallback-2026-07-01

### Removed
- summarize-connector-text-2026-03-13

Loading