Skip to content

fix(lab): let anyone reach strut's public POSTs; bump strut to a9f656c5 - #1855

Merged
Evanfeenstra merged 1 commit into
mainfrom
fix/lab-public-posts
Nov 11, 2026
Merged

Evanfeenstra merged 1 commit into
mainfrom
fix/lab-public-posts

Conversation

@Evanfeenstra

Copy link
Copy Markdown
Collaborator

Why

strut's paid-runs plan is merged. Anyone can now launch a runnable workflow or call a runnable tool, free or paid in sats over L402. mcp's /lab gate let through only one public POST (/public/mcp). Once the pin moved, every public launch, credit purchase, claim, cancel and tool call would have been mcp's 401 before strut saw it.

What

  • Gate (mcp/src/lab/mount.ts isPublicViewRead): POSTs pass when strut's exported PUBLIC_POST matches them, and reads when PUBLIC_PATH matches. The gate now follows strut as it adds routes. It also passes GET/HEAD /llms.txt, which shows only what GET /public shows and answers 404 when nothing is public. Everything the gate refused before is still refused, including /public-view, other methods, look-alikes and paths that resolve outside /public.
  • Pin d3060351 → a9f656c5 (strut main). I deleted strut's lock block and re-resolved it, since strut added @noble/curves. The ai / @ai-sdk/anthropic peer ranges are unchanged, and mcp's exact pins still satisfy them. strut's engines is now >=22.13; the image runs 22.23.4.

What else the bump brings

  • GET /public now lists runnable. strut seeds a Game Concept page under App.
  • The orchestrator template now grants remote/list and remote/buy-credit. The second is gated: a call becomes a proposal the person must accept, and it needs a wallet.
  • Nothing needs wiring in createLabStrut to boot or serve as before. Nothing is sold until an owner makes an item runnable and sets a wallet.
  • Knobs an operator can set later (none required):
    • LIGHTNING_* (or PUT /lab/earnings/wallet): the wallet.
    • STRUT_RATE_URL: the BTC exchange-rate source.
    • STRUT_PUBLIC_RUNS_MAX: public runs in flight at once (default 10).
    • STRUT_REMOTE_LOCAL: lets this strut buy from a seller on loopback or the LAN.
  • publicRuns.principal (one budget that pays for strangers' runs) is not reachable on a swarm. The lab never reads STRUT_HOST_CONFIG, so a public run bills the workflow's owner, which is strut's default rule. Setting a separate principal would need a small mcp change.

Tests

npm run test:node: 1055/1055 pass. tsc --noEmit is clean. mcp has no CI that runs these.

  • A new gate test sends one example per PUBLIC_POST route with no credential and expects it through. It also asserts that strut's matcher still matches each example. Look-alikes (/public/workflows/x/run/extra and others), the private doors (/workflows/x/run, /chat, …) and /llms.txt by any method other than GET/HEAD stay 401.
  • Through the real bridge with nothing runnable, strut answers credit, claim, launch, cancel and tool call with its own 404, never the gate's 401.
  • Three existing tests now expect what the bump brings: runnable in GET /public, the Game page, and remote/buy-credit among strut's gated tools.

Boot check: I booted the lab on this pin against a throwaway Neo4j with API_TOKEN set: graph workspace, all lab seeds, no new warnings. With no credential:

  • GET /lab/public → 200 {…, runnable: []}
  • POST /lab/public/credit → strut's 404 "sells nothing to run"
  • claim, launch, cancel and tool call → strut's 404s
  • GET /lab/llms.txt → strut's 404
  • POST /lab/workflows/hello/run, POST /lab/chat and POST /lab/public/workflows/x/run/extra → 401

Not tested: a priced item's 402 / L402 round trip through the gate (no wallet locally), and a real swarm.

The /lab gate passed one public POST by hand (`/public/mcp`). strut's
paid-runs plan added more: credit bought and claimed, a public run's
launch and cancel, a runnable tool's call. Behind the old gate every one
was mcp's 401 before strut saw it.

- The gate now passes exactly what strut's exported `PUBLIC_POST` matches
  (and `PUBLIC_PATH` for reads), so it follows strut as it grows, plus
  `GET/HEAD /llms.txt`. Everything else it refused stays refused.
- strut pin d3060351 -> a9f656c5 (main, paid-runs merged). Its lock block
  was re-resolved: strut now depends on @noble/curves.
- Tests follow the bump: `GET /public` lists `runnable`; strut ships a
  `Game` Concept page; the orchestrator gates `remote/buy-credit`.
@Evanfeenstra
Evanfeenstra merged commit 03616cd into main Nov 11, 2026
3 checks passed
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