WIP: feat(website): a report inbox for the launcher, filing reports as issues #251

Draft
TrashPanda wants to merge 2 commits from TrashPanda/Longshot:feat/report-inbox into main
Contributor

Why

The launcher's Send report (LongshotLauncher #42) has nowhere to send to: its inbox is unset, so crash and build reports never reach us. This makes the website's Worker that inbox. Each report becomes a Longshot issue, the same crash from many PCs collects on one issue, and players need no account.

What changed

  • website: worker/report.ts (new) answers POST /report. It checks the report (format 1, a kind the launcher knows, at most 256 KB of text and a 4 MB PNG, signature and version cut to one line, @mentions broken, control characters removed). Then it files the report through the Forgejo API as a bot account. file() searches for an open issue whose title starts with [report <key>] and comments there; if there is none, it opens a new issue with the labels in REPORT_LABELS. The picture is attached to whichever it filed. The answer is {"id": "#123", "url": ...}, which ReportSender shows the player.
  • website: the key is the first 10 hex digits of SHA-256(kind, signature), the same on every PC (keyOf).
  • website: worker/index.ts routes /report to it. In wrangler.jsonc: /report in run_worker_first, two rate limiters (REPORT_LIMIT 3 a minute per address, REPORT_LIMIT_ALL 20 a minute in total), and the vars REPORT_REPO and REPORT_LABELS.
  • website: off the site's own host (a PR preview), the inbox validates the report and answers {"id": "dry-run"} without filing it. Without the secret REPORT_FORGEJO_TOKEN, or with REPORTS_OFF=1, it answers 503, which the launcher already treats as "not sent, copy it instead".
  • docs: website/README.md, "The report inbox" (how it behaves, the limits, setup, turning it off, the token and previews); the header of .forgejo/workflows/website.yml names the new secret and why previews must stay reviewed.
  • tests: website/tests/report.test.ts (9 tests, a stand-in Forgejo and rate limiter, no network).

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
REPORT_FORGEJO_TOKEN (Worker secret) The bot's token (scope write:issue); without it the inbox is off (503) unset set it after merge (README steps) all - -
REPORTS_OFF (Worker secret) 1 turns the inbox off at once, no deploy unset for spam or an outage all - -
REPORT_REPO, REPORT_LABELS (vars) Where reports go, and their labels MagicBeanDev/Longshot, kind/bug,source/report as is all - -

Measurements

No performance change: a new route on the Worker. /download and the site are untouched (still 302/200 under wrangler dev).

Testing

Tested: On Linux (Node 24, wrangler 4.160.1, workerd), on feat/report-inbox:

  • node --test website/tests/*.test.ts: the 9 new tests and download.test.ts pass. The 7 failures in graphics-settings.test.ts fail the same way on main (the README names no condition for the HDR tone mapper).
  • npx wrangler deploy --dry-run: the config is valid, and the bindings show both rate limiters and the vars.
  • npx wrangler dev, real requests: a GET gets 405, an unknown kind 400, a report with no token 503, the 4th request in a minute from one address 429, and /download/launcher/nightly is still a 302.
  • Against the real git.magicbean.dev (file() from report.ts, filing on my fork with issues turned on for the test): the first report opened an issue with the label and frame.png attached and shown in it. The same crash again was a comment on that issue, and another signature opened a second issue. The @ mention showed broken. I deleted the test issues and the label afterwards, and turned issues off again.
  • python -I -B tests/run_all.py: 510 passed, 18 skipped, 1 failed. test_lift_cache.py needs numpy, which this PC's isolated Python lacks; it fails the same way on main.

Not tested: the deployed Worker. The secret isn't set until after merge, and the rate limiters only run for real on Cloudflare (they are also counted locally under wrangler dev, as above). Not run with the launcher itself: #42's DefaultInbox is still unset (see the launcher PR).

Needs other testers:

  • A maintainer with the Cloudflare account: after merge, the setup in the README. Then LONGSHOT_REPORT_URL=https://longshot.magicbean.dev/report with a launcher from #42, and a Send report, expecting an issue [report …] on Longshot (request to open)

Screenshots

Nothing on screen (the issues it files are plain Forgejo issues).

Risk and rollback

Spam, or a flood of issues. Each address is limited to 3 a minute and everyone together to 20; one problem is one issue; and REPORTS_OFF=1 (a secret, no deploy needed) turns it off. A leaked token can only write issues; revoke it in the bot's settings. Previews share the Worker's secrets, so the existing preview review is what protects the token. Reverting the commit removes the route.

Reviewer notes

Read worker/report.ts first. Setup left to a maintainer: the bot account (Triage role on Longshot, or Forgejo files the issues without labels), its token as a Worker secret, and the label source/report.

Issues

See LongshotLauncher #42.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: no performance change (named under Measurements).
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.
## Why The launcher's **Send report** (LongshotLauncher [#42](https://git.magicbean.dev/MagicBeanDev/LongshotLauncher/pulls/42)) has nowhere to send to: its inbox is unset, so crash and build reports never reach us. This makes the website's Worker that inbox. Each report becomes a Longshot issue, the same crash from many PCs collects on one issue, and players need no account. ## What changed - website: `worker/report.ts` (new) answers `POST /report`. It checks the report (format 1, a kind the launcher knows, at most 256 KB of text and a 4 MB PNG, signature and version cut to one line, `@mentions` broken, control characters removed). Then it files the report through the Forgejo API as a bot account. `file()` searches for an open issue whose title starts with `[report <key>]` and comments there; if there is none, it opens a new issue with the labels in `REPORT_LABELS`. The picture is attached to whichever it filed. The answer is `{"id": "#123", "url": ...}`, which `ReportSender` shows the player. - website: the key is the first 10 hex digits of SHA-256(kind, signature), the same on every PC (`keyOf`). - website: `worker/index.ts` routes `/report` to it. In `wrangler.jsonc`: `/report` in `run_worker_first`, two rate limiters (`REPORT_LIMIT` 3 a minute per address, `REPORT_LIMIT_ALL` 20 a minute in total), and the vars `REPORT_REPO` and `REPORT_LABELS`. - website: off the site's own host (a PR preview), the inbox validates the report and answers `{"id": "dry-run"}` without filing it. Without the secret `REPORT_FORGEJO_TOKEN`, or with `REPORTS_OFF=1`, it answers `503`, which the launcher already treats as "not sent, copy it instead". - docs: `website/README.md`, "The report inbox" (how it behaves, the limits, setup, turning it off, the token and previews); the header of `.forgejo/workflows/website.yml` names the new secret and why previews must stay reviewed. - tests: `website/tests/report.test.ts` (9 tests, a stand-in Forgejo and rate limiter, no network). ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | `REPORT_FORGEJO_TOKEN` (Worker secret) | The bot's token (scope write:issue); without it the inbox is off (503) | unset | set it after merge (README steps) | all | - | - | | `REPORTS_OFF` (Worker secret) | `1` turns the inbox off at once, no deploy | unset | for spam or an outage | all | - | - | | `REPORT_REPO`, `REPORT_LABELS` (vars) | Where reports go, and their labels | `MagicBeanDev/Longshot`, `kind/bug,source/report` | as is | all | - | - | ## Measurements No performance change: a new route on the Worker. `/download` and the site are untouched (still `302`/`200` under `wrangler dev`). ## Testing **Tested:** On Linux (Node 24, wrangler 4.160.1, workerd), on `feat/report-inbox`: - `node --test website/tests/*.test.ts`: the 9 new tests and `download.test.ts` pass. The 7 failures in `graphics-settings.test.ts` fail the same way on `main` (the README names no condition for the HDR tone mapper). - `npx wrangler deploy --dry-run`: the config is valid, and the bindings show both rate limiters and the vars. - `npx wrangler dev`, real requests: a GET gets `405`, an unknown kind `400`, a report with no token `503`, the 4th request in a minute from one address `429`, and `/download/launcher/nightly` is still a `302`. - **Against the real git.magicbean.dev** (`file()` from `report.ts`, filing on my fork with issues turned on for the test): the first report opened an issue with the label and `frame.png` attached and shown in it. The same crash again was a comment on that issue, and another signature opened a second issue. The `@` mention showed broken. I deleted the test issues and the label afterwards, and turned issues off again. - `python -I -B tests/run_all.py`: 510 passed, 18 skipped, 1 failed. `test_lift_cache.py` needs numpy, which this PC's isolated Python lacks; it fails the same way on `main`. **Not tested:** the deployed Worker. The secret isn't set until after merge, and the rate limiters only run for real on Cloudflare (they are also counted locally under `wrangler dev`, as above). Not run with the launcher itself: #42's `DefaultInbox` is still unset (see the launcher PR). **Needs other testers:** - A maintainer with the Cloudflare account: after merge, the setup in the README. Then `LONGSHOT_REPORT_URL=https://longshot.magicbean.dev/report` with a launcher from #42, and a Send report, expecting an issue `[report …]` on Longshot (request to open) ## Screenshots Nothing on screen (the issues it files are plain Forgejo issues). ## Risk and rollback Spam, or a flood of issues. Each address is limited to 3 a minute and everyone together to 20; one problem is one issue; and `REPORTS_OFF=1` (a secret, no deploy needed) turns it off. A leaked token can only write issues; revoke it in the bot's settings. Previews share the Worker's secrets, so the existing preview review is what protects the token. Reverting the commit removes the route. ## Reviewer notes Read `worker/report.ts` first. Setup left to a maintainer: the bot account (Triage role on Longshot, or Forgejo files the issues without labels), its token as a Worker secret, and the label `source/report`. ## Issues See LongshotLauncher [#42](https://git.magicbean.dev/MagicBeanDev/LongshotLauncher/pulls/42). ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: no performance change (named under Measurements). - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.
feat(website): a report inbox for the launcher, filing reports as issues
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 21s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
be26e892a8
POST /report on longshot.magicbean.dev takes the Longshot Launcher's problem
report (docs/launcher-reports.md in the launcher) and files it on Longshot as
the bot account whose token is the Worker secret REPORT_FORGEJO_TOKEN: one
issue per problem, by a key from the report's kind and signature in the
title, so the same crash on another PC is a comment on the open issue.

Checked and bounded (6 MB, a PNG, known kinds, no @mentions), rate-limited
per address and in total, a dry run on previews, and 503 until the secret
is set. website/README.md, "The report inbox", has the setup.

Assisted-by: Claude Opus 5.5
docs(website): the launcher's log tail limit is CrashCheck.LogTailBytes now
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
8c84682f12
Assisted-by: Claude Opus 5.5
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
This pull request is marked as a work in progress.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/report-inbox:TrashPanda-feat/report-inbox'
git switch 'TrashPanda-feat/report-inbox'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!251
No description provided.