Skip to content

fix(gastown): stop towns whose owner lacks Gastown access - #8558

Merged
eshurakov merged 1 commit into
mainfrom
fix/gastown-revoke-unflagged-towns
Oct 22, 2026
Merged

eshurakov merged 1 commit into
mainfrom
fix/gastown-revoke-unflagged-towns

Conversation

@eshurakov

Copy link
Copy Markdown
Contributor

Summary

Gastown is EOL, but the gastown-access gate let users keep running towns. Some use them only for compute. This PR stops towns whose owner is not in the gastown-access PostHog flag, and blocks their HTTP API access.

  • HTTP routes. /api/users/*, /api/towns/* and /api/orgs/* now require the gastownAccess claim or an admin token (gastownRouteAccessMiddleware). Plain Kilo API keys never carry the claim, so they no longer bypass the flag. tRPC already had this check.
  • Running containers. The TownDO alarm checks the owner's flag every 10 minutes (dos/town/owner-access.ts). The owner is the user the mayor runs as. When the flag is off, the alarm stops the container. TownContainerDO.setAccessRevoked stores a block, and assertAutomaticStartsEnabled and onStart enforce it, so warm-up, fetch, WebSocket and rig setup all fail. The block is separate from the run policy, so the owner cannot clear it. It clears when the owner is back in the flag.
  • Flag check. util/gastown-access-flag.util.ts calls PostHog /flags/?v=2 with the same body as posthog-node getFeatureFlag, scoped to this one flag. This is the call the web token route makes. Outside production, access is always allowed, as in the web app. A missing flag counts as denied. A PostHog error or quota limit keeps the last verdict.
  • Route fix. The town route middleware chain now uses every() from hono/combine. Before this, a 403 from townAuthMiddleware was lost and returned as a 500.
  • Adds the public PostHog project token NEXT_PUBLIC_POSTHOG_KEY to wrangler.jsonc. kiloclaw and kilo-mcp already commit the same token.

Verification

  • Ran a live PostHog /flags request with the worker's body. A cohort user returns enabled: true (condition_match). An unknown id returns enabled: false (no_condition_match), with errorsWhileComputingFlags: false.
  • Gastown unit suite: 338 passed. typecheck and lint pass.
  • New and changed integration tests (owner-access, user-route-auth): 50 passed. On main, the full integration suite fails 52 tests, and this branch fails the same 52.

Visual Changes

N/A

Reviewer Notes

  • Deploy order. Deploy only after the cohort is live in PostHog. The alarm check has no admin bypass, so admins and staff need to be in the cohort.
  • Distinct id. The flag is evaluated by Kilo user id, as in the web token route. PostHog links that id to the person through the signup alias.
  • Org towns. Org towns are checked against the creator, not the org.
  • Rig routes. Rig routes under /api/towns/:townId/rigs/:rigId/* keep their own auth, which containers need. A revoked owner can still write beads there, but no container can start.
  • Alarm cadence. Revoked towns use the idle alarm interval. PostHog gets about 6 requests per hour for each town with an active alarm.

Town, user and org HTTP routes now require the gastownAccess claim or an
admin token. Plain Kilo API keys no longer bypass the PostHog flag.

The TownDO alarm checks the owner's gastown-access flag every 10 minutes.
When the flag is off, it stops the running container and blocks all
container starts in TownContainerDO. The run policy cannot clear the block.
@kilo-code-bot

kilo-code-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed the Gastown access-revocation change end to end — the new route gate, the alarm-driven owner check, the container-level start block, and the PostHog flag util — and found no correctness or security defects on the changed lines.

Assumptions: [INFERENCE] I could not execute the test suites or typecheck (read-only review), so I relied on the PR's reported results; verified by reading that container-facing routes (container-ready, refresh-container-token, container-registry, db-snapshot, mayor-id, container-events, and the rig routes) are registered before the new /api/towns/:townId/* gate and keep their own auth. Two accepted-by-design edge cases are worth noting for operations: (1) an unknown PostHog verdict on the first-ever evaluation defaults to "not revoked" (fail-open for towns with no stored verdict), and (2) a legacy HTTP-created town with no owner_user_id/created_by_user_id and no rigs resolves no owner and is therefore never revoked.

Files Reviewed (14 files)
  • services/gastown/src/billing/ContainerBilling.error.ts
  • services/gastown/src/dos/Town.do.ts
  • services/gastown/src/dos/TownContainer.do.ts
  • services/gastown/src/dos/town/owner-access.ts
  • services/gastown/src/dos/town/owner-access.test.ts
  • services/gastown/src/gastown.worker.ts
  • services/gastown/src/middleware/gastown-access.middleware.ts
  • services/gastown/src/trpc/router.ts
  • services/gastown/src/util/gastown-access-flag.util.ts
  • services/gastown/src/util/gastown-access-flag.util.test.ts
  • services/gastown/test/integration/owner-access.test.ts
  • services/gastown/test/integration/user-route-auth.test.ts
  • services/gastown/worker-configuration.d.ts
  • services/gastown/wrangler.jsonc

Reviewed by deepseek-flash-latest · Input: 262.9K · Output: 51.1K · Cached: 5.6M

Review guidance: REVIEW.md from base branch main

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.

2 participants