77 lines
4.0 KiB
Markdown
77 lines
4.0 KiB
Markdown
# REPORT — mopac-discourse-go: Redmine 507 surface complete
|
|
|
|
Task: TASK-20260829-1700 — Discourse Go client (Redmine #507)
|
|
Delivered: 2026-08-29 17:00 turn
|
|
Repo: https://git.knownelement.com/UKRRS/mopac-discourse-go (existing,
|
|
AGPLv3 since first commit)
|
|
Clone: ~/projects/meta/MOPAC/discourse-go (origin main, 9 commits, pushed)
|
|
Status: **Redmine 507 scope complete, green, pushed. Feeds the 495
|
|
briefing delivery + fleet verification.**
|
|
|
|
No Redmine writes in this turn (PMO owns the ticket, per directive).
|
|
|
|
## Starting point
|
|
|
|
The repo already carried a clean v0 (categories list/create, topics
|
|
create/list/latest/get, posts create/update/get, whoami, raw JSON
|
|
passthrough, typed error classes, fake-server tests, container smoke) —
|
|
committed and pushed earlier today. This turn built on it rather than
|
|
resetting: v0 was green, standard-shaped and already on origin main.
|
|
|
|
## What landed this turn (8 commits, TDD red/green each)
|
|
|
|
| Commit | Content |
|
|
|---|---|
|
|
| `f51ca59` | style: gofmt tag alignment the v0 files missed |
|
|
| `14cdaa0` | feat: search — `GET /search.json` grouped hits (posts/topics/users/categories), term operators pass through |
|
|
| `6891f3f` | feat: private-message send — `POST /posts.json` with comma-joined `target_usernames`/`target_group_names`/`target_emails` |
|
|
| `5bbb307` | feat: webhook + SSO helpers — constant-time HMAC verification, `WebhookHandler`, `VerifySSO`/`BuildSSOResponse`/`SSOLogin`, env-only secret loaders |
|
|
| `5b95ac3` | feat: `DISCOURSE_KEY` env alias (explicit `DISCOURSE_API_KEY` wins) |
|
|
| `f133027` | feat(cli): `search`, `messages send`, `webhook verify`, `sso verify` |
|
|
| `445df7a` | test: smoke coverage for all of the above (real CLI vs containerized fake) |
|
|
| `bb96849` | docs: README + env.example to standard |
|
|
|
|
## Surface (Redmine 507 checklist)
|
|
|
|
- **category/topic/post list+create** — v0, unchanged, green.
|
|
- **search** — `Client.Search(term, page)` + `discourse-go search TERM
|
|
[-page N]`; url-encodes the term (no query smuggling), decodes the
|
|
four hit groups; typed errors preserved.
|
|
- **private-message send** — `Client.SendMessage(title, raw, targets)` +
|
|
`discourse-go messages send -title T -to u1,u2 [-group] [-email]`;
|
|
targets comma-joined on the wire exactly as Discourse expects;
|
|
undeliverable recipients surface as `ErrForbidden`.
|
|
- **webhook-helper** — `VerifyWebhook` (sha256-HMAC hex, `sha256=`
|
|
prefix tolerated, constant-time), `ParseWebhook`/`WebhookHandler`
|
|
(403 before dispatch), `webhook verify SIG` CLI (body on stdin, exit
|
|
0/2). Secret: `DISCOURSE_WEBHOOK_SECRET`.
|
|
- **SSO secret handling** — `VerifySSO` (HMAC over the base64 string),
|
|
`BuildSSOResponse`, `SSORedirectURL`, one-call `SSOLogin`, plus
|
|
`sso verify SSO SIG` CLI that prints the decoded payload. Secret:
|
|
`DISCOURSE_SSO_SECRET`.
|
|
- **env config** — `DISCOURSE_URL` / `DISCOURSE_API_KEY` (alias
|
|
`DISCOURSE_KEY`) / `DISCOURSE_API_USERNAME`; secrets env-only, never
|
|
flags, never logged (redaction sweep in the smoke greps all output).
|
|
|
|
## Verification
|
|
|
|
- `./dev.sh check` (build + vet + test inside the digest-pinned
|
|
`golang:1.26-bookworm` Docker builder): **green** — 24 library tests
|
|
across search/messages/webhook/SSO/env-alias plus the v0 suite.
|
|
- `./dev.sh smoke` (real CLI binary vs fake Discourse in a container on
|
|
127.0.0.1:8610): **smoke: OK** — new flows cover search hits, PM send
|
|
with recipient assertion, webhook good-sig=0/bad-sig=2, SSO
|
|
decode + tamper rejection; no live forum contacted.
|
|
- gofmt clean; working tree clean; `origin/main` at `bb96849`.
|
|
|
|
## Notes for the fleet
|
|
|
|
- `WebhookHandler`/`SSOLogin` are importable by the fleet's receiver
|
|
service — no shell-out, no third-party modules, stdlib crypto only.
|
|
- Fake server records PM deliveries (`/__pms.json` on the smoke fake)
|
|
so downstream delivery assertions can be scripted.
|
|
- Live-verification status unchanged from v0: reads OK against
|
|
community.turnsys.com with the current key; category create waits on
|
|
the admin-scoped key (Charles's 1900 list); everything new this turn
|
|
is exercised against the fake only, per no-live-writes discipline.
|