master
Drew Owens Publish 1.1.10 b94c7a3 1mo ago

Switch adom-molecule CLI auth from Authorization: Bearer to X-Api-Key

Background

The molecule CLI talks to Hydrogen's molecule API (/api/molecules/*). Hydrogen was just updated (Hydrogen repo) to authenticate and authorize those endpoints using the same convention as the editor/panels API:

  • Browser users → session_token cookie
  • AI / containers / CLI → X-Api-Key header (a Carbon c_-prefixed token)

Hydrogen resolves the caller's identity from Carbon GET /user, their org membership from GET /user/orgs, and enforces that the target owner/ username is the caller's own username, an org they belong to, or that the caller is an admin. S3 itself is unauthenticated; Carbon is the identity authority; Hydrogen enforces ownership.

The CLI currently sends Authorization: Bearer <token>. Hydrogen does not read that header for the molecule endpoints — it reads X-Api-Key. So the CLI must switch headers.

Note: the token value is unchanged. Carbon accepts the same c_… token; only the header name on the request to Hydrogen changes.

Scope of this issue (CLI repo only)

src/client.rs:

  1. In apply_auth() (or wherever the auth header is attached), send the token as the X-Api-Key header instead of Authorization: Bearer <token>.
    • Apply it to every authenticated call: get_json, post_json, delete, post_multipart.
    • head_ok (health) stays unauthenticated.
  2. Keep the existing 3-tier token resolution unchanged (~/.config/adom-cli/config.json api_token → ADOM_API_TOKEN env → /var/run/adom/api-key).

Example (ureq):

fn apply_auth(req: ureq::Request) -> ureq::Request {
    match resolve_token() {
        Some(token) => req.set("X-Api-Key", &token),
        None => req,
    }
}

That's the whole code change. No new dependencies.

Why no Hydrogen-side change is needed for the internal S3 hop

For info only: create/delete/generate-json internally call /api/curium/get-s3-api-key to fetch the shared S3 key. SvelteKit does not auto-forward custom headers like X-Api-Key to internal fetches, so Hydrogen now threads the token explicitly into CuriumClientTs.create(fetch, token). That is already handled in Hydrogen — the CLI just needs to send X-Api-Key.

Test plan

Against a CLI build pointed at prod Hydrogen (ADOM_HYDROGEN_URL, default https://hydrogen.adom.inc):

  1. Own user: adom-molecule create --owner <your-username> ... → succeeds.
  2. Member org: --owner <an-org-you-belong-to> → succeeds.
  3. Forbidden owner: --owner <an-owner-you-do-NOT-belong-to> → Hydrogen returns 403 (Forbidden - <you> cannot act on behalf of '<owner>').
  4. No / bad token: unset all token sources → 401.
  5. delete, optimize (queue), and create all exercise the same auth path — smoke-test each.

Quick header sanity check (token belongs to kcknox):

curl -sk -H "X-Api-Key: c_…" https://carbon.adom.inc/user        # 200 + identity
curl -sk -H "X-Api-Key: c_…" https://carbon.adom.inc/user/orgs   # 200 + org list

Out of scope (separate follow-ups)

  • Unify resolvers: Hydrogen now has two near-identical token resolvers — src/lib/server/editorAuth.ts (resolveCallerIdentity) and the new src/lib/server/apiAuth.ts (resolveCaller). Collapse to one shared helper.
  • /api/workspaces/repo/[owner]/[repo]/current (the non-editor workspace endpoints) authenticate by forwarding the browser cookie to Carbon with credentials: 'include'; they have no X-Api-Key path and won't work for containers. Decide whether containers ever need them and gate accordingly.
  • Consider adding X-Api-Key support to any other non-editor mutating routes surfaced during review.