Forgejo reserves names starting with FORGEJO_, and since runner v13
secrets.FORGEJO_TOKEN, _USER and _REGISTRY no longer reach the job as
the values set on the repository: Kaniko logged in with the wrong
credentials and every image push failed with UNAUTHORIZED.
The registry host and user are not secret (they are in every image
name), so write them in the workflow; the token comes from FJ_TOKEN,
as in datacenterbingo.
Assisted-by: Claude:opus-5-5
There was no way to put a file up for public use short of running an
S3 client with the uploader key. Add a "files" area to the admin:
upload one or more files, copy the link, edit the note, replace the
contents under the same link, delete. It has its own read/write/delete
permission flags like the other areas, and every action is audited.
The objects go to the storage stack's public bucket and are served by
Garage at s3.asxp.io/public/<key>, not by this app. The app's key can
only put and delete there, so a public_files table is the record of
what was published. Keys get a random prefix: links are not guessable
and names never collide.
That host is same-site with the admin, so a file that can carry script
must not render there. Only a fixed list of types (images, PDF, plain
text, audio, video) is served inline; the rest, HTML and SVG included,
is stored as an attachment. Image metadata is stripped by default, as
for the bingo photos, because of GPS tags.
A per-file limit and a total quota keep the small disk on asxpio-ops
from filling up.
Assisted-by: Claude:fable-5-2
The storage stack replaces MinIO, abandoned upstream, with Garage. Point
the app at garage:3900.
Garage only accepts keys in its own format, so hash the existing
ASXPIO_S3_* secrets into it the way storage's bootstrap does. The
secrets stay as they are; a pair already in Garage's format is used
unchanged.
Assisted-by: Claude:fable-5-2
The runner mounts a persistent nix-store volume over /nix. The current
nixos/nix image makes /etc/nix/nix.conf a symlink into /nix/store, and
the volume does not hold that path, so the link dangles and appending
to it fails before checkout: "/etc/nix/nix.conf: No such file or
directory".
Pass the settings in NIX_CONFIG instead. Without a config file Nix also
falls back to build-users-group = nixbld, which the container lacks, so
set it empty and turn the sandbox off, as the image config does.
Assisted-by: Claude:opus-5-5
The bingo photos sat under bingo/ in the invoice bucket. Store them in
BINGO_S3_BUCKET (datacenterbingo-photos in prod, created and pre-filled
by the storage repo with the same keys) and fall back to S3_BUCKET when
unset, so dev and tests need nothing new.
Assisted-by: Claude:opus-5
A newly recorded incident only showed up in search once a crawler
happened to come back. IndexNow lets Bing, Yandex and the other
participating engines know about changed URLs right away.
Bingo::IndexNow posts the card URLs plus the affected datacenter pages
to api.indexnow.org, in a background thread so admin saves don't wait.
Incidents ping their datacenter. Edits ping the datacenter, or every
page when positions move (create, delete, reorder, sort). A deleted
datacenter's pages are included so the engines see the 404. BingoWeb
serves the key at /<key>.txt.
The key comes from the INDEXNOW Forgejo secret, written to .env as
INDEXNOW_KEY. With no key set this does nothing.
Assisted-by: Claude:opus-5
datacenterbingo.xyz resolves now, so the stopgap second hostname is no
longer needed. Drop it from the Traefik rule and the deploy env; the
app-side BINGO_ALT_HOST support stays but is unset.
Assisted-by: Claude:opus-5
datacenterbingo.xyz is not in the .xyz zone yet, while bingo.asxp.io
already resolves. Serve the card on both (BINGO_ALT_HOST) so it is
reachable now; canonical and hreflang links keep pointing at the .xyz
domain. Footer gets a copyright line linking back to asxp.io.
Assisted-by: Claude:opus-5
The card gets its own domain instead of a subdomain of the IE site.
www. is routed to the same container and 301-redirected to the apex in
config.ru, so there is one canonical host for links and the cert.
Assisted-by: Claude:opus-5
Forgejo no longer allows creating secrets prefixed FORGEJO_, so the
regenerated registry token (the old one got 401 reqPackageAccess on
push) has to live under a new name.
Assisted-by: Claude:opus-5
Add a second public site to the same process: a 5x5 card of the 25
largest Russian datacenters where a cell is crossed out once an incident
with a public source is recorded (Yandex Sasovo and Kaluga drone strikes
on 2026-10-08/09 are seeded). config.ru dispatches by Host, so it rides
the existing container, DB and bucket instead of a new deploy.
The card is localized ru/en/uk with per-locale columns falling back to
ru. Content is managed from /admin/bingo on asxp.io, reusing AdminAuth
and CSRF. Photos go to the existing bucket under bingo/ and are proxied
by the app, so the bucket stays private and storage needs no change.
The seed data was fact-checked against operator sites and press on
2026-10-10; several entries in the original list were wrong or vague.
Assisted-by: Claude:opus-5
One asset per invoice from a registry (lib/crypto_asset.rb): BTC, LTC,
ETH, XMR, SOL, ALGO, USDT/USDC on ERC-20/TRC-20/BEP-20/Solana/Algorand.
Migration 003 generalizes the ltc_* columns to crypto_* + crypto_coin
(existing LTC invoices backfilled). QR payload adapts per asset: BIP21
amount URIs where supported, bare address for tokens with a network
hint on the PDF. Default addresses come from the CRYPTO_ADDRESSES
secret (JSON code=>address); LTC_ADDRESS still works as legacy.
Extends the .env heredoc the workflow drops on the prod host with:
- ADMIN_USER / ADMIN_PASSWORD — gate /admin/*
- DATABASE_URL — points at the storage stack's Postgres
- S3_* (endpoint, bucket, creds) — MinIO; S3_PUBLIC_ENDPOINT is the
s3.asxp.io hostname used only for
presigning so browser-facing URLs
resolve over Traefik+TLS.
Non-secret values stay hardcoded in the workflow; secrets come from
Forgejo. ASXPIO_DB_PASSWORD / S3 creds must match the corresponding
secrets on the storage repo or the app can't authenticate.