Public: anyone can read this; the crew can change it.

Persistent spawn caps (M2 of #2922) #3021

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Anonymous spawn caps now survive a restart, and there is a new global cap across all anonymous callers (M2 of #2922).

shipped production is serving it deploy 3b382b3

opened this

Anonymous spawn caps now survive a restart, and there is a new global cap across all anonymous callers (M2 of #2922).

bun test test/spawn-anon.test.ts passes (7 tests), and so do the migration and kernel tests. bun x tsc --noEmit printed only a read-only tempdir error, so I can't say the type check is clean; I didn't run the rest of the suite.

  • Persistence: counts are rows in a new anon_spawn table in forge.db under FORGE_APP_DATA, added by migration 20261020010000-anon-spawns. The table is pruned to the last 24 hours on each check.
  • Rate module: src/spawn/rate.ts now takes the database. The in-memory map is gone. The per-client cap is still 3 per day.
  • Global cap: 30 anonymous spawns per day across all clients, over the same 24-hour window. The 30 is my pick, and it's the constant ANON_SPAWNS_GLOBAL_PER_DAY if you want a different number. When it trips, the 429 reports the earliest time a slot frees up.
  • Call sites: src/spawn/door.ts passes ctx.app.db to the check and the count.
  • Tests: the existing test now builds the app before resetting counts. A new test shows a second connection to the same forge.db still sees the counts, that they age out after a day, and that the global cap blocks a fresh client.

I left out two things: a configurable cap through settings, and counting signed-in spawns.

To try it, make 3 anonymous POST /api/v1/forges requests from one client, restart the app, and make a fourth. It should return 429 rate_limited.

Closes #3014

commented

Anonymous spawn caps now survive a restart, and there is a new global cap across all anonymous callers (M2 of #2922).

bun test test/spawn-anon.test.ts passes (7 tests), and so do the migration and kernel tests. bun x tsc --noEmit printed only a read-only tempdir error, so I can't say the type check is clean; I didn't run the rest of the suite.

  • Persistence: counts are rows in a new anon_spawn table in forge.db under FORGE_APP_DATA, added by migration 20261020010000-anon-spawns. The table is pruned to the last 24 hours on each check.
  • Rate module: src/spawn/rate.ts now takes the database. The in-memory map is gone. The per-client cap is still 3 per day.
  • Global cap: 30 anonymous spawns per day across all clients, over the same 24-hour window. The 30 is my pick, and it's the constant ANON_SPAWNS_GLOBAL_PER_DAY if you want a different number. When it trips, the 429 reports the earliest time a slot frees up.
  • Call sites: src/spawn/door.ts passes ctx.app.db to the check and the count.
  • Tests: the existing test now builds the app before resetting counts. A new test shows a second connection to the same forge.db still sees the counts, that they age out after a day, and that the global cap blocks a fresh client.

I left out two things: a configurable cap through settings, and counting signed-in spawns.

To try it, make 3 anonymous POST /api/v1/forges requests from one client, restart the app, and make a fourth. It should return 429 rate_limited.

Approved

Approved. Caps are now rows in forge.db (migration sorts last, indexed on at, pruned on each check), the in-memory map is gone, the 429 path is unchanged, and the new test covers restart, ageing out and the global cap. Keyed on clientBucket, so no client-written header decides it. Two things I would do next: (1) check and note are separated by the awaited spawn, so concurrent strangers can overshoot the cap by a few; insert the row before spawning and delete it if the spawn fails. (2) The README should say the 3/day and 30/day figures; the 429 words should say whether it was your limit or the forge-wide one, so a person knows whether to wait or ask.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs