-
released this
2026-08-06 20:51:27 -05:00 | 97 commits to main since this releasePhase 2 of
docs/query-api-plan.md: a read-only HTTP service over the tables 0.23.0 started writing.Nothing consumes it yet. The crawler still listens on nothing and still commits to Forgejo; lol-companion still fetches the raw URLs.
The service
A second entrypoint on the same image —
node dist/src/api/index.js. Three routes, each one query:Route Auth Returns GET /v1/healthnone liveness, plus the age of the oldest artifact GET /v1/manifestkey patch, publishedAt,sha256, size per artifactGET /v1/artifacts/{name}key the whole file, byte-identical, brotli, ETag Built on
node:httprather than a framework. This is a router with one path parameter over a service that never accepts a body, and the dependency count stays at three.The stored brotli is served untouched when the client accepts it, so the common path spends no CPU compressing. Measured over loopback on patch 16.15:
Artifact File On the wire Served synergies7.88 MB 448 KB 48 ms counters1.71 MB 110 KB 20 ms runes1.32 MB 50 KB 12 ms A manifest poll that finds nothing changed is 1.1 KB and a 304 in 2.8 ms; a conditional GET of the 7.6 MB synergies file is a 304 in 9.8 ms.
/v1/artifacts/{name}is also the only response that reproduces the committed bytes, because it servespayloadrather than reassembling rows — Postgres normalises jsonb key order.scripts/verify-api.mjschecks exactly that, in both encodings, against each raw URL.Keys
Per install, never one shared key baked into the app: an asar unpacks in minutes, and a shared key cannot be revoked without breaking every install that has not updated. Per-key issuance buys revocation, per-client rate limits and accounting. It does not buy secrecy, and nothing here needs any — these are anonymised aggregates of public match data, world-readable at the Forgejo URLs today.
npm run key:create -- --label joey-desktop # prints the key once npm run key:create -- --list npm run key:create -- --revoke <key_id>Secrets are stored as SHA-256 deliberately, not bcrypt or argon2: the secret is 256 bits of CSPRNG output, so there is no dictionary to run and a slow KDF would spend real CPU per request to buy nothing.
The API gets its own Postgres role — SELECT on the published tables and
api_key, UPDATE onlast_used_at, no DDL and no INSERT. It never holds the crawler's DSN, soparticipant_statis unreadable from the process serving HTTP rather than merely unqueried. The crawler therefore createsapi_key, and the API asserts the tables exist at boot so a deployment ordering mistake reads as one sentence instead of a 500 per request.Two corrections to the plan, both found by building it
- Keys parse with an anchored regex, not
split('_'). base64url's alphabet contains_, so splitting on the last separator lands inside roughly half of all real secrets — and it would have surfaced as an intermittent authentication failure rather than a parse error. There is a test for exactly this case. - The
api_key_activepartial index is dropped.key_idis already the primary key.
Fixed
- Integration tests now run one file at a time. They share
lolc_testand all truncate inbeforeEach, so vitest's parallel workers had a second file deleting rows a first was midway through asserting on. Invisible until two files touched the same tables.
Deploying
Not deployed by this release. It needs a second service in the Swarm stack (same image,
command: ["node", "dist/src/api/index.js"]), thelolc_apiPostgres role, and at least one key minted in the crawler container. The stack file carries all three as comments.Keep it on the LAN: a bearer token over plaintext is a non-starter the moment it leaves the network.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Keys parse with an anchored regex, not