Skip to content

feat(gastown): add org-level towns, rigs, and auth middleware#1153

Merged
jrf0110 merged 21 commits intomainfrom
feat/gastown-org-towns
Mar 18, 2026
Merged

feat(gastown): add org-level towns, rigs, and auth middleware#1153
jrf0110 merged 21 commits intomainfrom
feat/gastown-org-towns

Conversation

@jrf0110
Copy link
Contributor

@jrf0110 jrf0110 commented Mar 17, 2026

Adds organization-level town ownership to Gastown, enabling any org member to create, manage, and interact with shared towns. This is the foundational infrastructure for the collaborative multi-player Gastown described in #447.

Summary

  • Adds GastownOrgDO — a per-org Durable Object (SQLite, keyed by orgId) storing org-owned towns and rigs, with a watchdog alarm to periodically health-check town alarms. Registered as GASTOWN_ORG binding with a v2 migration tag in wrangler.jsonc.
  • Bakes org memberships into the gastown JWT — the token minting endpoint (POST /api/gastown/token) now queries the user's org memberships and includes them as an orgMemberships claim. All org membership checks in the worker read from JWT claims instead of querying Hyperdrive, eliminating DB round-trips from every org-town operation.
  • Adds org-auth.middleware.ts: verifies org membership via JWT claims, blocks billing_manager role, and sets orgId/orgRole on the Hono context.
  • Adds town-auth.middleware.ts: verifies the caller owns or is an org member of a town for /api/towns/:townId/* routes via JWT claims; handles both personal and org-owned towns with legacy fallback. Replaces the old townOwnershipMiddleware for the catch-all route.
  • Adds org-towns.handler.ts with 8 REST handlers (create/list/get/delete for towns and rigs) scoped under /api/orgs/:orgId/*.
  • Adds tRPC procedures: listOrgTowns, createOrgTown, deleteOrgTown, listOrgRigs, createOrgRig with per-procedure org membership checks (all via JWT).
  • Refactors verifyTownOwnership and verifyRigOwnership to be org-aware: personal towns use the fast-path user DO lookup; org towns fall back to TownDO config + JWT membership verification.
  • Adds resolveRigOwnerStub — a helper that returns the correct DO stub (GastownUserDO or GastownOrgDO) for rig CRUD operations based on town ownership. Used by listRigs, createRig, deleteRig, deleteTown, and ensureMayor.
  • Adds org Gastown UI under /organizations/[id]/gastown — town list/create/delete page, with sub-pages reusing existing personal gastown components wrapped in OrganizationByPageLayout.
  • Adds Gastown entry to OrganizationAppSidebar.

Closes #447 (org-level towns portion).

Verification

  • pnpm typecheck — all 30 workspace projects pass
  • pnpm lint — clean across all packages
  • prettier --check — all files formatted correctly
  • Manual review of all new files and modified code

Visual Changes

N/A

Reviewer Notes

  • JWT-based org auth: Org memberships are baked into the gastown JWT at mint time. The only staleness window is when a user is removed from an org — they retain access until their token expires (1 hour max, 55 min effective). The UI itself becomes inaccessible immediately since the Next.js app checks membership for page rendering.
  • The resolveRigOwnerStub abstraction avoids if (isOrg) … else … branching across procedures. It resolves the correct DO stub once, and callers use a common RigOwnerStub interface for rig/town CRUD.
  • deleteOrgTown tRPC and personal deleteTown tRPC both now call townStub.destroy() before deleting the owner record.
  • GastownOrgDO reuses the user_rigs table schema for org rigs. This works because each DO has isolated SQLite storage, but the naming is semantically misleading. A follow-up could rename this to a generic rigs table.
  • org-membership.util.ts still exists but is no longer imported by any worker code — it can be removed in a cleanup pass.

@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Mar 17, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 1
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
cloudflare-gastown/src/dos/Town.do.ts 525 syncConfigToContainer() never removes cleared values, so revoked PATs and cleared commit-identity flags stay active in TownContainerDO.envVars until the container is rebuilt.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
cloudflare-gastown/src/handlers/town-config.handler.ts 13 maskSensitiveValues() was not updated for the new github_cli_pat field, so the REST config endpoints still return the raw GitHub CLI PAT to any authorized town/org member.
Files Reviewed (14 files)
  • cloudflare-gastown/src/dos/Town.do.ts - 1 issue
  • cloudflare-gastown/src/handlers/town-config.handler.ts - 1 summary-only issue
  • cloudflare-gastown/src/trpc/router.ts - 0 new issues
  • cloudflare-gastown/src/dos/TownContainer.do.ts - 0 issues
  • cloudflare-gastown/src/dos/town/config.ts - 0 issues
  • cloudflare-gastown/src/dos/town/container-dispatch.ts - 0 issues
  • cloudflare-gastown/src/types.ts - 0 issues
  • cloudflare-gastown/container/src/agent-runner.ts - 0 issues
  • cloudflare-gastown/container/src/types.ts - 0 issues
  • src/app/(app)/gastown/[townId]/settings/TownSettingsPageClient.tsx - 0 new issues
  • src/app/api/gastown/token/route.ts - 0 issues
  • src/components/gastown/CreateRigDialog.tsx - 0 issues
  • src/lib/organizations/organizations.ts - 0 issues
  • packages/worker-utils/src/kilo-token.ts - 0 issues

Reviewed by gpt-5.4-20260305 · 977,716 tokens

@jrf0110 jrf0110 force-pushed the feat/gastown-org-towns branch from 7403c1a to ba3fb04 Compare March 17, 2026 21:35
jrf0110 added 11 commits March 18, 2026 10:26
…stown UI

Implement organization-owned towns as described in #447. Adds GastownOrgDO,
org auth/town auth middleware, REST + tRPC endpoints for org towns and rigs,
org-aware ownership verification, and Next.js org gastown pages.
…are ordering, org-aware rig CRUD

- listUserOrgIds now filters out billing_manager memberships
- Replace townOwnershipMiddleware with townAuthMiddleware for /api/towns/:townId/* catch-all
- Add resolveRigOwnerStub to route rig CRUD to correct DO (user vs org)
- Fix listRigs/createRig/deleteRig/deleteTown/ensureMayor to use resolveRigOwnerStub
…ive lookups

Add orgMemberships claim to the gastown JWT, populated at token mint time
from the DB. All org membership checks in the worker now read from JWT
claims instead of querying Hyperdrive, eliminating DB round-trips from
every org-town tRPC call and REST handler.
…r takeover for org gastown

- createOrgTown now mints a kilocode_token on the TownDO config at
  creation time (matching personal createTown), so ensureMayor can
  start the mayor without waiting for rig creation.
- GastownTownSidebar accepts basePath/backHref overrides for org routes.
- AppSidebar detects /organizations/[id]/gastown/[townId] paths and
  renders the town-specific sidebar with org-prefixed navigation links.
…orrect team

Add organizationId to StartAgentRequest and propagate it from TownConfig
through container-dispatch to the agent's KILO_CONFIG_CONTENT as
provider.kilo.options.kilocodeOrganizationId, matching how cloud-agent-next
handles org billing attribution.
…ials use the creator's identity

Org towns now set owner_user_id to the creator's user ID on the TownDO
config. This ensures all credential flows (container JWT, kilocode token,
agent callbacks) use the creator's identity for billing and auth.

Also fixes ensureMayor falling back to an empty string for userId when
owner_user_id was missing, which caused 401 errors on mayor tool calls.
handleMayorListRigs and verifyRigBelongsToTown now check the TownDO
config for owner_type and route to GastownOrgDO for org-owned towns
instead of always using GastownUserDO. Fixes empty rig list returned
by gt_list_rigs when the mayor calls tools in an org town.
…n pages

- Add fullBleed prop to OrganizationByPageLayout/OrganizationTrialWrapper
  to bypass the PageContainer max-width wrapper. All org gastown sub-pages
  now render edge-to-edge like personal gastown.
- Add basePath prop to TownOverviewPageClient, TerminalBar, and
  MayorTerminalBar. Org pages pass /organizations/[id]/gastown/[townId]
  so all in-app navigation stays within the org context.
- Fix hardcoded /gastown/ links in TownOverviewPageClient (rig click,
  agents link, topology rig select) and TerminalBar navigation map.
…fix auth gap, remove dead code

- verifyRigOwnership now uses Promise.all for parallel org DO fan-out
  instead of sequential iteration (O(1) round-trip vs O(N))
- handleCreateOrgRig returns 401 when userId is missing instead of
  silently falling back to empty string
- Delete dead org-membership.util.ts (replaced by JWT-based lookups)
- Import UserRigRecord from db/tables instead of local re-declaration
- Extract shared resolveTownOwnership to deduplicate resolveRigOwnerStub
  and verifyTownOwnership
jrf0110 added 10 commits March 18, 2026 10:26
…org towns

- deleteTown now calls resolveTownOwnership and checks owner role for
  org towns, closing the auth gap where any member could delete via the
  generic endpoint
- deleteRig does the same check via resolveTownOwnership on rig.town_id
- verifyTownOwnership returns orgTown.created_by_user_id as owner_user_id
  instead of fabricating the calling user's ID
…d validation to org input schemas

- handleCreateOrgRig now checks orgDO.getTownAsync(town_id) to verify
  the town belongs to the org before creating the rig
- All organizationId tRPC input schemas now use z.string().uuid() for
  consistency with townId/rigId validation
…, UI role gating

- handleDeleteOrgTown verifies town belongs to org BEFORE calling destroy
- townAuthMiddleware rejects uninitialized towns (no owner set)
- updateTownConfig strips ownership fields and requires owner role for org towns
- OrgTownListPageClient hides delete button for non-owner members
- OrganizationAppSidebar hides Gastown nav entry for billing_manager users
…ve dev-mode auth bypasses

- createOrgRig now mints kilocode_token using the town's owner_user_id
  from TownDO config instead of the calling member's identity, preventing
  credential rotation when non-owner members add rigs.
- Remove dev-mode auth bypasses for kilo/town/org middleware. The JWT
  flow works e2e in dev, and skipping auth created bugs (missing
  kiloUserId/kiloOrgMemberships on org REST handlers).
…n org towns

- createRig, createOrgRig: only re-mint kilocode_token when the caller
  IS the town owner (has their api_token_pepper). Non-owner members keep
  the existing town token instead of overwriting with an unusable one.
- sling, ensureMayor: refreshGitCredentials now uses the town owner's
  userId from TownDO config, not the calling member's identity.
- configureRig userId uses the town owner for org towns.
… installations

refreshGitCredentials now accepts an optional orgId parameter and passes
it through to GIT_TOKEN_SERVICE.getTokenForRepo(). For org towns, the
organization_id from TownDO config is passed so the installation lookup
query can match org-owned GitHub App installations via the
owned_by_organization_id column in platform_integrations.
…or org towns

CreateRigDialog now accepts an optional organizationId prop. When set:
- GitHub/GitLab repo queries use organizations.cloudAgentNext endpoints
  which call fetchGitHubRepositoriesForOrganization instead of the
  personal fetchGitHubRepositoriesForUser
- The 'Connect GitHub or GitLab' link points to the org integrations
  page (/organizations/[id]/integrations) instead of /integrations

The org town overview page passes organizationId through
TownOverviewPageClient to CreateRigDialog.
New town config fields:
- github_cli_pat: GitHub PAT used exclusively for gh CLI operations (PRs,
  issues). When set, GH_TOKEN prefers this over the integration token, so
  PRs appear under the user's GitHub identity.
- git_author_name / git_author_email: override the commit author. When
  set, the user becomes the primary author and the AI agent name is
  exposed as GASTOWN_AI_AGENT_NAME for co-authorship trailers.
- disable_ai_coauthor: omit the AI Co-authored-by trailer.

Changes:
- TownConfig + TownConfigUpdateSchema: new fields added
- container-dispatch.ts: maps new fields to GITHUB_CLI_PAT,
  GASTOWN_GIT_AUTHOR_NAME, GASTOWN_GIT_AUTHOR_EMAIL,
  GASTOWN_DISABLE_AI_COAUTHOR env vars
- agent-runner.ts: uses custom author if set, exposes AI agent identity
  for co-authorship, prefers GITHUB_CLI_PAT for GH_TOKEN
- TownSettingsPageClient: new GitHub CLI and Commit Identity sections
- router.d.ts: updated tRPC type declarations
- updateTownConfig tRPC now calls syncConfigToContainer() after saving,
  which pushes config-derived env vars (GIT_TOKEN, GITHUB_CLI_PAT,
  GASTOWN_GIT_AUTHOR_NAME/EMAIL, GASTOWN_DISABLE_AI_COAUTHOR) to the
  running container via TownContainerDO.setEnvVar(). New agent processes
  spawned after the update inherit the new values.
- buildContainerConfig now includes github_cli_pat, git_author_name,
  git_author_email, disable_ai_coauthor in the X-Town-Config header.
…r, read-only settings

Security:
- getTownConfig masks kilocode_token, github_cli_pat, git_auth tokens,
  and env_vars for non-owner org members (shows ****last4)
- REST createOrgRig no longer accepts kilocode_token in request body —
  the town's existing token (minted by owner) is always used

Container:
- Add TownContainerDO.deleteEnvVar() for removing cleared settings
- syncConfigToContainer now calls deleteEnvVar when a value is cleared,
  preventing stale credentials from persisting

UI:
- Org town settings page passes role from OrganizationByPageLayout
- TownSettingsPageClient accepts readOnly prop — hides save buttons
  and shows 'View only' badge for non-owner org members
@jrf0110 jrf0110 force-pushed the feat/gastown-org-towns branch from 8ccefc9 to 27b75f5 Compare March 18, 2026 15:26
@jrf0110 jrf0110 merged commit d34b625 into main Mar 18, 2026
17 of 18 checks passed
@jrf0110 jrf0110 deleted the feat/gastown-org-towns branch March 18, 2026 15:32
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.

Cloud Gastown: Future ideas — capabilities unique to or enhanced by the cloud model

2 participants