Skip to content

refactor(env): rename E2A_BASE_URL to E2A_API_URL; split it from the CLI's E2A_URL#522

Merged
jiashuoz merged 2 commits into
mainfrom
refactor/env-api-url-rename
Jul 17, 2026
Merged

refactor(env): rename E2A_BASE_URL to E2A_API_URL; split it from the CLI's E2A_URL#522
jiashuoz merged 2 commits into
mainfrom
refactor/env-api-url-rename

Conversation

@jiashuoz

Copy link
Copy Markdown
Member

Why

The client-side base-URL env vars named one concept badly and two concepts identically, so following our own docs silently misrouted traffic.

E2A_URL and the SDKs' E2A_BASE_URL are not the same thing:

  • The CLI's api_url is the deployment root. login opens a browser against it (cli/src/commands/login.ts:310) and points users at /get-started — a Next.js route (web/src/app/(app)/get-started) the Go backend does not serve. https://e2a.dev works because the web front also proxies /v1 (web/Caddyfile:35).
  • The SDKs and the MCP server only ever call /v1, and want the API host alone.

Two names for "the base URL", with two different defaults, hid that distinction. The concrete failures:

Docs taught a var the SDKs don't read README.md and docs/deployment.md teach E2A_URL; the SDKs read only E2A_BASE_URL. A self-hoster who followed the docs got their SDK silently pointed at production api.e2a.dev.
The CLI alias broke e2a login The CLI accepted E2A_BASE_URL as an alias for its deployment root, so exporting E2A_BASE_URL=https://api.e2a.dev to configure an SDK silently repointed the CLI at the API host.
tether forced the CLI onto the API host tether.env.example taught E2A_BASE_URL ("self-host: your API base") and lib.sh:51 defaulted it to api.e2a.dev — overriding the CLI's own e2a.dev default through that alias.

What changed

  • SDKs (TS + Python) read E2A_API_URL, matching the name the server already uses for the same concept (internal/config/config.go:344 — its externally visible API base). E2A_BASE_URL still works, with a deprecation warning (console.warn / DeprecationWarning).
  • MCP moves to E2A_API_URL (E2A_URL + E2A_BASE_URL deprecated) and now defaults to the API host — it is a pure API client.
  • CLI keeps E2A_URL alone and no longer reads E2A_BASE_URL. Dropping the alias silently would strand tether self-hosters on production, so it warns — gated to the actually-dangerous case (legacy var set, E2A_URL unset, root still at default), so a legitimate SDK user who also configured their CLI gets no noise.
  • tether migrates to E2A_URL, carries a legacy ~/.e2a-tether.env over with a warning (rather than reverting to prod), unsets the stale name so the CLI doesn't double-warn, and tracks the CLI's default instead of forcing api.e2a.dev.
  • Docs: dropped the E2A_AGENT_EMAIL fallback claim from both SDK READMEs and the root README — it was never implemented in either SDK (they raise missing_email), though it is real in the CLI and tether. Added the CLI's env-var table (it documented none despite reading five) and split E2A_URL vs E2A_API_URL in docs/deployment.md.

Also removed the MCP one-shot deprecation guard: main() calls loadConfig exactly once, so it deduped nothing and only leaked state across tests.

Compatibility

Additive. Every previously-working configuration still works; the old names warn rather than break. The one behavior change is the CLI ignoring E2A_BASE_URL — loud, not silent, and tether (its main consumer) migrates itself.

Verification

Suites: TS SDK typecheck+unit+types, MCP vitest+types, Python 323 passed / 18 skipped, CLI 120 passed, tether bash -n clean.

Driven for real, not just unit-tested:

  • Both SDKs against actual dialed origins across all four resolution cases (default → E2A_API_URL → deprecated E2A_BASE_URL + warning → both set, canonical wins).
  • The built CLI binary: warning fires for the self-hoster case, silent when E2A_URL is set.
  • t_load_config through the legacy-migration path: carries over, warns, unsets, and defaults correctly.

⚠️ pytest inside a git worktree silently tests the main checkout via the editable install — run it with PYTHONPATH=$(pwd)/src or you are testing the wrong tree.

Follow-ups (not in this PR)

  • mcp/src/config.ts is dead code (nothing in src/ imports it but a type; no bin field since npm stdio publishing was retired in GA /v1 readiness: settle API contract, regenerate SDKs, slim CLI, re-curate MCP (hosted-only) #247) and now also contradicts the new canonical name. Deleting it is a separate call.
  • The split-host topology internal/config/config.go:377 describes ("web on one host, API/MCP on another") still can't be expressed by the CLI, which holds only one URL. That's the deeper fix.

🤖 Generated with Claude Code

jiashuoz and others added 2 commits July 17, 2026 00:28
…CLI's E2A_URL

The client-side base-URL env vars named one concept badly and two concepts
identically, so following the docs silently misrouted traffic.

E2A_URL and the SDKs' E2A_BASE_URL are NOT the same thing. The CLI's api_url is
the deployment root: `login` opens a browser against it (login.ts:310) and
points users at /get-started, a Next.js route the Go backend does not serve.
https://e2a.dev works because the web front also proxies /v1. The SDKs and the
MCP server only ever call /v1 and want the API host alone. Both names existing
for "the base URL", with different defaults, hid that distinction.

The concrete failures:

  - README.md and docs/deployment.md taught only E2A_URL, which the SDKs did
    not read. A self-hoster who followed the docs got their SDK silently
    pointed at production api.e2a.dev.
  - The CLI accepted E2A_BASE_URL as an alias for its deployment root, so
    exporting E2A_BASE_URL=https://api.e2a.dev to configure an SDK silently
    repointed the CLI at the API host and broke `e2a login`.
  - tether taught E2A_BASE_URL ("self-host: your API base") and forced the CLI
    onto api.e2a.dev through that alias.

So:

  - SDKs (TS + Python) read E2A_API_URL, matching the name the server already
    uses for the same concept (config.go:344 — its externally visible API base).
    E2A_BASE_URL still works, with a deprecation warning.
  - MCP moves to E2A_API_URL (E2A_URL/E2A_BASE_URL deprecated) and defaults to
    the API host; it is a pure API client.
  - The CLI keeps E2A_URL alone and no longer reads E2A_BASE_URL. Dropping the
    alias silently would strand tether self-hosters on production, so it warns
    when E2A_BASE_URL is set and the root is still the default.
  - tether migrates to E2A_URL, carrying a legacy ~/.e2a-tether.env over with a
    warning, and defaults to the CLI's own default instead of forcing api.e2a.dev.

Also drops the E2A_AGENT_EMAIL fallback claim from both SDK READMEs and the
root README: it was never implemented in either SDK (they raise missing_email),
though it is real in the CLI and tether. Documents the CLI's env vars, which
were undocumented.

The MCP one-shot deprecation guard is gone: main() calls loadConfig exactly
once, so it deduped nothing and only leaked state across tests.

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

Review follow-ups on the E2A_API_URL split.

[P1] The deployment guide's audience table and setup example still grouped
"CLI / SDK user" together and instructed only E2A_URL — the var the changed
SDKs no longer read. A self-hoster following the primary setup path pointed
their SDK at production api.e2a.dev, the exact bug this branch fixes. Split the
audience row and the walkthrough into a CLI section (E2A_URL + e2a login) and an
SDK/MCP section whose example exports E2A_API_URL, in both docs/deployment.md
and the README table. Each section now states which var the other audience's
tooling ignores.

[P2] The CLI's "you set a var I ignore" warning only fired when the resolved URL
equalled the default. E2A_BASE_URL used to override ~/.e2a/config.json, so a
user with stored host A and a legacy env override for host B now silently
connects to A — and the default-only gate stayed quiet because A != default.
Warn whenever E2A_BASE_URL is set and E2A_URL is absent, and name the host
actually in use so the stored-vs-intended mismatch is visible. Regression test
added with a stored non-default api_url.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jiashuoz

Copy link
Copy Markdown
Member Author

Both addressed in 6029c08.

[P1] Docs no longer misroute SDK users. The CLI / SDK user row and the single export E2A_URL walkthrough were the misdirection — the E2A_API_URL row I'd added lower down didn't undo the headline instruction. Split into two audiences with two setup examples, in both the docs/deployment.md table and the README table:

  • CLI userE2A_URL + e2a login
  • SDK / MCP userexport E2A_API_URL=… + E2A_API_KEY

Each section now names the var the other audience's tooling ignores, e.g. "The CLI does not read E2A_API_URL … so a self-hoster who only exports E2A_API_URL leaves the CLI pointed at production."

[P2] Legacy override is no longer silently ignored. You're right that gating on resolved === DEFAULT_URL missed the stored-host case: E2A_BASE_URL used to override ~/.e2a/config.json, so stored host A + legacy env host B silently kept A. The warning now fires whenever E2A_BASE_URL is set and E2A_URL is absent, and names the host actually in use so the mismatch is visible. Driven against the built binary with a seeded non-default api_url:

e2a: E2A_BASE_URL is set but the CLI does not read it — that name configures the SDKs.
     The CLI uses E2A_URL for the deployment root and is talking to https://stored.selfhost.dev.
     Set E2A_URL to point it at a self-hosted deployment.

Regression test added with a stored non-default api_url asserting both the warning and that it names the stored host. CLI suite 121 passing.

@jiashuoz
jiashuoz merged commit bce5118 into main Jul 17, 2026
15 checks passed
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