archal CLI hides a small detail that matters when you start sending
requests from anything else: twin endpoints live behind the Archal
control-plane proxy, which requires two auth headers instead of one.
This guide documents the pattern so anyone building a non-CLI integration
(Lambda, Cloudflare Worker, Python script, browser-based test, polyglot
CI job) can reach the same twin your CLI session is already using.
When you need this
- You ran
archal twin start githuband now want to hit the printed URL from something other than the CLI — a Lambda, a test harness, an SDK smoke test, a bash + curl pipeline, etc. - You’re self-hosting the Archal control plane and want to verify the proxy from outside the monorepo.
- You’re writing a fidelity harness that pokes twin REST endpoints
directly without loading the full
@archal/runtimepackage.
@archal/runtime directly, use that instead — it handles both headers
for you. This guide is specifically for environments where adding a
runtime dependency is not practical.
The two-header pattern
Every request to a twin endpoint must carry:-
Authorizationauthenticates your request to the Archal control plane. The token comes fromarchal login(stored in~/.archal/credentials.json) or a long-lived token you created from the dashboard. The control plane uses it to verify that thesessionIdin the URL belongs to you and that the session has not expired. -
x-archal-upstream-authorizationis what the twin actually sees asAuthorizationafter the control plane forwards your request. The proxy strips the originalAuthorizationheader, rewritesx-archal-upstream-authorizationintoAuthorization, and passes the whole thing to the twin worker. Twins that enforce bearer-token auth (like the github twin’srequireGitHubBearerToken) check this newAuthorizationheader for a non-empty token.
ghp_test_bootstrap_token as the placeholder value.
Example (curl)
Given a running twin session:x-archal-upstream-authorization and the twin will return a real
github-shaped 401:
Authorization and the control plane returns 401
before the request ever reaches the twin.
Example (Python urllib)
Example (AWS Lambda / Cloudflare Worker)
Header semantics, precisely
The proxy’s header rewrite lives ininfra/api/routes/session-events.ts.
The order of operations is:
- Validate the outer
Authorization: Bearer <archal-token>against the caller’s Archal session. Requests with no token, an expired token, or a token belonging to a different user are rejected with 401 or 403 at this stage — the twin never sees them. - Capture the value of
x-archal-upstream-authorization(if present). - Copy the request headers forward, stripping a hardcoded list of
sensitive headers including
x-archal-upstream-authorizationitself. - Set the forwarded
Authorizationheader to the captured upstream value (or leave it blank if none was supplied). - Forward the rewritten request to the twin worker.
- The twin ALWAYS sees
Authorizationas whatever you sent inx-archal-upstream-authorization. Your real Archal token never reaches the twin. - Sending an
Authorizationheader that’s NOT the Archal token — for example, trying to smuggle a real github PAT through the outer header directly — fails at step 1 because the control plane validates the outer header first. Always use the two-header pattern.
See also
- Twin sessions (
archal twin) — how to create, list, and stop twin sessions with the CLI. twins/github/src/auth.ts— reference bearer-token check including thex-archal-upstream-authorizationfallback. Every stateful twin follows the same pattern.infra/api/routes/session-events.ts— the proxy’s header rewrite code path.
