Aller au contenu

Support relay

The Support relay lets people without a GitHub account report a bug from their robot. The owner's browser sends the redacted Diagnostic report to the relay, and the relay creates the GitHub issue as a GitHub App. The robot never calls it (see ADR 0009).

It is a Cloudflare Worker whose code lives in relay/. The Support relay workflow tests it and deploys it whenever relay/ changes on main, or when it is run by hand from the Actions tab.

API

Request Answer
GET /status {"available": true}, or false while the kill switch is on.
POST /report with {"description": "...", "report": "...", "contact": "...", "followOnDiscord": false} 201 {"number": 12, "url": "https://github.com/…/issues/12"}, plus "thread": "https://discord.com/channels/…" when a Discord thread was opened
POST /discord/interactions Discord's interactions endpoint (signed with Ed25519): the /issue command.

report must be a redacted Diagnostic report: it starts with CatWandConnect diagnostic report and Redacted: yes, and is at most 14 KB. description is required (2,000 characters at most) and contact is optional (100 characters). The request body is limited to 20 KB.

Errors are returned as {"error": "<code>"}:

Status Code Meaning
400 invalid_report, invalid_description, invalid_contact The request does not have the expected format.
403 origin_not_allowed The page is not a Device interface.
413 too_large The body is over 20 KB.
429 rate_limited, daily_cap Too many reports from this address, or for the day.
503 unavailable The kill switch is on.
502 github_unavailable GitHub refused or did not answer.

On any error the Device interface falls back to the prefilled GitHub issue.

CORS. Only pages served over http:// from a .local name, localhost or a private IPv4 address (10.x, 172.16–31.x, 192.168.x, which covers the hotspot 192.168.4.1) get an Access-Control-Allow-Origin header.

Issues. Each issue is titled [device] <first line of the description> and labelled from-device and needs-triage. It contains the description, firmware, chip, the robot's random Device ID (never its MAC address), the optional contact, and the full report in a code block. @ mentions and fixes #N keywords in user text are neutralised.

Abuse limits. There is no captcha, because Cloudflare Turnstile cannot run on a .local page. Instead the relay allows 3 reports per minute per IP address, a global cap of DAILY_CAP issues per UTC day (counted on GitHub with the from-device label, 30 by default), and the format and size checks above.

Discord

The relay uses its own bot, CatWandConnect Support. It is not the admin bot. Its only permissions are View Channels, Send Messages, Create Public Threads, Send Messages in Threads, Read Message History, Embed Links and Attach Files.

  • Follow on Discord (followOnDiscord: true). After creating the issue, the relay opens a #support forum post tagged bug that links to the issue, and adds the post's link to the issue body.
  • If the contact is exactly a member's Discord username, the post mentions that member. This needs the Server Members intent; without it, the name stays plain text.
  • Posts allow no other mentions.
  • If Discord fails, the issue is still created.
  • /issue. In a #support post, a member with Manage Threads runs /issue. The relay creates a needs-triage issue with the post's title and first message, any catwandconnect-report*.txt Diagnostic report attached to that message, and the post link, then answers in the post with the issue link. Running it again on the same post links the existing issue instead of creating a duplicate. The relay finds that issue from its own earlier answer in the post first, then through GitHub search, because search takes a while to index a new issue.
  • Release notifications. After each release, the notify-discord job of the Release workflow runs relay/scripts/notify-release.mjs.
  • It reads the squash-merge titles since the previous tag (for a stable release, since the previous stable one), the issues and PRs they reference, and the issues those PRs close.
  • It posts the version and how to update in every Discord post linked from those closed issues, in their body or comments.
  • A hidden [release vX.Y.Z] marker line prevents posting twice.
  • It never fails the release.

The Support relay workflow registers /issue on the server at every deploy.

Setup (maintainer, once)

The agent never sees the secrets. They are pasted only into the repository's Actions secrets.

  1. Cloudflare.
  2. In Workers & Pages, pick a workers.dev subdomain if the account has none. The relay will be https://catwandconnect-support.<subdomain>.workers.dev.
  3. Copy the Account ID from the account home page.
  4. Under My Profile → API Tokens → Create Token, use the Edit Cloudflare Workers template, limited to this account.
  5. GitHub App. Under Settings → Developer settings → GitHub Apps → New GitHub App:
  6. Name it CatWandConnect Support, with the repository URL as its homepage.
  7. Untick Webhook → Active.
  8. Set Repository permissions → Issues: Read and write.
  9. Choose "Only on this account", then create it.
  10. Note the App ID, then Generate a private key: a .pem file downloads.
  11. Install App on vistorx/CatWandConnect only.
  12. Secrets. Under Settings → Secrets and variables → Actions → New repository secret, add:
  13. CLOUDFLARE_ACCOUNT_ID
  14. CLOUDFLARE_API_TOKEN
  15. SUPPORT_APP_ID
  16. SUPPORT_APP_PRIVATE_KEY: the whole .pem file, including its BEGIN and END lines.

Then delete the downloaded .pem. A new one can be generated at any time. 4. Discord bot. In the Developer Portal, create the application CatWandConnect Support. - On the Bot tab, untick Public Bot, then Reset Token. - Add the secrets DISCORD_BOT_TOKEN, DISCORD_APPLICATION_ID and DISCORD_PUBLIC_KEY (the Public Key comes from General Information). - Invite the bot with https://discord.com/oauth2/authorize?client_id=<application id>&scope=bot%20applications.commands&permissions=309237763072. - After the next deploy, set Interactions Endpoint URL (General Information) to <relay URL>/discord/interactions. Discord checks it when you save. 5. Deploy. Run the Support relay workflow (Actions tab → Run workflow). Its log ends with the relay's URL and {"available":true}. 6. Wire it. Put the relay URL in firmware/web/links.js (relay). The Help page then checks GET /status and offers "Send to support" next to the prefilled issue. With relay empty, the relay unreachable or the kill switch on, the Help page offers only the prefilled issue.

Operating it

  • Kill switch. In the Cloudflare dashboard, open Workers → catwandconnect-support → Settings → Variables and set ENABLED to false. This takes effect immediately. Then set it in relay/wrangler.toml too, so the next deploy keeps it. Robots fall back to the prefilled issue.
  • Daily cap. Change DAILY_CAP the same way.
  • Logs. Cloudflare dashboard → the Worker → Observability. The relay logs only GitHub errors, never reports.
  • Rotating the GitHub key. Generate a new private key on the App, replace SUPPORT_APP_PRIVATE_KEY, run the workflow, then delete the old key on the App.
  • Tests. Run cd relay && npm test. They also run in CI with the Robot core tests.