extremely claude-assisted go game based on atproto! working on cleaning up and giving a more unique design, still has a bit of a slop vibe to it.
cloud-go STATUS.md
6.3 kB
Markdown

Project Status - atprotogo #

Last Updated: September 22, 2026

Current State #

The application builds (npm run build) and runs on Cloudflare Pages with a D1 database. Gameplay, social and image features are implemented end to end, and game records are really published to the AT Protocol network.

Implemented #

Platform #

  • SvelteKit app built for Cloudflare Pages (@sveltejs/adapter-cloudflare), deployed with npm run deploy / wrangler pages deploy
  • D1 database (atprotogo-db, bound as DB) accessed through Kysely (kysely-d1)
  • OAuth sessions and state in KV when the SESSIONS_KV / STATES_KV bindings are configured, otherwise an in-memory store
  • Bluesky OAuth via @atcute/oauth-node-client, in two configurations: a public loopback client for local development (the atproto localhost exception — the authorization server synthesises the metadata from the client_id, so nothing is published and no signing key is used) and a confidential client that publishes <PUBLIC_BASE_URL>/oauth/v2/client-metadata.json and signs its requests with PRIVATE_KEY_JWK
  • Image generation (board, OG, reaction, nudge) with @cf-wasm/resvg

Features #

  • Game Creation: Create Go games with various board sizes (5x5, 7x7, 9x9, 13x13, 19x19)
  • Game Joining: Join waiting games or play with specific opponents
  • Gameplay: Place stones, pass, resign
  • Handicap: Support for handicap stones (up to 9 on 19x19, 5 on 13x13)
  • Scoring: tenuki territory scoring with dead-stone marking and winner determination
  • Reactions: Comment on specific moves with text, emoji, and star ratings
  • Profiles: Player profiles with game history and ELO ratings
  • Image Generation: Board SVGs/PNGs, OG images, reaction images, nudge images
  • Real-time: Client-side Jetstream subscriber (src/lib/firehose.ts) streams moves, passes and resignations into the game page

Architecture Notes #

Real-time updates #

The game page subscribes to Bluesky's public Jetstream over a WebSocket and applies boo.sky.go.move / pass / resign records as they are published, so players see opponent moves without refreshing.

D1 discovery index #

Route handlers resolve a game by reading the D1 games table first and only sampling the network when it is missing. That index is maintained by a Durable Object Jetstream consumer in the standalone firehose-worker/ Worker (its own wrangler.toml, deployed separately from the Pages app): the Durable Object holds a WebSocket to Jetstream filtered to boo.sky.go.{game,move,pass,resign,action}, applies records to the same D1 database through the shared helpers in src/lib/server/firehose-records.ts, resumes from a stored Jetstream cursor after reconnects (~36h of retention), and uses a Durable Object alarm as keepalive. A Cron Trigger bootstraps and recovers the consumer. It is a separate Worker because @sveltejs/adapter-cloudflare emits a fixed _worker.js and offers no supported way to export extra Worker modules from the Pages app.

File Structure #

src/
├── routes/
│   ├── game/[id]/+page.svelte        # Main game UI with reactions
│   ├── profile/[did]/                # Player profiles
│   ├── og-image/[id]/+server.ts      # Social image generation
│   └── api/games/
│       ├── +server.ts                # Create game
│       └── [id]/
│           ├── join/+server.ts       # Join game
│           ├── move/+server.ts       # Record move
│           ├── pass/+server.ts       # Record pass
│           ├── cancel/+server.ts     # Resign
│           ├── reaction/+server.ts   # Create reaction
│           └── score/+server.ts      # Scoring
├── lib/
│   ├── server/
│   │   ├── db.ts                     # D1 + Kysely, ensureGameInDb()
│   │   ├── auth.ts                   # OAuth client
│   │   ├── scoring.ts                # tenuki scoring
│   │   ├── elo.ts                    # Ratings
│   │   └── firehose-records.ts       # Shared record → D1 helpers (used by firehose-worker)
│   ├── atproto-client.ts             # AT Protocol helpers
│   ├── firehose.ts                   # Client-side Jetstream subscriber
│   └── components/
│       └── Board.svelte              # jgoboard board component
lexicons/
├── boo.sky.go.game.json
├── boo.sky.go.move.json
├── boo.sky.go.pass.json
├── boo.sky.go.resign.json
├── boo.sky.go.reaction.json
└── boo.sky.go.profile.json
migrations/
└── 0001_initial_schema.sql           # D1 schema
firehose-worker/                      # Durable Object Jetstream consumer

Development #

Local Development #

# Install dependencies
npm install

# Build, then serve the worker with its D1 and KV bindings
npm run build
npx wrangler pages dev --port 8788

Logging in needs neither a tunnel nor a signing key on a loopback origin, so npm run setup:key is only required to exercise the deployed (confidential) client configuration. Open the app at http://127.0.0.1:8788, not localhost — the spec only allows 127.0.0.1/[::1] redirect URIs. See LOCAL_DEVELOPMENT.md.

Production (Cloudflare Pages) #

# Build for production
npm run build

# Deploy to Cloudflare
npx wrangler pages deploy .svelte-kit/cloudflare --project-name=atprotogo

The discovery-index consumer deploys separately:

npm run deploy:firehose

Production Stack:

  • Cloudflare D1 database (atprotogo-db)
  • Cloudflare KV for sessions/state (optional bindings)
  • Environment secrets from the dashboard
  • Global edge deployment

Known Limitations #

  • Scoring is a territory estimate that relies on players marking dead stones
  • The D1 discovery index only contains what the Jetstream consumer has seen (plus manual backfill via npm run backfill), so older games may be missing until backfilled
  • No time controls or undo functionality

Documentation #

  • README.md - Project overview and setup
  • CLOUDFLARE_DEPLOYMENT.md - Deployment guide
  • MIGRATION_SUMMARY.md - Migration details
  • LOCAL_DEVELOPMENT.md - Local OAuth setup