Relationships
#461 swamp CLI: send user/device identity on extension-registry HTTP calls
Opened by john · 5/27/2026· Shipped 5/27/2026
Problem
CLI-initiated HTTP traffic to swamp.club (extension pulls, metadata
lookups, checksum verifies) is anonymous at the HTTP layer and
therefore unattributable in server-side telemetry, even when the CLI is
running on a logged-in user's machine.
Concrete: when I ran swamp extension pull @webframp/cloudflare just
now from a machine where I'm logged in as john, the resulting three
HTTP requests landed on the server like this:
target=/api/v1/extensions/@webframp/cloudflare auth=anonymous user= distinct_id=anon_nquhxl
target=/api/v1/extensions/@webframp/cloudflare@2026.05.27.1/download auth=anonymous user= distinct_id=anon_nquhxl
target=/api/v1/extensions/@webframp/cloudflare@2026.05.27.1/checksum auth=anonymous user= distinct_id=anon_nquhxlanon_nquhxl is a per-request hash the server computes from request
headers (IP + UA). It's not stable across invocations from the same
device — every CLI call produces a different anon_* ID. So per-user
and per-device attribution are both impossible.
Traces in Axiom: download, checksum, metadata.
Why it matters
With this gap closed we'd be able to answer (from telemetry alone):
- Which user pulled
@foo/barand how often? - Which version of an extension does a given operative tend to install?
- Are publishes by user X correlated with pulls by users Y, Z?
- Per-user
consumer.stats.publishlatency — which heavy users are driving the p99? (the consumer-side question we were ultimately trying to answer with the recent observability work)
Today none of these are answerable for CLI traffic. Web-app traffic
already attributes correctly because BetterAuth sessions ride the
Cookie header.
Current state on the server
The swamp-club Fresh middleware (routes/_middleware.ts) already
accepts three forms of auth on /api/*:
Authorization: Bearer swamp_<personal-key>→ resolved via BetterAuth'sverifyApiKey, stampsuser.id+user.usernameAuthorization: Bearer swamp_org_<collective-key>→ stampscollective.id+collective.slugx-api-key: <key>→ same as Bearer
No retro server-side changes are needed for the personal-key path — it's been live the whole time. The CLI just isn't using it.
Proposed change
The CLI should send identity on extension-registry HTTP calls. Two reasonable shapes (could land in either order, or both):
Shape A — Authoritative identity when logged in
When the user has a swamp auth login session, attach
Authorization: Bearer swamp_<personal-key> to all
swamp.club/api/* requests (pulls, checksums, metadata, search, …).
Server already stamps user.id / user.username on the span and
the existing OTel baggage propagation (PRs #601 + #602) will carry
that identity through to repo / S3 / downstream-service spans
automatically — so the per-user "what did john pull?" query starts
working immediately.
Shape B — Stable device identity when not logged in
For anonymous users (or for CLI calls that intentionally don't carry
the user token), send a stable per-device X-Distinct-Id header
generated and persisted by the CLI (same value already in the body
of cli_invocation telemetry events). The server-side middleware
prefers this over the per-request anon_* hash. That makes
attribution work device-level even pre-login, and matches the
existing distinct_id-in-event-body convention.
Shape B is the smaller change and likely lands first; Shape A is the fuller fix and matches what every other authenticated HTTP client in the ecosystem does.
Surface to look at on the CLI side
Wherever the CLI builds the fetch() call for extension pulls /
checksums (probably a shared client.ts or similar wrapper around
swamp.club/api/v1/extensions/...). Whatever already constructs the
/ingest telemetry POST body is the place that knows the
distinct_id for Shape B, and whatever already implements
swamp auth login knows the bearer token for Shape A.
Out of scope
- Auth on the
/ingestendpoint specifically — telemetry events already carrydistinct_idin their body, and the server-side identity_map resolves username from that asynchronously. This issue is only about the extension-fetch HTTP traffic. - Forcing login. Shape B explicitly preserves anonymous-but-attributed
flows for users who haven't run
swamp auth login.
Background
This came out of a multi-PR observability sweep on swamp-club (
#597,
#599,
#601,
#602) that got
identity (user.username, user.id, distinct_id) propagating
automatically through every server span via OTel baggage + the
AsyncLocalStorageContextManager fix. The server-side story is now
complete — this is the remaining gap that lives on the CLI side.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.