Relationships
#890 swamp serve --auth-mode token: redeem reads token-main as null → every valid token rejected 'Authentication failed' (real error hidden at debug)
Opened by magistr · 6/29/2026· Shipped 6/29/2026
Summary
swamp serve --auth-mode token rejects every freshly-minted, active server token with WebSocket auth rejected: "Authentication failed". The real (debug-only) error is Server token '<name>' does not exist — mint it first: serve's redeem path reads the token's token-main data artifact and gets null, even though the token was just minted and is visible via swamp access token list / swamp model get. --auth-mode none works perfectly, so the entire client→serve→model→datastore→vault chain is fine — only token validation is broken.
Environment
- swamp
20260629.145610.0-sha.ac2f277d(stable), client and server on the same version. - Reproduces on both
filesystemand@keeb/mongodb-datastoredatastores. - macOS client + Linux (debian-slim) serve container; also reproduces serve+client both local on macOS.
Minimal repro (no container, no proxy, no TLS)
# terminal 1
swamp serve --host 127.0.0.1 --port 9099 --auth-mode token --admins user:magistr --log --log-level debug
# terminal 2
TOK=$(swamp access token mint t --principal user:magistr --duration 1d --vault <any-vault> --json \
| python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])')
swamp auth server-login --server ws://127.0.0.1:9099 --token "$TOK"
swamp model method run <model> <method> --server ws://127.0.0.1:9099Client prints {"error":"Could not connect to ws://127.0.0.1:9099/"}. Serve (debug) logs:
DBG serve·token-auth Token authentication failed for "t": "Server token 't' does not exist — mint it first"
WRN serve WebSocket auth rejected from "127.0.0.1": "Authentication failed"Root cause
serve validates by running the server-token model's redeem method as an ad-hoc run keyed by type + definition name:
src/serve/token_auth.ts:79
modelMethodRun(libCtx, deps, {
modelIdOrName: split.name,
methodName: "redeem",
typeArg: SERVER_TOKEN_MODEL_TYPE.normalized, // swamp/server-token
definitionName: split.name,
inputs: { presentedToken: presented },
})redeem → readToken() in src/domain/models/access/server_token_model.ts:65:
const raw = await context.readResource!("token-main"); // returns null
if (raw === null) throw new Error(`Server token '${name}' does not exist — mint it first`);context.readResource("token-main") returns null, so redeem throws "does not exist". mint (writeResource("token","token-main",…)) persists the artifact under the minted instance, but the redeem path's instance-scoped readResource resolves a different owner (the ad-hoc typeArg+definitionName run) and can't see it.
Proof the token exists but serve can't read its data
For a token preboot minted before serve even started:
| Check | Result |
|---|---|
swamp access token mint preboot … |
created |
swamp model get preboot |
instance exists (id 01f323a8…, type swamp/server-token) |
swamp access token list |
preboot active, full metadata |
swamp vault read-secret <vault> server-token-preboot |
correct 64-char secret (matches the minted secret byte-for-byte) |
swamp data list preboot |
"Model not found" |
serve redeem → readResource("token-main") |
null → "does not exist" |
swamp access token list uses a different read path than redeem's instance-scoped readResource, which is why the list sees the token but redeem does not. The swamp data list <name> → "Model not found" (while swamp model get <name> succeeds) corroborates that the instance's data artifact isn't resolvable by the instance.
Independent of
datastore backend (filesystem AND mongo), bind address (localhost / 127.0.0.1 / LAN IP with TLS), transport (ws and wss), mint-before-vs-after serve start, and credential delivery (--token flag and swamp auth server-login). All fail identically. --auth-mode none succeeds in every case.
Secondary issue (observability)
The real error is logged only at debug; serve returns a generic "Authentication failed" and the client surfaces only "Could not connect". This made a config-looking failure extremely hard to diagnose. Suggest logging the redeem failure reason at warn (or distinguishing "token not found" vs "secret mismatch" vs "revoked/expired") so operators can tell a genuine bad token from this internal lookup failure.
Related
Related to the reverse-proxy friction in #889 (WS host allowlist), but this is a separate, more fundamental blocker: even with the host check satisfied and TLS working, token auth never succeeds.
Shipped
Click a lifecycle step above to view its details.