From db1b7cf555cb0167d2823703001a2897612cfef2 Mon Sep 17 00:00:00 2001 From: Geoff Whatley Date: Thu, 16 Jul 2026 20:43:09 +1000 Subject: [PATCH 1/3] chore(#381): route bugsink alerts through resend smtp --- apps/bugsink/README.md | 8 ++++++++ apps/bugsink/fly.toml | 4 ++++ docs/architecture/deployment.md | 19 ++++++++++--------- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/bugsink/README.md b/apps/bugsink/README.md index 34eb07925..9acc963d6 100644 --- a/apps/bugsink/README.md +++ b/apps/bugsink/README.md @@ -30,6 +30,7 @@ move across with `bugsink-manage migrate_to_current_objectstorage`. | `SECRET_KEY` | `openssl rand -base64 50` | | `DATABASE_URL` | the Neon `bugsink` database, pooled connection URL | | `CREATE_SUPERUSER` | `email:password` — first boot only, unset after | +| `EMAIL_HOST_PASSWORD` | the Resend API key (`resend` item, `vers` 1Password vault) | | `R2_ENDPOINT_URL` | `https://.r2.cloudflarestorage.com` | | `R2_BUCKET` | `vers-bugsink-files` | | `R2_ACCESS_KEY_ID` | R2 S3 access key id | @@ -38,6 +39,13 @@ move across with `bugsink-manage migrate_to_current_objectstorage`. The R2 credentials also live on the `bugsink-r2` item in the `vers` 1Password vault. Unset the four `R2_*` secrets and Bugsink falls back to storing files in the database. +## Alerts + +New-issue, regression, and unmute alerts email the superuser account through Resend SMTP: the +non-secret SMTP settings (`EMAIL_HOST`, `EMAIL_HOST_USER`, `DEFAULT_FROM_EMAIL`) live in `fly.toml`, +and `EMAIL_HOST_PASSWORD` carries the API key. Per-project alert toggles live in each Bugsink +project's settings. + ## Housekeeping The `.github/workflows/bugsink-vacuum.yml` workflow runs `bugsink-manage vacuum_files` monthly (and diff --git a/apps/bugsink/fly.toml b/apps/bugsink/fly.toml index 43891977b..e17007e45 100644 --- a/apps/bugsink/fly.toml +++ b/apps/bugsink/fly.toml @@ -21,6 +21,10 @@ processes = ['app'] BASE_URL = 'https://vers-bugsink.fly.dev' BEHIND_HTTPS_PROXY = 'True' SINGLE_USER = 'True' +# Alert email goes out through Resend SMTP; EMAIL_HOST_PASSWORD (the API key) is a secret. +EMAIL_HOST = 'smtp.resend.com' +EMAIL_HOST_USER = 'resend' +DEFAULT_FROM_EMAIL = 'Bugsink ' [[vm]] memory = '512mb' diff --git a/docs/architecture/deployment.md b/docs/architecture/deployment.md index 0cbd14284..bd53fc20f 100644 --- a/docs/architecture/deployment.md +++ b/docs/architecture/deployment.md @@ -28,14 +28,14 @@ and mesh traffic is already encrypted, so services set `force_https = false`. Non-sensitive config (service URLs, `NODE_ENV`, log level) lives in each `fly.toml` or Dockerfile. Secrets are set with `fly secrets set` and never committed. -| App | Secrets | -| ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | -| `service-activity`, `service-avatar`, `service-user`, `service-verification` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY` | -| `service-session` | the above + `API_IDENTIFIER`, `JWT_SIGNING_PRIVKEY` | -| `service-replay` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY`, `SERVICE_AUTH_PRIVATE_KEY` | -| `service-keys` | `ROLL_KEY_ROOTS`, `SERVICE_AUTH_PUBLIC_KEY` | -| `app-web` | `SESSION_SECRET`, `COOKIE_DOMAIN`, `SERVICE_AUTH_PRIVATE_KEY` | -| `vers-bugsink` | `SECRET_KEY`, `DATABASE_URL`, `CREATE_SUPERUSER` (first boot) | +| App | Secrets | +| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| `service-activity`, `service-avatar`, `service-user`, `service-verification` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY` | +| `service-session` | the above + `API_IDENTIFIER`, `JWT_SIGNING_PRIVKEY` | +| `service-replay` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY`, `SERVICE_AUTH_PRIVATE_KEY` | +| `service-keys` | `ROLL_KEY_ROOTS`, `SERVICE_AUTH_PUBLIC_KEY` | +| `app-web` | `SESSION_SECRET`, `COOKIE_DOMAIN`, `SERVICE_AUTH_PRIVATE_KEY` | +| `vers-bugsink` | `SECRET_KEY`, `DATABASE_URL`, `EMAIL_HOST_PASSWORD`, `CREATE_SUPERUSER` (first boot) | - `SERVICE_AUTH_PUBLIC_KEY` — Ed25519 SPKI public key a service verifies inbound calls with. - `SERVICE_AUTH_PRIVATE_KEY` — its PKCS8 private half, held by the callers that sign outbound s2s @@ -283,7 +283,8 @@ op item create --vault vers --category login --title bugsink \ fly secrets set -a vers-bugsink \ SECRET_KEY="$(openssl rand -base64 50)" \ DATABASE_URL="" \ - CREATE_SUPERUSER="me@$DOMAIN:$BUGSINK_ADMIN_PASSWORD" + CREATE_SUPERUSER="me@$DOMAIN:$BUGSINK_ADMIN_PASSWORD" \ + EMAIL_HOST_PASSWORD="" fly deploy --config apps/bugsink/fly.toml --ha=false fly secrets unset CREATE_SUPERUSER -a vers-bugsink From fe581c29419a6fbf798048008ca36714ec20fd56 Mon Sep 17 00:00:00 2001 From: Geoff Whatley Date: Thu, 16 Jul 2026 22:52:23 +1000 Subject: [PATCH 2/3] docs(#381): document discord webhook alert delivery --- apps/bugsink/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/bugsink/README.md b/apps/bugsink/README.md index 9acc963d6..f77671ddd 100644 --- a/apps/bugsink/README.md +++ b/apps/bugsink/README.md @@ -41,7 +41,10 @@ The R2 credentials also live on the `bugsink-r2` item in the `vers` 1Password va ## Alerts -New-issue, regression, and unmute alerts email the superuser account through Resend SMTP: the +New-issue, regression, and unmute alerts deliver two ways. Every project carries a Discord +messaging-service config pointing at the alarms channel's incoming webhook (the +`bugsink-discord-webhook` item in the `vers` 1Password vault); a new project gets the same webhook +added in its settings. Alert email to the superuser account goes out through Resend SMTP: the non-secret SMTP settings (`EMAIL_HOST`, `EMAIL_HOST_USER`, `DEFAULT_FROM_EMAIL`) live in `fly.toml`, and `EMAIL_HOST_PASSWORD` carries the API key. Per-project alert toggles live in each Bugsink project's settings. From 2386a74dfcb385b86d3036bc3ec3055482856e69 Mon Sep 17 00:00:00 2001 From: Geoff Whatley Date: Fri, 17 Jul 2026 00:19:45 +1000 Subject: [PATCH 3/3] docs(#381): drop email alerting, document discord-only delivery --- apps/bugsink/README.md | 13 +++++-------- apps/bugsink/fly.toml | 4 ---- docs/architecture/deployment.md | 23 ++++++++++++----------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/apps/bugsink/README.md b/apps/bugsink/README.md index f77671ddd..9d902e665 100644 --- a/apps/bugsink/README.md +++ b/apps/bugsink/README.md @@ -30,7 +30,6 @@ move across with `bugsink-manage migrate_to_current_objectstorage`. | `SECRET_KEY` | `openssl rand -base64 50` | | `DATABASE_URL` | the Neon `bugsink` database, pooled connection URL | | `CREATE_SUPERUSER` | `email:password` — first boot only, unset after | -| `EMAIL_HOST_PASSWORD` | the Resend API key (`resend` item, `vers` 1Password vault) | | `R2_ENDPOINT_URL` | `https://.r2.cloudflarestorage.com` | | `R2_BUCKET` | `vers-bugsink-files` | | `R2_ACCESS_KEY_ID` | R2 S3 access key id | @@ -41,13 +40,11 @@ The R2 credentials also live on the `bugsink-r2` item in the `vers` 1Password va ## Alerts -New-issue, regression, and unmute alerts deliver two ways. Every project carries a Discord -messaging-service config pointing at the alarms channel's incoming webhook (the -`bugsink-discord-webhook` item in the `vers` 1Password vault); a new project gets the same webhook -added in its settings. Alert email to the superuser account goes out through Resend SMTP: the -non-secret SMTP settings (`EMAIL_HOST`, `EMAIL_HOST_USER`, `DEFAULT_FROM_EMAIL`) live in `fly.toml`, -and `EMAIL_HOST_PASSWORD` carries the API key. Per-project alert toggles live in each Bugsink -project's settings. +New-issue, regression, and unmute alerts post to the alarms Discord channel: every project carries a +Discord messaging-service config pointing at the channel's incoming webhook (the +`bugsink-discord-webhook` item in the `vers` 1Password vault), and a new project gets the same +webhook added in its settings. No outgoing email is configured — alerting is webhook-only, and +per-project alert toggles live in each Bugsink project's settings. ## Housekeeping diff --git a/apps/bugsink/fly.toml b/apps/bugsink/fly.toml index e17007e45..43891977b 100644 --- a/apps/bugsink/fly.toml +++ b/apps/bugsink/fly.toml @@ -21,10 +21,6 @@ processes = ['app'] BASE_URL = 'https://vers-bugsink.fly.dev' BEHIND_HTTPS_PROXY = 'True' SINGLE_USER = 'True' -# Alert email goes out through Resend SMTP; EMAIL_HOST_PASSWORD (the API key) is a secret. -EMAIL_HOST = 'smtp.resend.com' -EMAIL_HOST_USER = 'resend' -DEFAULT_FROM_EMAIL = 'Bugsink ' [[vm]] memory = '512mb' diff --git a/docs/architecture/deployment.md b/docs/architecture/deployment.md index bd53fc20f..6b323149b 100644 --- a/docs/architecture/deployment.md +++ b/docs/architecture/deployment.md @@ -28,14 +28,14 @@ and mesh traffic is already encrypted, so services set `force_https = false`. Non-sensitive config (service URLs, `NODE_ENV`, log level) lives in each `fly.toml` or Dockerfile. Secrets are set with `fly secrets set` and never committed. -| App | Secrets | -| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| `service-activity`, `service-avatar`, `service-user`, `service-verification` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY` | -| `service-session` | the above + `API_IDENTIFIER`, `JWT_SIGNING_PRIVKEY` | -| `service-replay` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY`, `SERVICE_AUTH_PRIVATE_KEY` | -| `service-keys` | `ROLL_KEY_ROOTS`, `SERVICE_AUTH_PUBLIC_KEY` | -| `app-web` | `SESSION_SECRET`, `COOKIE_DOMAIN`, `SERVICE_AUTH_PRIVATE_KEY` | -| `vers-bugsink` | `SECRET_KEY`, `DATABASE_URL`, `EMAIL_HOST_PASSWORD`, `CREATE_SUPERUSER` (first boot) | +| App | Secrets | +| ---------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `service-activity`, `service-avatar`, `service-user`, `service-verification` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY` | +| `service-session` | the above + `API_IDENTIFIER`, `JWT_SIGNING_PRIVKEY` | +| `service-replay` | `DATABASE_URL`, `SERVICE_AUTH_PUBLIC_KEY`, `SERVICE_AUTH_PRIVATE_KEY` | +| `service-keys` | `ROLL_KEY_ROOTS`, `SERVICE_AUTH_PUBLIC_KEY` | +| `app-web` | `SESSION_SECRET`, `COOKIE_DOMAIN`, `SERVICE_AUTH_PRIVATE_KEY` | +| `vers-bugsink` | `SECRET_KEY`, `DATABASE_URL`, `CREATE_SUPERUSER` (first boot) | - `SERVICE_AUTH_PUBLIC_KEY` — Ed25519 SPKI public key a service verifies inbound calls with. - `SERVICE_AUTH_PRIVATE_KEY` — its PKCS8 private half, held by the callers that sign outbound s2s @@ -283,8 +283,7 @@ op item create --vault vers --category login --title bugsink \ fly secrets set -a vers-bugsink \ SECRET_KEY="$(openssl rand -base64 50)" \ DATABASE_URL="" \ - CREATE_SUPERUSER="me@$DOMAIN:$BUGSINK_ADMIN_PASSWORD" \ - EMAIL_HOST_PASSWORD="" + CREATE_SUPERUSER="me@$DOMAIN:$BUGSINK_ADMIN_PASSWORD" fly deploy --config apps/bugsink/fly.toml --ha=false fly secrets unset CREATE_SUPERUSER -a vers-bugsink @@ -292,7 +291,9 @@ fly secrets unset CREATE_SUPERUSER -a vers-bugsink In the Bugsink UI, create one project per app, set each project's DSN as that app's `SENTRY_DSN` secret, and set the web project's DSN as the `VITE_SENTRY_DSN` GitHub Actions variable plus a -`vers-app-web` secret of the same name. Mint an API token for CI source-map uploads +`vers-app-web` secret of the same name. Add the alarms Discord webhook (the +`bugsink-discord-webhook` item in the `vers` 1Password vault) as each project's messaging service, +so new-issue alerts reach the alarms channel. Mint an API token for CI source-map uploads (`SENTRY_AUTH_TOKEN` GitHub secret) and one for the MCP server, added to the vault item as `mcp-token`.