Conversation
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Other Observations (not in diff)No additional issues found outside the diff. Files Reviewed (100 files)Worker & Middleware (core security)
Handlers
Durable Objects
Utilities
Container
Other files (types, prompts, tables, tests, UI, config) - 0 new issues |
src/app/(app)/gastown/[townId]/settings/TownSettingsPageClient.tsx
Outdated
Show resolved
Hide resolved
1f8c996 to
43d6046
Compare
45e5e90 to
3decd5c
Compare
b67140b to
dfbf2ad
Compare
153a305 to
908395d
Compare
aea8ec9 to
c275145
Compare
cb10af4 to
43dbbe5
Compare
43dbbe5 to
2f4fc7e
Compare
2f4fc7e to
30c72c9
Compare
Gastown is an autonomous software engineering platform built on Cloudflare Workers, Durable Objects, and Containers. This branch implements the full stack from scratch.
30c72c9 to
1e0cf8a
Compare
| townId, | ||
| source_rig_id: params.rigId, | ||
| severity: parsed.data.priority ?? 'medium', | ||
| message: parsed.data.title, |
There was a problem hiding this comment.
WARNING: body and metadata from the request are validated by the Zod schema (lines 12-14) but silently dropped here — only title is passed as message. The body field likely contains important context for the escalation that should be preserved.
Additionally, source_agent_id is hardcoded to undefined even though the authenticated agent's ID is available via getEnforcedAgentId(c). This means escalations lose attribution to the agent that created them.
| message: parsed.data.title, | |
| message: [parsed.data.title, parsed.data.body].filter(Boolean).join('\n\n'), |
| ); | ||
| } | ||
| console.log( | ||
| `${HANDLER_LOG} handleSlingBead: rigId=${params.rigId} title="${parsed.data.title?.slice(0, 80)}" metadata=${JSON.stringify(parsed.data.metadata)}` |
There was a problem hiding this comment.
WARNING: JSON.stringify(parsed.data.metadata) logs the full metadata object, which could contain sensitive data (tokens, credentials, PII) depending on what callers include. Consider logging only the metadata keys or a truncated summary.
| `${HANDLER_LOG} handleSlingBead: rigId=${params.rigId} title="${parsed.data.title?.slice(0, 80)}" metadata=${JSON.stringify(parsed.data.metadata)}` | |
| `${HANDLER_LOG} handleSlingBead: rigId=${params.rigId} title="${parsed.data.title?.slice(0, 80)}" metadataKeys=${JSON.stringify(Object.keys(parsed.data.metadata ?? {}))}` |
- Add .catch() error logging on fire-and-forget dispatchAgent in slingBead - Validate SDK session.create response with Zod schema instead of duck-typing - Add tests verifying registerAgent stores rig_id correctly - Store town config per-request via Hono context with Zod validation - Emit notification_failed bead events when escalation mayor notification fails - Wrap git credential write in try/catch during rig creation for resilience - Verify /api/gastown/git-credentials route exists (confirmed) - Return 403 (not 400) for cross-town access attempts via resolveTownId
## Summary Addresses all 8 hardening items identified in the PR #544 code review. Closes #686 ## Changes ### 1. Fire-and-forget `dispatchAgent` error logging - Added `.catch()` with `console.error` on the `void this.dispatchAgent()` call in `slingBead` so failures are visible in logs. ### 2. Zod validation for SDK session creation response - Replaced fragile `sessionResult.data ?? sessionResult` + `'id' in session` duck-typing with a Zod schema (`z.object({ id: z.string().min(1) }).passthrough()`). - Now throws a clear error if the SDK response shape changes unexpectedly. ### 3. `registerAgent` stores `rig_id` — verified + tested - Verified the code path: handler passes `{ ...parsed.data, rig_id: params.rigId }` → `agents.registerAgent` stores `input.rig_id ?? null` in the beads table `rig_id` column. - Added two integration tests: one verifying `rig_id` is stored when provided, one verifying it's `null` when omitted. ### 4. Thread-safe `currentTownConfig` via Hono context - Added Zod validation (`z.record(z.string(), z.unknown())`) for the `X-Town-Config` header JSON. - Typed the Hono app with `ContainerEnv` variables and store config per-request via `c.set('townConfig', ...)`. - Renamed module-level variable to `lastKnownTownConfig` to clarify it's a cache, not the authoritative source. ### 5. Emit bead event on failed escalation notification - Added `notification_failed` to the `BeadEventType` enum (both `bead-events.table.ts` and `rig-bead-events.table.ts`). - Both `routeEscalation` and the re-escalation alarm now emit a `notification_failed` bead event with metadata (target, reason, severity) when mayor notification fails. - Fixed the re-escalation path that was silently swallowing errors with `.catch(() => {})`. ### 6. Resilient rig creation + git credential write - Wrapped the `updateTownConfig` git credential write in a try/catch so a failed credential write doesn't propagate as a rig creation failure. - Clear error logging with rig ID and integration ID for debugging. - Container-side `resolveGitCredentialsIfMissing` and `refreshTownGitCredentials` serve as recovery mechanisms. ### 7. `/api/gastown/git-credentials` route — verified - Confirmed the route exists at `src/app/api/gastown/git-credentials/route.ts` with proper auth, ownership verification, and Zod validation. No changes needed. ### 8. Return 403 for cross-town access attempts - Introduced `resolveTownId()` in auth middleware returning a discriminated union: `{ townId }` | `{ error: 'missing', status: 400 }` | `{ error: 'forbidden', status: 403 }`. - Updated all 31 handler call sites across 8 handler files to use `resolveTownId()` and return the correct HTTP status code. - Kept `getTownId()` as a backward-compatible convenience wrapper. ## Files changed (16) | File | Change | |------|--------| | `cloudflare-gastown/src/dos/Town.do.ts` | Items 1, 5 | | `cloudflare-gastown/container/src/process-manager.ts` | Item 2 | | `cloudflare-gastown/container/src/control-server.ts` | Item 4 | | `cloudflare-gastown/src/db/tables/bead-events.table.ts` | Item 5 | | `cloudflare-gastown/src/db/tables/rig-bead-events.table.ts` | Item 5 | | `cloudflare-gastown/src/middleware/auth.middleware.ts` | Item 8 | | `cloudflare-gastown/src/handlers/rig-*.handler.ts` (8 files) | Item 8 | | `cloudflare-gastown/test/integration/rig-do.test.ts` | Item 3 | | `src/routers/gastown-router.ts` | Item 6 |
What is Gastown?
Gastown is an agent orchestration system for managing multiple AI coding agents working concurrently across git repositories. It was originally built as a local CLI tool — two Go binaries (
gtfor orchestration andbdfor the Beads work-tracking database) coordinated with tmux on the user's machine.The core primitive is beads — a universal data model where every object (issues, mail, merge requests, escalations, convoys, agents) is a bead. Agents are organized into roles: a Mayor that coordinates, Polecats that do the work, a Refinery that reviews and merges, and a Witness that monitors health.
What is this PR?
Gastown Cloud — a cloud-native implementation of Gastown as a Kilo platform feature, built on Cloudflare Workers, Durable Objects, and Containers. Instead of tmux on a laptop, agents run in Cloudflare Containers. Instead of databases on disk, state lives in DO SQLite. Users interact through a web dashboard with live agent terminals via xterm.js.
There's still a lot of work to do being tracked in #204
Architecture
A diagram for ants

Data model
Agent system
Terminal and UI
Infrastructure