You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Drawspell (drawspell.space) is a multiplayer tabletop for Magic: The Gathering cards, with realtime shared state and deck import powered by Scryfall card data. The web client renders the board and handles client-side state, while the backend runs a PartyServer/Cloudflare Durable Object that hosts the Yjs document and per-room tokens. Both apps live under apps/ and communicate over PartyKit/PartyServer WebSockets.
Architecture at a glance
flowchart LR
Web["apps/web<br/>TanStack React Start (Vite)"] -->|WebSocket: Yjs sync + intents| Server["apps/server<br/>PartyServer (Durable Object)"]
Discord["apps/discord<br/>Cloudflare Worker (Discord Interactions)"] -->|Service Binding `SERVER`| Server
Web -->|HTTPS| Scryfall["Scryfall API"]
Discord -->|Discord REST API| DiscordApi["Discord API"]
Server --> DO[(Durable Object storage)]
Loading
Web app (apps/web): UI, routing, and client-side state; connects to PartyServer for sync; fetches Scryfall card data.
Realtime server (apps/server): PartyServer runtime; applies intents to the Yjs document; manages hidden state and room tokens in Durable Object storage.
Discord worker (apps/discord): verifies slash-command interactions, provisions rooms via server binding, and sends DM invite links.
Shared types: server imports types from apps/web/src/types (see apps/server/src/domain/types.ts).
External services: Scryfall API for card metadata; Discord API for interactions/DMs; Cloudflare Workers + Durable Objects for hosting.
Runs wrangler dev --env development in apps/server.
Dev: Discord worker
bun run dev:discord
Runs wrangler dev --env development in apps/discord.
Build
bun run build
Builds the web app.
Preview
bun run preview
Builds + previews the web app.
Test
bun run test
Runs tests for all workspaces.
Typecheck
bun run typecheck
Runs typechecks for all workspaces.
Deploy: staging (server + web)
bun run deploy:staging
Deploys dedicated staging workers.
Deploy: staging server
bun run deploy:server:staging
Deploys drawspell-server-staging.
Deploy: staging web
bun run deploy:web:staging
Builds with CLOUDFLARE_ENV=staging and deploys drawspell-staging.
Deploy: web
bun run deploy:web
Deploys apps/web.
Deploy: server
bun run deploy:server
Deploys apps/server.
Deploy: Discord worker
bun run deploy:discord
Deploys apps/discord.
Register Discord commands (guild)
bun run register:discord:commands -- --guild-id <GUILD_ID>
Fast propagation for testing.
Register Discord commands (global)
bun run register:discord:commands:global
Use after guild validation.
Smoke test: Discord /drawspell create
bun run test:discord:smoke
Runs TO-01 integration expectation used post-registration.
Cloudflare types (web)
bun run cf-typegen
Generates Workers types for the web app.
Cloudflare types (discord)
bun run --cwd apps/discord cf:typegen
Regenerates Discord worker Env types from wrangler.jsonc + .dev.vars.
Lint
TBD
No lint script is defined in package.json.
Configuration
Environment variables
Name
Used by
Description
Source
VITE_PUBLIC_POSTHOG_KEY
apps/web
Public PostHog API key used by the web client.
apps/web/.env
VITE_PUBLIC_POSTHOG_HOST
apps/web
Public PostHog host used by the web client.
apps/web/.env
JOIN_TOKEN_SECRET
apps/web, apps/server
HMAC secret for join tokens; must match across web + server workers.
Cloudflare secret or local .dev.vars
DISCORD_PUBLIC_KEY
apps/discord
Discord public key for interaction signature validation.
Cloudflare secret or apps/discord/.dev.vars
DISCORD_BOT_TOKEN
apps/discord
Bot token for DM fanout and command registration.
Cloudflare secret or apps/discord/.dev.vars
DISCORD_SERVICE_AUTH_SECRET
apps/discord, apps/server
Shared secret for internal provisioning auth.
Cloudflare secret or .dev.vars in each app
DISCORD_APPLICATION_ID
apps/discord registration script
Discord application ID used for slash-command registration.
Cloudflare secret/env var or apps/discord/.dev.vars
DISCORD_COMMAND_GUILD_ID
apps/discord registration script
Optional default guild for faster command registration rollout.
Local env or shell export
DISCORD_API_BASE_URL
apps/discord registration script
Optional Discord API base URL override for command registration.
Local env
VITE_ENV
apps/web
Build/runtime environment selector used by the web app to resolve Drawspell hosts.
wrangler.jsonc vars or Vite define
NODE_ENV
apps/server, apps/discord
Runtime environment selector used for server host resolution and Discord-to-server routing.
wrangler.jsonc vars
Env files and loading
Web: Vite loads .env* files from apps/web during vite build and vite dev.
Web: public VITE_* values belong in apps/web/.env*; runtime-only values and secrets belong in apps/web/wrangler.jsonc plus wrangler secret.
Server: Durable Object binding rooms is configured in apps/server/wrangler.jsonc; secrets use wrangler secret or apps/server/.dev.vars.
Drawspell web/server origins are centralized in packages/shared/src/constants/hosts.ts.
apps/web selects hosts with VITE_ENV; apps/server and apps/discord select them with NODE_ENV.
Discord: service binding SERVER is configured in apps/discord/wrangler.jsonc; default/prod binds to drawspell-server, and env.development binds to drawspell-server-development for local wrangler dev --env development.
Discord command registration script reads exported shell env values only (DISCORD_BOT_TOKEN, DISCORD_APPLICATION_ID, and optional DISCORD_COMMAND_GUILD_ID/DISCORD_API_BASE_URL).
Discord worker Env type declarations are generated into apps/discord/worker-configuration.d.ts; rerun bun run --cwd apps/discord cf:typegen when Discord worker vars/bindings or .dev.vars keys change.
Common workflows
Run the full system locally
In one terminal: bun run dev:server
In another terminal: bun run dev
Open the URL printed by Vite and create a game.
Run a single app/package
Web only: bun run --cwd apps/web dev
Server only: bun run --cwd apps/server dev
Discord only: bun run --cwd apps/discord dev
Deployment / operations
Staging deploy target is dev branch by convention.
Staging deploy command: bun run deploy:staging.
Web deploy: bun run deploy:web (config: apps/web/wrangler.jsonc).
Server deploy: bun run deploy:server (config: apps/server/wrangler.jsonc, apps/server/partykit.json).
Discord deploy: bun run deploy:discord (config: apps/discord/wrangler.jsonc).
Slash-command registration workflow: see apps/discord/README.md for guild-first rollout and global rollout commands.
Durable Object migrations are defined in apps/server/wrangler.jsonc.
CI: TBD (no CI config found in the repo; add one under .github/ or similar).
Performance
See docs/performance-report.md for the latest benchmarks, load scripts, and measurement notes.
Contributing
Use Bun for scripts and dependency management.
Run bun run test and bun run typecheck before opening a PR.
Keep changes scoped and update docs/tests when behavior changes.
Formatting/linting: TBD (follow existing file style until a formatter is added).