docs(09.1-01): complete ntfy backend fix plan — pulse-me- prefix, company server, bearer auth
- SUMMARY documents gap closure mapping each 09-HUMAN-UAT.md missing item to file/line - All 4 backend ntfy publish sites updated; ProfileChannelsSection QR + help line updated - tsc --noEmit + vitest notify.test.ts both pass
This commit is contained in:
parent
985728abb6
commit
c4fb6e22a4
1 changed files with 139 additions and 0 deletions
139
.planning/phases/09.1-ntfy-backend-fix/09.1-01-SUMMARY.md
Normal file
139
.planning/phases/09.1-ntfy-backend-fix/09.1-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
phase: 09.1-ntfy-backend-fix
|
||||
plan: "01"
|
||||
subsystem: notifications
|
||||
tags: [ntfy, personal-channels, bearer-auth, mobile-profile, gap-closure]
|
||||
|
||||
requires:
|
||||
- phase: 09-user-profile-preferences-new
|
||||
provides: "personal-channels.ts, notify.ts route_to_user path, ProfileChannelsSection.tsx, notification_channels.owner_user_id"
|
||||
|
||||
provides:
|
||||
- "pulse-me- topic prefix enforced by regex and mint function"
|
||||
- "All four ntfy publish sites route personal channels to company ntfy server with bearer auth"
|
||||
- "Global/admin ntfy rows continue using their config-driven server + auth_token"
|
||||
- "ProfileChannelsSection QR + subscribe link target NEXT_PUBLIC_NTFY_BASE_URL"
|
||||
- "Custom-topic help line in ProfileChannelsSection advanced disclosure"
|
||||
|
||||
affects:
|
||||
- "Any plan adding new ntfy publish sites (must apply same personal/global branch)"
|
||||
- "Any plan touching ProfileChannelsSection advanced section"
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Personal-vs-global ntfy branch on owner_user_id: isPersonal = !!channel.owner_user_id; personal -> NTFY_BASE_URL + NTFY_PULSE_TOKEN; global -> channel.config.server_url + channel.config.auth_token"
|
||||
- "NEXT_PUBLIC_NTFY_BASE_URL module-level constant in ProfileChannelsSection with fallback to https://ntfy.wulfconsulting.cloud"
|
||||
|
||||
key-files:
|
||||
modified:
|
||||
- lib/services/personal-channels.ts
|
||||
- lib/services/pipeline-steps/notify.ts
|
||||
- lib/services/pipeline-steps/approval.ts
|
||||
- lib/services/ticket-digest-service.ts
|
||||
- components/mobile/profile/ProfileChannelsSection.tsx
|
||||
|
||||
key-decisions:
|
||||
- "pulse-me- prefix (not pulse-) to avoid collision with noc-* and soc-* reserved namespaces on company ntfy instance"
|
||||
- "approval.ts uses soft fallback when NTFY_PULSE_TOKEN missing (send unauthenticated) to match its existing best-effort posture; notify.ts and personal-channels.ts fail loud"
|
||||
- "ticket-digest-service.ts local NotificationChannel interface NOT extended — owner_user_id read via type cast to avoid type churn in a file that is self-contained"
|
||||
- "Global/admin ntfy rows (owner_user_id IS NULL) preserved — existing ntfy.sh and custom self-hosted deployments keep working"
|
||||
|
||||
requirements-completed: [CHAN-03, CHAN-05, CHAN-07, ROUTE-04]
|
||||
|
||||
duration: 15min
|
||||
completed: "2026-05-11"
|
||||
---
|
||||
|
||||
# Phase 09.1 Plan 01: ntfy Backend Fix Summary
|
||||
|
||||
**Rerouted all four personal-channel ntfy publish sites from public ntfy.sh to the company private server (https://ntfy.wulfconsulting.cloud) with bearer auth, tightened topic prefix from `pulse-` to `pulse-me-`, and aligned the mobile profile QR/subscribe-link to the company server.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~15 min
|
||||
- **Started:** 2026-05-11T06:30:00Z
|
||||
- **Completed:** 2026-05-11T06:45:00Z
|
||||
- **Tasks:** 2
|
||||
- **Files modified:** 5
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Updated `NTFY_TOPIC_RE` from `/^[A-Za-z0-9_-]{6,64}$/` to `/^pulse-me-[A-Za-z0-9-]{6,64}$/` — rejects `pulse-`, `noc-`, `soc-`, and arbitrary names
|
||||
- Updated `mintNtfyTopic()` to return `pulse-me-XXXXXXXX` (8 hex chars entropy, same as before)
|
||||
- Rewrote `sendChannelTest` ntfy case: drops `channel.config.server_url` / `channel.config.auth_token`; always uses `NTFY_BASE_URL` + `NTFY_PULSE_TOKEN` Bearer header; fails loud when token missing
|
||||
- Rewrote `sendNtfy` in `notify.ts`: personal branch (`owner_user_id` set) → company server + NTFY_PULSE_TOKEN; global branch → existing config-driven behavior preserved
|
||||
- Updated `approval.ts` ntfy block: same personal/global split; soft fallback when token missing (matching the function's existing best-effort posture)
|
||||
- Updated `ticket-digest-service.ts` deliver() ntfy block: same split; both `getAvailableChannels` and `deliver` SELECTs now include `owner_user_id`
|
||||
- Added `NTFY_BASE` constant in `ProfileChannelsSection.tsx` using `NEXT_PUBLIC_NTFY_BASE_URL || 'https://ntfy.wulfconsulting.cloud'`
|
||||
- Updated subscribe link href + text + QRCodeSVG value to use `NTFY_BASE` — `ntfy.sh` no longer appears in the file
|
||||
- Added muted help line `Topic must start with <code>pulse-me-</code>` between custom-topic Input and the server error paragraph
|
||||
|
||||
## Task Commits
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
|------|------|--------|-------|
|
||||
| 1 | Backend: regex + prefix + bearer auth (4 files) | 2ff2dc9 | lib/services/personal-channels.ts, lib/services/pipeline-steps/notify.ts, lib/services/pipeline-steps/approval.ts, lib/services/ticket-digest-service.ts |
|
||||
| 2 | ProfileChannelsSection QR + subscribe link + help line | 985728a | components/mobile/profile/ProfileChannelsSection.tsx |
|
||||
|
||||
## Gap Closure
|
||||
|
||||
Maps each `missing` item from `09-HUMAN-UAT.md` to where it was addressed:
|
||||
|
||||
| Gap Item | File | Location |
|
||||
|----------|------|----------|
|
||||
| Env vars NTFY_BASE_URL / NEXT_PUBLIC_NTFY_BASE_URL | personal-channels.ts, notify.ts, approval.ts, ticket-digest-service.ts, ProfileChannelsSection.tsx | Read at runtime with fallback default; not written to .env |
|
||||
| mintNtfyTopic() must mint `pulse-me-XXXXXXXX` | lib/services/personal-channels.ts | Line 65: `return \`pulse-me-\${id}\`` |
|
||||
| NTFY_TOPIC_RE must enforce `^pulse-me-[A-Za-z0-9-]{6,64}$` | lib/services/personal-channels.ts | Lines 44-52: updated regex constant |
|
||||
| sendChannelTest must force NTFY_BASE_URL + NTFY_PULSE_TOKEN | lib/services/personal-channels.ts | Lines 103-130: ntfy case rewritten |
|
||||
| notify.ts ntfy publish: company server + bearer auth for personal | lib/services/pipeline-steps/notify.ts | Lines 365-395: personal/global branch in sendNtfy |
|
||||
| ticket-digest-service.ts ntfy publish: same pattern | lib/services/ticket-digest-service.ts | Lines 651-664: isPersonal branch + owner_user_id in SELECTs |
|
||||
| ProfileChannelsSection QR + subscribe link target NEXT_PUBLIC_NTFY_BASE_URL | components/mobile/profile/ProfileChannelsSection.tsx | Lines 35, 295, 302: NTFY_BASE constant + usage |
|
||||
| Inline error copy updated to 'Topic must start with pulse-me-' | components/mobile/profile/ProfileChannelsSection.tsx | Lines 321-323: muted help line added |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**[Rule 3 - Blocking] Restored lib/types/pipeline.ts and other files after git reset --soft**
|
||||
|
||||
- **Found during:** Pre-task setup — `git reset --soft` to the expected base commit left the working tree files at an older state, causing Phase 9 types (RouteToUser, owner_user_id, etc.) to be missing from pipeline.ts
|
||||
- **Fix:** `git checkout HEAD -- lib/types/pipeline.ts lib/auth.ts lib/bootstrap.ts lib/services/msgraph-client.ts package.json package-lock.json` to restore those files to the correct HEAD state (1ab3bfe)
|
||||
- **Files modified:** lib/types/pipeline.ts (restored), lib/auth.ts (restored), lib/bootstrap.ts (restored), others
|
||||
- **Impact:** No functional change to the 5 plan target files; purely a worktree hygiene issue
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None — all changes wire directly to environment variables already present in .env (NTFY_PULSE_TOKEN) or read at runtime with sensible defaults.
|
||||
|
||||
## Threat Surface
|
||||
|
||||
No new trust boundaries beyond those declared in the plan's threat model (T-09.1-01 through T-09.1-06). All mitigations applied:
|
||||
|
||||
- T-09.1-01: `pulse-me-` prefix keeps personal topics out of noc-*/soc-* namespace
|
||||
- T-09.1-02: Server-side regex gate enforced in isValidNtfyTopic; client help line is advisory
|
||||
- T-09.1-03: NTFY_PULSE_TOKEN only read in server-side modules; never exposed via NEXT_PUBLIC_
|
||||
- T-09.1-05: notify.ts + personal-channels.ts return structured error when token missing; approval.ts uses soft fallback
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Files exist:
|
||||
- lib/services/personal-channels.ts: FOUND
|
||||
- lib/services/pipeline-steps/notify.ts: FOUND
|
||||
- lib/services/pipeline-steps/approval.ts: FOUND
|
||||
- lib/services/ticket-digest-service.ts: FOUND
|
||||
- components/mobile/profile/ProfileChannelsSection.tsx: FOUND
|
||||
|
||||
Commits exist:
|
||||
- 2ff2dc9: FOUND (Task 1 — backend 4 files)
|
||||
- 985728a: FOUND (Task 2 — ProfileChannelsSection)
|
||||
|
||||
TypeScript: `npx tsc --noEmit --pretty` exit 0 — no errors.
|
||||
Vitest: `npx vitest run lib/services/pipeline-steps/notify.test.ts` — 1 passed.
|
||||
|
||||
Grep sanity:
|
||||
- grep "ntfy.sh" lib/services/personal-channels.ts → (none)
|
||||
- grep "pulse-me-" lib/services/personal-channels.ts → 3 matches (comment, regex, return)
|
||||
- grep "NEXT_PUBLIC_NTFY_BASE_URL" components/mobile/profile/ProfileChannelsSection.tsx → 1 match (line 35)
|
||||
- grep "ntfy.sh" components/mobile/profile/ProfileChannelsSection.tsx → (none)
|
||||
- grep "owner_user_id" notify.ts approval.ts ticket-digest-service.ts → multiple matches in each
|
||||
Loading…
Add table
Add a link
Reference in a new issue