Skip to content

feat(moshpit): let an API key drive the namespace - #169

Merged
ralyodio merged 1 commit into
mainfrom
feat/moshpit-api-key
Aug 1, 2026
Merged

feat(moshpit): let an API key drive the namespace#169
ralyodio merged 1 commit into
mainfrom
feat/moshpit-api-key

Conversation

@ralyodio

@ralyodio ralyodio commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

/api/moshpit/* is documented at the top of the router as an API, and was one only if you had a browser cookie. The API key moshcode already holds — the one /api/me and /api/sessions both accept — got 401 from every endpoint here, so the namespace was the single part of the product no script could touch.

Found while trying to submit a batch of ~30 registrations: there was no way to do it except by hand in a browser.

What changed

One middleware, scoped to /api/moshpit: if there's no cookie session, resolve the bearer key with the same userForApiKey helper the other machine endpoints use.

  • Same helper, same keys, same 401 when there is no key. This widened who can authenticate, not whether anyone has to.
  • A cookie session still wins when both are present — that caller is already identified.
  • Not applied to the /pit pages. Those are CSRF-guarded form posts, and a bearer token has no business standing in for a session there.

Blast radius — stated plainly

This widens what a leaked API key can do. It could already read your account and drive your CLI sessions; it can now claim endings, mint names, and start a name checkout (POST /api/moshpit/tlds/:tld/buy). That's the same blast radius the key has everywhere else in the product, which is the argument for consistency rather than against it — but it is a real change and worth a second opinion before merge.

Testing

New apps/pwa/test/moshpit-api-key.test.mjs — 7 tests against the real middleware stack (session + CSRF guard), not a stub:

  • a key reads and writes; no key is still 401
  • a made-up key is nobody
  • one account's key cannot repoint another account's ending (authenticating is not authorising)
  • a refused write leaves nothing behind
  • POST /pit/claim with a bearer token still gets 403 from the CSRF guard, and claims nothing

apps/pwa: 330 pass, 0 fail. Repo root: 769 pass, 0 fail, 2 skipped.

🤖 Generated with Claude Code

/api/moshpit/* is documented at the top of the router as an API and was one
only if you had a browser cookie. The key `moshcode` already holds -- the one
/api/me and /api/sessions both accept -- got 401 from every endpoint here, so
the namespace was the single part of the product no script could touch. That
is why a batch of registrations could not be submitted without sitting in the
browser doing it by hand.

Same helper, same keys, same 401 when there is no key. A cookie session still
wins when both are present, because that caller is already identified.

Scoped to /api/moshpit. The /pit pages stay browser routes: they are
CSRF-guarded form posts, and a bearer token has no business standing in for a
session there -- there is a test that posts one at /pit/claim and expects the
403 it gets today.

Worth stating plainly: this widens what a leaked API key can do. It could
already read your account and drive your CLI sessions; it can now claim
endings, mint names and start a name checkout. That is the same blast radius
the key has everywhere else, which is the argument for consistency, not
against it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio marked this pull request as ready for review August 1, 2026 02:59
@ralyodio
ralyodio merged commit 681ca9e into main Aug 1, 2026
3 checks passed
@ralyodio
ralyodio deleted the feat/moshpit-api-key branch August 1, 2026 02:59
@ralyodio ralyodio mentioned this pull request Aug 1, 2026
ralyodio added a commit that referenced this pull request Aug 1, 2026
install.sh resolves releases/latest, so the sixteen commits merged since
v0.13.3 have been sitting on main unreachable — including a fix for a page
that locks browsers up.

The headline is the pit. /pit rendered every ending an account held and a form
per name under each, with no bound on either: at 50 endings x 100 names that
was 3.1 MiB of HTML and 36,082 DOM elements, and it managed to jam a browser
with no script on the page at all (#167). It now draws a window and says what
it is not drawing — 173 KiB, 1,926 elements — with a filter box over the top
that takes `eggs` as a substring and `def*` as a glob, debounced against the
API (#168).

The namespace also stopped being the one part of the product a script could
not touch: /api/moshpit/* now accepts the same API key /api/me and
/api/sessions already did (#169), and /pit/dns finally documents the
TronBrowser route for machines whose DNS is not theirs to change (#165).

  moshcode: foreign keys are enforced, and the licence package.json
  claims actually ships (#154)
  cli: help aliases exit 0 (#157), invalid integration commands fail (#160),
  `--` is honoured (#159), a BOM before a shebang no longer breaks (#158)
  skills: engines with no skills primitive are reported, not dropped (#166);
  `--name` requires a value (#156)
  mcp: an unsupported flag is rejected rather than registered as the server
  name (#164)
  pit: the namespace rules are vendored again with a drift test holding them
  to the published package (#161, #162, #163)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant