- commit
- d260facae69d8b326fc1f7346b9e027a40b87d1d
- parent
- 0125f11ec2afc9470892c65398979152040a803d
- author
- dami <gazey55@gmail.com>
| A | .dockerignore | 11 | +++++++ |
| A | Dockerfile | 35 | +++++++++++++++++++++ |
| A | deploy.md | 50 | ++++++++++++++++++++++++++++++ |
| A | fly.toml | 45 | +++++++++++++++++++++++++++ |
@@ -0,0 +1,35 @@
1+# mosaic: the Go engine and the Deno/Fresh site in one image. Generation2+# and judging run as subprocesses of the site; the daily loop runs3+# in-process (MOSAIC_DAILY=1) with everything persistent under /data.4+5+# --- engine ---------------------------------------------------------------6+FROM golang:1.25-bookworm AS engine7+WORKDIR /src/engine8+COPY engine/go.mod engine/go.sum ./9+COPY engine/ ./10+ENV CGO_ENABLED=011+RUN go build -trimpath -ldflags="-s -w" -o /out/mosaic-engine .12+13+# --- site -----------------------------------------------------------------14+FROM denoland/deno:2.9.5 AS site15+WORKDIR /app/site16+COPY site/deno.json site/deno.lock ./17+RUN deno install18+COPY site/ ./19+RUN deno task build20+21+# --- runtime --------------------------------------------------------------22+FROM denoland/deno:2.9.523+WORKDIR /app/site24+COPY --from=engine /out/mosaic-engine /app/engine/bin/mosaic-engine25+COPY --from=site /app/site /app/site26+ENV MOSAIC_ENGINE=/app/engine/bin/mosaic-engine \27+ MOSAIC_DATA=/data/ \28+ MOSAIC_DAILY=1 \29+ MOSAIC_PUBLIC=1 \30+ PORT=808031+VOLUME ["/data"]32+EXPOSE 808033+# warm the module cache so the first request is not a cold compile34+RUN deno cache _fresh/server.js || true35+CMD ["sh", "-c", "mkdir -p /data && exec deno serve -A --host 0.0.0.0 --port ${PORT} _fresh/server.js"]@@ -0,0 +1,50 @@
1+# Deploying the daily2+3+One machine on Fly.io runs the Deno site with the Go engine as a4+subprocess; everything persistent (games, generator cache, day archives,5+the days index) lives on the `/data` volume. Generation is offline: the6+day's field and all its growth rings are generated ahead (cached), and7+daytime growth only reveals a stored ring.8+9+## First time10+11+```12+fly launch --no-deploy --copy-config --name mosaic-daily --region ams13+fly volumes create mosaic_data --region ams --size 114+fly deploy15+```16+17+## Every time18+19+```20+fly deploy21+```22+23+## Configuration (fly.toml [env])24+25+- `MOSAIC_DAILY=1` runs the daily loop in-process; `MOSAIC_DAILY_ROLLOVER_UTC`26+ (default 0) is the hour the day changes; `MOSAIC_DAILY_GROW_MINUTES` (60)27+ the growth cadence; `MOSAIC_DAILY_CHUNKS`/`CHUNK_G`/`G` (3 / 60 / 1000) the28+ field; `MOSAIC_DAILY_RINGS` (`all`) how many rings are generated ahead;29+ `MOSAIC_DAILY_NAMESPACE` changes every day's seed.30+- `MOSAIC_PUBLIC=1` serves `/mcp` for any Host; `MOSAIC_PUBLIC_URL` is the31+ origin agents are told to use.32+- `MOSAIC_DATA=/data/` is the volume.33+34+## Checks35+36+- `fly logs` shows `{"daily": "started day-YYYY-MM-DD ..."}` on boot and37+ `prepared <date>` once tomorrow's field is generated.38+- `curl https://mosaic-daily.fly.dev/leaderboard/<date>` returns both boards.39+- A restart (`fly machines restart`) resumes today's game from its log at40+ the same revision.41+42+## Playing against it43+44+Open `https://mosaic-daily.fly.dev/` and press "copy prompt", or:45+46+```47+cd site && deno task solve -- --url https://mosaic-daily.fly.dev/mcp --join <game_id> --agent me --model my-model48+```49+50+(`list_games` on the MCP endpoint names today's game: `day-<date>`.)@@ -0,0 +1,45 @@
1+# Fly.io: one machine, one volume, the daily loop in-process.2+app = "mosaic-daily"3+primary_region = "ams"4+5+[build]6+ dockerfile = "Dockerfile"7+8+[env]9+ MOSAIC_DAILY = "1"10+ MOSAIC_PUBLIC = "1"11+ MOSAIC_PUBLIC_URL = "https://mosaic-daily.fly.dev"12+ MOSAIC_DATA = "/data/"13+ MOSAIC_DAILY_GROW_MINUTES = "60"14+ MOSAIC_DAILY_RINGS = "all"15+ PORT = "8080"16+17+[[mounts]]18+ source = "mosaic_data"19+ destination = "/data"20+ initial_size = "1gb"21+22+[http_service]23+ internal_port = 808024+ force_https = true25+ auto_stop_machines = "off"26+ auto_start_machines = true27+ min_machines_running = 128+ processes = ["app"]29+30+ [http_service.concurrency]31+ type = "requests"32+ hard_limit = 50033+ soft_limit = 20034+35+[[http_service.checks]]36+ interval = "30s"37+ timeout = "10s"38+ grace_period = "60s"39+ method = "GET"40+ path = "/"41+42+[[vm]]43+ size = "shared-cpu-2x"44+ memory = "2gb"45+ cpus = 2