Skip to main content
← Back to list
01Issue
BugClosedSwamp CLITeam
AssigneesNone

Relationships

#1390 Purely local operations require a swamp-club account: local-token serve is gated while deprecated --auth-mode none is not, third-party OIDC is gated, and 'Local_Encryption' is paywalled by capitalization

Opened by magistr · 7/24/2026

Summary

Several gated operations perform no swamp-club.com interaction whatsoever, yet refuse to run without a swamp-club account. The most consequential is swamp serve --auth-mode token, whose token validation is entirely local. Because the two authenticated modes are gated and --auth-mode none is not, an account-less user is pushed onto the deprecated, unauthenticated mode — the security gradient runs backwards.

The operations are genuinely local

  • src/serve/token_auth.ts — the imports are repository_factory, libswamp/mod, server_token_model, deps, logger. No HTTP client, no swamp-club call. Incoming tokens are redeemed against the local swamp/server-token model, with the secret in the local vault (server_token_model.ts:151-176).
  • src/libswamp/vaults/create.ts — writes a local YAML config. Secrets go to the user's own provider.
  • src/libswamp/datastores/setup.ts — writes local config and syncs to the user's own bucket with the user's own cloud credentials.

None of these contacts swamp-club at any point, so the gates are pure client-side pre-flights.

Deterministic repro

A — purely local serve is blocked

export SWAMP_HOME="$(mktemp -d)"; mkdir -p "$SWAMP_HOME/config"   # no credentials
swamp serve --host <loopback> --port 39118 --auth-mode token --admins user:alice --no-schedule

Observed:

Error: swamp serve is a team feature that requires a free swamp-club.com account.
...
Your token should include the serve:* scope.

Loopback bind, local token auth, no swamp-club feature in play. On an air-gapped host this cannot be satisfied at all.

B — the only mode that runs is the deprecated one

serve.ts:960-963 gates mode === "oauth" || mode === "token". --auth-mode none is ungated, and serve.ts:965-971 then warns that it "is deprecated and will be removed in a future release. Use --auth-mode token" — the mode just refused. assertOffLoopbackSecurity (serve.ts:181-198) additionally forbids none off-loopback, so an account-less user cannot bind to a LAN address in any mode.

C — a case variant of a local vault type is paywalled

swamp vault create Local_Encryption my-vault      # -> paywall
swamp vault create local_encryption my-vault      # -> created fine, fully offline

vault_create.ts:85 compares vaultType !== "local_encryption" case-sensitively, while the registry normalizes with toLowerCase() throughout (vault_type_registry.ts:90,97,127,...). So the same offline AES-GCM file vault is gated or not depending on capitalization.

D — third-party OIDC still requires a swamp-club account

--oauth-provider <url> accepts any HTTPS issuer (serve_auth_config.ts:119-135), but the gate at serve.ts:960 fires on mode === "oauth" unconditionally. swamp serve --auth-mode oauth --oauth-provider https://[HOST-1] --admins alice involves swamp-club nowhere and is still refused.

Impact

The practical effect of gating token and oauth while leaving none open is that the least secure configuration is the one available by default to anyone not signed in. A user who just wants an authenticated local server — the thing the deprecation warning explicitly tells them to use — has to create an account to get it, and if they do not, the tool steers them to the mode it is simultaneously warning them off.

Independently: local vault creation, local datastore setup, and local serve are the features people reach for before they have decided whether to join swamp-club at all. Requiring the account for operations that never contact the service makes the requirement look like a paywall on local functionality rather than an authorization check.

Expected

Some of this may be deliberate product policy rather than a defect, so treating it as one question: which of these gates are intended?

  • If serve --auth-mode token is meant to be gated, the deprecation warning on --auth-mode none should not recommend a mode the user cannot use, and the gate message should say so up front.
  • --auth-mode oauth with a non-swamp-club issuer has no swamp-club dependency and looks like it should be ungated.
  • The Local_Encryption capitalization gap is unambiguously a bug — compare case-insensitively, as the registry does.
  • Whatever the policy, it is currently documented nowhere: not in --auth-mode help, not in the vault create / datastore setup help or examples, not in the README, and not in the shipped skills that walk users through exactly these commands (share/guide.md:95-99, vault/references/providers.md, repo/reference.md:234,310). Users meet the requirement for the first time at runtime, mid-task.

Environment

  • Verified against source main @ 10943ef. Gate shipped in a519305 (#1934), 2026-07-23; installed stable 20260722.210030.0-sha.7ff1e2f0 predates it, so none of this reproduces on the current release binary yet.
  • macOS 14.
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED

Closed

7/24/2026, 10:45:16 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

magistr commented 7/24/2026, 9:57:32 PM

Correction from the author — please read before triaging. I got a load-bearing claim wrong.

My 'Expected' section asserted the account requirement is 'documented nowhere'. That is false, and I should have checked the published manual before writing it. I only checked the source tree for docs/ and manual/ directories, found none, and generalised from that. The manual at /manual documents both the requirement and its reasoning:

  • /manual/explanation/api-key-scoping: 'Some features — external datastores, non-local vaults, and swamp serve in token or oauth mode — require a swamp-club.com account even though the resources they manage live locally or in your own cloud infrastructure.'
  • Same page, the rationale: 'these features cross the boundary from single-machine use into shared team state... an S3 datastore, an AWS Secrets Manager vault, or a network-accessible serve instance affect resources that multiple team members rely on — and Swamp needs to know who is configuring them.'
  • /manual/how-to/authenticate-with-api-keys covers the collective-token path for the same three features.

So the gating is deliberate, documented policy with a stated rationale, not an oversight. My framing of sections A, B and D as defects was wrong, and the question I posed ('which of these gates are intended?') is already answered in the manual. Apologies for the noise.

What I still think stands, narrowed:

  1. Section C is a real bug and unaffected by the above. vault create Local_Encryption is gated while local_encryption is not, because vault_create.ts:85 compares case-sensitively against a registry that normalises with toLowerCase(). A local AES-GCM file vault should not be gated on capitalisation. If nothing else here is actionable, this is.

  2. Discoverability, restated correctly. The requirement is in the manual but not at the point of use: not in --auth-mode help, not in vault create / datastore setup help or examples. The user meets it as a runtime error. That is a much weaker claim than the one I filed and may not be worth acting on.

  3. The deprecation-warning contradiction. serve.ts:965-971 tells an account-less user to 'Use --auth-mode token' — the mode the gate just refused. Whatever the policy, that specific sentence points at a door the reader cannot open, and could name the account requirement instead.

Feel free to close as intended-behaviour if only point 1 is worth keeping; I would not object to it being split out and the rest dropped.

stack72 commented 7/24/2026, 10:45:16 PM

@magistr Thanks for the detailed reports — we appreciate the work you're putting in to make the tool better. Fixes are in #1956 and will be released shortly.

Sign in to post a ripple.