Add BMAD planning artifacts: brief, PRD, architecture, UX, spec, tickets

Includes the BMAD install config, the initiative ticket tree, the portal
context-resources scripts, and the .gitignore change that tracks BMAD files
(generated _bmad/render/ is ignored).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sk72AxLwQnYttF6VFSxQLt
This commit is contained in:
Ebbe Baß
2026-10-15 13:33:02 +02:00
co-authored by Claude Sonnet 5.5
parent 97b2a95062
commit 24de5878ed
60 changed files with 13647 additions and 6 deletions
@@ -0,0 +1,34 @@
---
topic: ping-mee Self-Service Portal
updated: 2026-10-15T12:37
---
- (event) Create. Inputs (rich, structured, no elicitation): PRD+addendum, brief+addendum, ARCHITECTURE-SPINE, UX DESIGN.md+EXPERIENCE.md, context-resources scripts. Slug inherited from PRD: ping-mee-self-service-platform. Interactive, express.
- (decision) Companions: ADOPTED prd.md, prd addendum.md, ARCHITECTURE-SPINE.md, DESIGN.md, EXPERIENCE.md (FR-level consequences and AD rules are load-bearing and would bloat the kernel). Sources absorbed: brief.md, brief addendum.md. No spec-authored companions.
- (capability) CAP-1 sign-in via OIDC, roles from AD groups, server-side sessions (1h idle/4h absolute), disabled accounts cut off, sensitive-request throttling [PRD FR-1..FR-4]
- (capability) CAP-2 user changes own password (current+new), policy shown from AD, applied over LDAPS, other sessions ended, e-mail sent [FR-5..FR-7]
- (capability) CAP-3 user edits own profile fields; e-mail change needs password + confirmation to new address + notice and 24h undo link to old [FR-8..FR-11]
- (capability) CAP-4 profile picture upload (JPEG/PNG/HEIC/WebP <=10MB), crop, write <sam>.jpg to share, Pending until sync processed, remove [FR-12..FR-15]
- (capability) CAP-5 publishers write announcements (rich text stored as Markdown, severity always set, optional window/expiry/schedule), edit/withdraw/notify again; page + banners [FR-16..FR-19]
- (capability) CAP-6 announcement e-mail to all OU users with valid mail minus opt-outs via outbox; opt-in default; failures visible to Admin [FR-20, FR-21]
- (capability) CAP-7 admin manages the portal: accent colour with contrast warning, backgrounds, runtime settings, audit log viewer, status page [FR-22..FR-25, FR-32]
- (capability) CAP-8 every sensitive action leaves an audit entry, kept 12 months, never holding passwords/tokens/picture content [FR-28]
- (capability) CAP-9 dashboard, per-device preferences (theme/language/German form of address), German+English, responsive desktop+phone, WCAG 2.1 AA [FR-29..FR-31]
- (capability) CAP-10 deployable anywhere: one Docker container, config+secrets via env vars, nothing ping-mee-specific, consistent DB backups, health check [FR-26, FR-27]
- (constraint) Architecture: Django modular monolith with adapters, one owner per external system, 19 rules in ARCHITECTURE-SPINE (AD-1..AD-19) are binding.
- (constraint) Single container, one worker, SQLite WAL on a volume; portal writes its own consistent backup files because Veeam copies directories.
- (constraint) AD only via LDAPS with a service account limited to a configurable OU and fixed attribute allow-lists; no cn/name edits.
- (constraint) Existing PowerShell sync/logon scripts stay unchanged: portal only writes <sam>.jpg to source and reads <sam>.md5 from processed.
- (constraint) Internet-facing: no injection, no cross-user access, throttling before any AD call, passwords never stored or logged, strict CSP with no inline script or external assets.
- (constraint) Expired, forgotten or must-change passwords are never handled by the portal; the Admin resets them in AD.
- (decision) Companion precedence: behaviour = PRD; architecture = ARCHITECTURE-SPINE; look = DESIGN.md; UX behaviour = EXPERIENCE.md. Where PRD and UX docs differ, the 2026-10-15 update pass aligned them.
- (question) Open: exact retry count + IONOS limits; restore test; authentik single logout; rich-text editor library + asset build; image location; confirm accepted early-clear of Pending (PRD Q-9).
- (event) Self-validate pass 1 (coherence): rules 1-6,8 hold - each CAP has intent+success, 9 non-goals, constraints bend design, ids CAP-1..CAP-10 unique. Fixed: open questions copied stale PRD list -> PRD Q-1,2,4..9 marked resolved/accepted per spine AD-6/10/11/12/14/15, SPEC lists only truly open items.
- (event) Self-validate pass 2 (preservation): FR-1..FR-32 each covered by a CAP range; PRD addendum, spine, DESIGN, EXPERIENCE adopted as companions so FR consequences, AD rules and screen behaviour are preserved by reference; brief absorbed (all claims present in PRD). Wrapper-only dropped: PRD document-purpose, journeys narrative personas (kept in PRD/EXPERIENCE), success-metric SM-1..5 targets (kept in PRD).
- (event) spec finalized
- (decision) Story Breakdown agreed (17 stories, execution order): 1 scaffold, 2 audit/throttle/session foundations, 3 directory adapter, 4 OIDC sign-in, 5 page shell+prefs, 6 scheduler+mail outbox, 7 password change, 8 profile edit, 9 e-mail change, 10 picture backend, 11 picture screens, 12 announcements core, 13 announcement editor, 14 announcement e-mail+opt-out, 15 dashboard, 16 admin pages+maintenance jobs, 17 hardening+acceptance.
- (decision) Checkpoints: spec_checkpoint on stories 1,3,4,7,10; done_checkpoint (pause after) on 4,7,16,17. User is sole reviewer.
- (constraint) Testing environment: a test OU can be created, but NO separate authentik - authentik is production only. Portal gets its own dedicated OIDC application/provider in prod authentik, restricted to test users while testing; never modify existing authentik applications.
- (event) stories.yaml written (17 stories) and checked against schema rules: required fields, unique string ids, prefix-free, no status field.
- (event) Clarification: stories.yaml check above was done with a regex structure check (17 ids/titles/descriptions, unique, quoted, prefix-free, no status, no stray unindented lines); a full YAML parse was not possible because PyYAML could not be installed.
@@ -0,0 +1,100 @@
---
id: SPEC-ping-mee-self-service-platform
companions:
- ../../prds/prd-ping-mee-self-service-platform-2026-10-15/prd.md
- ../../prds/prd-ping-mee-self-service-platform-2026-10-15/addendum.md
- ../../architecture/architecture-ping-mee-self-service-platform-2026-10-15/ARCHITECTURE-SPINE.md
- ../../ux-designs/ux-ping-mee-self-service-platform-2026-10-15/DESIGN.md
- ../../ux-designs/ux-ping-mee-self-service-platform-2026-10-15/EXPERIENCE.md
sources:
- ../../briefs/brief-ping-mee-self-service-platform-2026-10-15/brief.md
- ../../briefs/brief-ping-mee-self-service-platform-2026-10-15/addendum.md
---
> **Canonical contract.** This SPEC and the files in `companions:` are the complete, preservation-validated contract for what to build, test, and validate. Source documents listed in frontmatter are for traceability — consult them only if you need narrative rationale or prose color this contract intentionally omits.
# ping-mee Self-Service Portal
## Why
The ping-mee homelab, used by about ten family members and friends, keeps all accounts in one Active Directory. Today every change — a password, a name, a phone number, a picture — goes through the admin by hand, and there is no single place to announce downtime. This is a pain to solve for the admin and the users, and it should be built to a standard that lets it run safely on the internet and later be deployed in other Active Directory homelabs.
## Capabilities
*Each capability's requirement-level detail (FR-N with testable consequences) is in the PRD companion; the number in brackets is the PRD range.*
- **CAP-1** [FR-1..FR-4]
- **intent:** A person with an AD account signs in through the identity provider and gets only the access their AD group membership allows.
- **success:** A user in the configured OU signs in via OIDC; a user outside the Publisher and Admin groups cannot reach those pages by direct address; sessions end after 1 hour idle or 4 hours total; a disabled or locked AD account loses access at its next request; repeated sensitive requests are refused before any AD call.
- **CAP-2** [FR-5..FR-7]
- **intent:** A signed-in user changes their own password.
- **success:** With the correct current password and a policy-conforming new one, the AD password changes over LDAPS, the user's other sessions end and a confirmation e-mail is sent; a policy rejection is shown in plain words with no change; five wrong current-password attempts block further tries for the configured pause.
- **CAP-3** [FR-8..FR-11]
- **intent:** A signed-in user edits their own first name, last name, display name, e-mail and telephone numbers.
- **success:** Saved values appear in the matching AD attributes; injection strings change nothing else; an e-mail change applies only after the current password, a click on a confirmation page from the new address, and sends the old address a notice with a 24-hour undo that ends the user's sessions and notifies the Admin.
- **CAP-4** [FR-12..FR-15]
- **intent:** A signed-in user sets or removes their profile picture.
- **success:** A JPEG, PNG, HEIC or WebP file up to 10 MB is cropped, re-encoded and written as `<sAMAccountName>.jpg` to the source folder; the page shows Pending until `processed/<name>.md5` matches the written file; other formats and larger files are refused with a reason.
- **CAP-5** [FR-16..FR-19]
- **intent:** A Publisher announces planned changes and downtime.
- **success:** A Publisher writes a titled announcement in a rich-text editor with an always-set severity and an optional downtime window, expiry and publish time; it appears as a banner and in a list for signed-in users, can be edited, withdrawn, or re-sent on request; script content in the body never runs.
- **CAP-6** [FR-20, FR-21]
- **intent:** Users hear about announcements by e-mail unless they opted out.
- **success:** On publish every user in the OU with a valid e-mail address, minus explicit opt-outs, receives the e-mail through the configured SMTP server; users who opted out receive nothing; the Publisher sees counts and reasons beforehand; failures are listed for the Admin.
- **CAP-7** [FR-22..FR-25, FR-32]
- **intent:** An Admin manages how the portal looks and behaves and can see whether it is healthy.
- **success:** Only Admin-group members reach the Admin pages; the accent colour is checked for contrast in both themes with a warning but allowed; runtime settings change without restart; the status page shows backup age, failed e-mails and job runs; the audit log is readable.
- **CAP-8** [FR-28]
- **intent:** The Admin can reconstruct who changed what.
- **success:** Every password change, profile change, e-mail change step, picture action, announcement action, settings change and throttled or failed attempt produces an entry with who, what, when and IP, kept 12 months, holding no password, token or picture content.
- **CAP-9** [FR-29..FR-31]
- **intent:** Each user sees a clear landing page and sets how the portal looks and speaks on their own device.
- **success:** The dashboard shows active announcements and an account card with the password date; theme, language and German form of address are chosen from the avatar menu and stored on the device; German and English work, with formal and informal German; the portal is usable on desktop and phone and meets WCAG 2.1 AA.
- **CAP-10** [FR-26, FR-27]
- **intent:** The portal runs unchanged in any environment from configuration alone.
- **success:** One Docker image starts only when all required environment variables are valid; no image content names ping-mee, a domain, OU, group, IP or path; secrets appear in no log or UI; a health check works without AD; consistent database backup files appear in the backup folder on schedule.
## Constraints
- Architecture is binding as written in `ARCHITECTURE-SPINE.md` (rules AD-1..AD-19): Django modular monolith with one owner per external system.
- One container, one worker, SQLite on a volume; the portal writes its own consistent backups, because the host copy job copies directories.
- AD is reached only over LDAPS with a service account limited to a configurable OU and fixed read and write attribute lists; `cn` and `name` are never edited.
- The existing PowerShell sync, logon and registry scripts stay unchanged; the portal only writes `<sAMAccountName>.jpg` to the source folder and reads `<sAMAccountName>.md5` from the processed folder.
- The portal is internet-facing: no injection, no cross-user access, throttling before any AD call, passwords never stored or logged, strict content-security policy with no inline script and no external fonts, scripts or CDNs.
- Expired, forgotten or must-change passwords are never handled by the portal; the Admin resets them in AD.
- Precedence when companions differ: behavior follows the PRD, architecture follows the spine, look follows `DESIGN.md`, screen behavior follows `EXPERIENCE.md`.
## Non-goals
- No forgotten-password or expired-password flow.
- No admin console for other users' data: Admins cannot edit another user's profile, picture or password.
- No renaming of AD objects (`cn`, `name`).
- No multi-domain support.
- No replacement of the PowerShell scripts or the sync task.
- No public status or announcement page; signed-out visitors see nothing.
- No SVG uploads.
- No alerting beyond the Admin status page; no staging environment (a test OU is used instead).
- No identity-provider features of its own: sign-in and multi-factor stay with authentik.
## Success signal
A family member with no help from the admin changes their password, name, phone number and picture in the portal, while the admin publishes a scheduled downtime announcement that every opted-in user receives by e-mail and sees as a banner, and an attacker's injection, cross-user and brute-force attempts against the internet-facing portal all fail and are recorded.
## Assumptions
- Default values are placeholders to confirm at build: failed-attempt limit and pause (5, 15 minutes), confirmation link lifetime (1 hour), picture output (512×512 JPEG), Pending timeout, throttle limits per endpoint, backup retention, and the "backup too old" age (26 hours).
- E-mails use the portal-wide default form of address, because a per-device choice cannot reach server-sent mail.
- The default accent colour is the Fluent 2 default blue (`#0F6CBD`) and needs verifying against the component package.
- Active Directory attribute names for the password policy (`minPwdLength`, `pwdProperties`, `pwdHistoryLength`, `msDS-ResultantPSO`) need verifying in the test OU.
- Users depend on authentik being available for sign-in; no fallback.
- Data of users removed from AD is deleted by a nightly reconciliation; audit entries stay until retention ends.
- Sample personas and journeys in the PRD and UX documents are illustrative.
## Open Questions
- What are the exact retry count and the IONOS sending limits for announcement e-mail?
- How is a restore from the portal's backup files tested before it is needed?
- Does sign-out also end the authentik session?
- Which rich-text editor library is used, and how are the front-end assets built into the image?
- Where does the Docker image live: a registry, or built on the host?
- Is the early-clearing of Pending after a failed AD write by the sync script an acceptable limitation (PRD Q-9; accepted in the spine, confirm at first use)?
@@ -0,0 +1,95 @@
- id: "1"
title: Project scaffold, configuration and container
description: >-
Create the Django project, environment-variable configuration that refuses to start when invalid, the multi-stage Docker image and compose file, the health check, JSON logging, security headers with a strict content-security policy, and the test setup with adapter fakes. Covers CAP-10 and the foundations of AD-1, AD-13 and AD-18 in the architecture spine.
spec_checkpoint: true
invoke_dev_with: >-
Verify and pin current versions of Python, Django (5.2 LTS) and the other stack entries on the web before binding them. Nothing in the repository may name ping-mee, a domain, an OU, a group, an IP or a path.
- id: "2"
title: Audit, throttle and session foundations
description: >-
Build the audit service, the database-backed throttle counters, and server-side sessions with the username in its own column and a kill-by-user function. Covers CAP-8 and the base of CAP-1; rules AD-4, AD-6, AD-7 and AD-19.
- id: "3"
title: Directory adapter
description: >-
Build the only code that talks to Active Directory: lookup by sAMAccountName inside the configured OU, group membership, account state, password verification, password policy read, and the allow-listed writes, plus an in-memory fake. Covers CAP-1 to CAP-3; rules AD-1, AD-2, AD-5 and AD-8.
spec_checkpoint: true
invoke_dev_with: >-
Use python-ldap, not ldap3. LDAPS must verify against a CA bundle supplied by environment variable. Test only against a test OU with a test service account scoped to it, never against the production OU. Verify the password-policy attribute names in the test OU.
- id: "4"
title: Sign-in with OIDC and roles
description: >-
Implement sign-in through the identity provider using the authorization-code flow, session creation, roles from AD group membership with a short cache, disabled accounts cut off, and the trusted-proxy client IP rule. Covers CAP-1; rules AD-5, AD-6 and AD-7.
spec_checkpoint: true
done_checkpoint: true
invoke_dev_with: >-
Use Authlib. Authentik is the production instance only: the portal gets its own dedicated OIDC application and provider, restricted to test users while testing. Never change any existing authentik application. The proxy is Zoraxy: take the last X-Forwarded-For entry from a trusted proxy and never X-Real-IP.
- id: "5"
title: Page shell, preferences and translations
description: >-
Build the base layout with the Fluent web components bundled into the image: top bar, left menu with role-based groups, avatar menu for theme, language and German form of address, German and English text, light and dark themes, and the phone layout. Covers CAP-9 and the layout parts of DESIGN.md and EXPERIENCE.md.
invoke_dev_with: >-
No inline scripts and no external assets. Preferences live in cookies; the theme is set by the server and CSS, not by script. Follow the mock-ups in the UX folder.
- id: "6"
title: Scheduler and mail outbox
description: >-
Build the single 30-second scheduler tick with idempotent jobs, job-run records, the mailer adapter and the outbox with retries, backoff and a rate limit, and a fake mailer. Covers CAP-6 and CAP-10; rules AD-11 and AD-12.
- id: "7"
title: Password change
description: >-
Build the password page with the Active Directory password policy listed up front, the current-password check with the attempt cap, the LDAPS change, ending the user's other sessions, and the confirmation e-mail. Covers CAP-2 (FR-5 to FR-7); rules AD-2, AD-3 and AD-8.
spec_checkpoint: true
done_checkpoint: true
invoke_dev_with: >-
Test only against the test OU. Never log, store or audit a password. Plain-language messages for policy rejections.
- id: "8"
title: Profile edit
description: >-
Build the profile page for first name, last name, display name, telephone and mobile with validation and LDAP escaping. Covers part of CAP-3 (FR-8, FR-9); rules AD-2 and AD-3.
- id: "9"
title: E-mail change with confirmation and undo
description: >-
Build the e-mail change flow: current password, confirmation page from a link to the new address, notice with a 24-hour undo link to the old address, and the undo effects (restore, end sessions, notify the Admin). Covers CAP-3 (FR-10, FR-11); rules AD-8 and AD-9.
invoke_dev_with: >-
A link opened with GET must show a page and change nothing; only a POST with CSRF applies the change. Tokens are hashed at rest and single use.
- id: "10"
title: Picture backend
description: >-
Build upload validation, decoding of JPEG, PNG, HEIC and WebP with size and pixel limits, square re-encoding, the share adapter with atomic writes and timeouts, and the stored MD5 state. Covers CAP-4 (FR-12, FR-14, FR-15 backend); rule AD-10.
spec_checkpoint: true
invoke_dev_with: >-
Pin the HEIC library after checking current versions on the web. Refuse SVG and everything else. Never store or serve the original upload. The share is mounted by the host; write only to the source folder and read the processed folder.
- id: "11"
title: Picture screens
description: >-
Build the picture page with in-browser cropping, the Pending status read from the share and Active Directory, and removal. Covers CAP-4 (FR-13 to FR-15 UI); see the picture mock-up and EXPERIENCE.md.
- id: "12"
title: Announcements core
description: >-
Build the announcement model with an always-set severity, draft, publish and withdraw, the list page and the banners, with safe Markdown rendering. Covers CAP-5 (FR-16 to FR-19 backend); rules AD-16 and AD-19.
- id: "13"
title: Announcement editor
description: >-
Build the publisher pages with the rich-text editor, downtime window, expiry, scheduled publishing, edit, and the "notify again" option. Covers CAP-5 (FR-16, FR-17, FR-18 UI); see the editor mock-up.
invoke_dev_with: >-
Choose a maintained open-source rich-text editor that outputs Markdown and can be bundled into the image, after checking current options on the web. The server must treat the submitted Markdown as untrusted.
- id: "14"
title: Announcement e-mail and opt-out
description: >-
Build recipient resolution (all users in the OU with a valid address minus opt-outs), the opt-in and opt-out page with its notice, the recipient summary for the Publisher, and the failure listing. Covers CAP-6 (FR-20, FR-21); rule AD-11.
- id: "15"
title: Dashboard
description: >-
Build the landing page with active announcement banners and the account card, including the password last changed date. Covers CAP-9 (FR-30); see the dashboard mock-up.
- id: "16"
title: Admin pages and maintenance jobs
description: >-
Build the Admin pages (accent colour with contrast warning, backgrounds, runtime settings, audit log viewer, status page) and the maintenance jobs (database backup with retention, nightly AD reconciliation, audit retention). Covers CAP-7, CAP-8 and CAP-10; rules AD-12 to AD-14.
done_checkpoint: true
invoke_dev_with: >-
Backups use the SQLite online backup API into the backup folder; the status page shows the age of the last good backup.
- id: "17"
title: Hardening and acceptance
description: >-
Run security tests (injection, cross-user access, throttling, session handling), the accessibility pass, and write deployment notes and a smoke checklist for the test OU. Covers all capabilities and the success signal in SPEC.md.
done_checkpoint: true