Skip to content

Modernize: Slack signing-secret auth, fetch, containerization + GHCR/Coolify deploy - #17

Open
ddbruce wants to merge 1 commit into
masterfrom
modernization
Open

Modernize: Slack signing-secret auth, fetch, containerization + GHCR/Coolify deploy#17
ddbruce wants to merge 1 commit into
masterfrom
modernization

Conversation

@ddbruce

@ddbruce ddbruce commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

Behavior-preserving modernization of whoson. Same /whoson command args (today/week/yesterday/tomorrow/weekdays), same responses. Verified with 10 unit tests + 6 end-to-end checks (valid Slack signatures pass; unsigned/tampered rejected; upstream fetch + token passthrough work); amd64 image builds with 0 npm vulnerabilities.

⚠️ Required deploy change (auth)

The old code compared the deprecated Slack verification token (SLACK_SIGNING_TOKEN). This PR switches to proper signing-secret HMAC verification, which needs a new env var:

  • Remove SLACK_SIGNING_TOKEN
  • Add SLACK_SIGNING_SECRET — from the Slack app → Basic Information → App Credentials → Signing Secret

The Slack app itself does not need re-creating. Also note BASE_URL is now documented in .env.example (the code always required it; it was undocumented).

Changes

  • Auth: deprecated verification token → signing-secret HMAC over the raw body, with a 5-minute timestamp replay guard. Fixes the missing-return bug that let unauthenticated requests fall through.
  • HTTP client: dropped cachios (it pulled axios 0.19 w/ CVEs and never actually cached — no per-request ttl was ever passed) for native fetch with a 10s timeout + error handling. JSON vs text passthrough preserved to match the old axios behavior.
  • Deps/runtime: Express 4.17 → 5.x, dropped standalone body-parser for built-ins, dotenv 8 → 17; Dockerfile node:latest → pinned node:24-alpine, npm ci, non-root, HEALTHCHECK + GET /health.
  • Logic: extracted the weekday-resolution switch verbatim into a pure resolveWhosonQuery(text, now) helper so it's unit-testable; behavior identical.
  • Tooling/CI: ESLint 8 (EOL) → ESLint 9 flat config; added Jest tests; replaced dead Drone CI with GitHub Actions (lint + test + amd64 build).
  • Deploy: scripts/deploy.sh + config.example.sh (build amd64 → push GHCR → Coolify POST /api/v1/deploy?uuid=); secrets in gitignored scripts/config.sh.

Notes / non-changes

  • The upstream schedule endpoint still receives its token as ?token= — that's the upstream's contract, unchanged.

Behavior-preserving modernization. Same /whoson command args, same responses.

Security / correctness:
- Replace the deprecated Slack verification-token check with signing-secret
  HMAC verification (X-Slack-Signature over the raw body + timestamp replay
  guard). Requires SLACK_SIGNING_SECRET (was SLACK_SIGNING_TOKEN).
- Fix missing-return auth bug: unauthenticated requests now stop with 401.
- Add try/catch + a hard fetch timeout around the upstream call; user gets a
  friendly message instead of a hung/500 request.

Dependencies / runtime:
- Drop cachios (pulled axios 0.19, CVEs) — it never actually cached here (no
  per-request ttl was ever passed) so native fetch is a drop-in.
- Drop standalone body-parser for built-in express.json()/urlencoded().
- Express 4.17 -> 5.x, dotenv 8 -> 17.
- Dockerfile: node:latest -> node:24-alpine, npm ci, non-root, HEALTHCHECK,
  add GET /health.

Tooling / delivery:
- ESLint 8 (EOL) -> ESLint 9 flat config; extract the weekday resolver into a
  pure, tested helper (resolveWhosonQuery); add Jest tests (10).
- Replace dead Drone CI with GitHub Actions (lint + test + amd64 build).
- Add scripts/deploy.sh + config.example.sh (build amd64, push GHCR, trigger
  Coolify redeploy); real secrets live in gitignored scripts/config.sh.

Verified: 10 unit tests + 6 end-to-end checks (valid signatures pass,
unsigned/tampered rejected, upstream wiring) pass; amd64 image builds clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant