datacenterbingo/CLAUDE.md
Sergei Poljanski 1a5506c77d
All checks were successful
Build and Deploy to Production / test (push) Successful in 18s
Build and Deploy to Production / build (push) Successful in 49s
Build and Deploy to Production / deploy (push) Successful in 33s
Add a public reachability status page
asxpio now probes public hosts tied to each datacenter once a minute
and stores 5-minute rows (bingo_monitor_* tables, granted to the
read-only bingo role). Show them: /:locale/status lists every monitored
site with its current state and a 24-hour bar, /status.json has the
same data, and monitored datacenter pages get a small block.

A site is down only when nothing answered for two buckets. Everything
reads as unknown when the worker is stale or fewer than half of the
control hosts in Russia answer, since probes run from Finland and
filtering can look like an outage. The page says plainly that this is
not an incident record.

Refresh test/schema.sql for asxpio's migration 017.

Assisted-by: Claude:opus-5-5
2026-10-18 10:33:52 +04:00

4.1 KiB
Raw Blame History

datacenterbingo

Public site for datacenterbingo.xyz: a 5×5 bingo card of Russia's largest datacenters, crossed out on publicly sourced incidents. Ruby/Sinatra, read-only. Primary repo git.czsk.it/asxpi/datacenterbingo (Forgejo, CI and deploy); github.com/asxpi/datacenterbingo is a push mirror.

Shared with asxpio

Content is edited in the asxp.io admin (/admin/bingo, repo asxpio: lib/bingo.rb, lib/bingo_admin.rb). Both apps use the same Postgres tables (bingo_datacenters, bingo_incidents) and the photos under bingo/ in the datacenterbingo-photos bucket (asxpio's admin uploads them there).

  • asxpio owns the schema. Migrations live there. After it adds a bingo migration, refresh test/schema.sql (instructions in its header) and update the models here.
  • lib/bingo.rb here is the read side of asxpio's: models, Board, PROVIDERS, the T UI strings, and photo/IndexNow key reading. The admin side (writes, photo upload and resizing, IndexNow pings, ranking, seeding, guess_provider) stays in asxpio. Keep shared parts in step: PROVIDERS, Board, and model methods.
  • Prod uses a read-only DB role bingo (SELECT on the two tables) and a MinIO user limited to s3:GetObject on datacenterbingo-photos. Both are provisioned by the storage repo (init/), from the same BINGO_* secrets set on both repos.

Research

Before adding or changing datacenters, incidents or photos, follow OSINT.md. It covers sources, verification, photo rules (no people, and renderings only for unbuilt sites) and credit format.

Layout

  • app.rb: BingoWeb. Routes: / (redirect by Accept-Language), /ru|en|uk (card; ?view=all, ?p[]=provider), /:locale/dc/:slug, /sitemap.xml, /robots.txt, /p/:file (photo proxy, immutable), /<indexnow-key>.txt, icons, /healthz.
  • Feeds: /:locale/feed.xml (RSS 2.0) and /:locale/feed.json (JSON Feed 1.1), the latest 50 incidents, newest first. Item IDs are tag:<host>,2026:incident-<id>, so they stay stable when an incident is edited. Pages advertise both via <link rel="alternate"> and in the footer.
  • /data.json: every datacenter with names in all locales, specs, pages, photo credit and incidents with sources. Licensed CC BY 4.0, with the attribution inside the file. Feeds and dataset send Access-Control-Allow-Origin: *.
  • Reachability: /:locale/status and /status.json (Bingo::Status), plus a block on monitored datacenter pages. They read the bingo_monitor_* tables that asxpio's bin/monitor writes (TCP probes from Finland, 5-minute buckets, control hosts in Russia). States: up, degraded, down (nothing answered for two buckets), unknown (worker stale, or under half the controls answering). Not incidents: never cross out a cell from this alone.
  • config.ru: www. → BINGO_HOST 301.
  • views/: layout.erb (CSS and SEO head), index.erb (card, provider filter, dialogs), dc.erb, partials. assets/: icons.
  • Card rules: always 5×5. The bare locale URL is the operational card (indexed). ?view=all and provider-filtered URLs are noindex, with canonical pointing to the bare page.

Dev and tests

nix develop, then bin/test. That provisions an ephemeral Postgres and loads test/schema.sql. Run cp .env.example .env for a local server (rackup), pointing it at a DB that has the bingo tables.

Deploy

On push to main, .forgejo/workflows/deploy.yaml tests, builds with kaniko to $FJ_REGISTRY/$FJ_USER/datacenterbingo, then over SSH writes /opt/datacenterbingo/{docker-compose.yml,.env} and runs docker compose up -d. The container is on the traefik and storage networks, and the Traefik router datacenterbingo takes BINGO_HOST and www..

Secrets:

Secret Purpose
FJ_REGISTRY, FJ_USER, FJ_TOKEN kaniko clone + push (write:package, read:repository); Forgejo reserves the FORGEJO_ prefix
DEPLOY_IP, DEPLOY_USER, DEPLOY_SSH_KEY SSH to prod
BINGO_DB_PASSWORD read-only Postgres role bingo
BINGO_S3_ACCESS_KEY, BINGO_S3_SECRET_KEY read-only MinIO user
INDEXNOW same key as asxpio's INDEXNOW (served here, pinged from there)