# CannaManage Sprint 6 Plan — Multi-Persona Review Panel **Date:** 2026-06-12 **Reviewed Document:** `docs/sprint-6/cannamanage-sprint6-plan.md` (v2, ~1266 lines) **Review Method:** 6-persona stakeholder simulation, scoring on 4 dimensions (0–100%) **Iteration:** 1 **Sprint Goal:** Production readiness — deploy, DSGVO compliance, Stripe payments, immutable audit log, grow calendar, notifications, launch --- ## Decisions Incorporated (13 confirmed ✅) All open questions from the planning session are resolved: 1. ✅ **Stripe** (fresh account) — SEPA + PayPal + Card (D1) 2. ✅ **SEPA Lastschrift** as primary payment method (D2) 3. ✅ **IONOS VPS** (existing plate-software.de, 8 GB RAM) (D3) 4. ✅ **Subdomain** `cannamanage.plate-software.de` (D4) 5. ✅ **pg_dump** daily 7d + weekly 4w retention (D5) 6. ✅ **Gitea Actions** self-hosted CI/CD (D6) 7. ✅ **Uptime Kuma** for monitoring (D7) 8. ✅ **Consent-first** DSGVO approach (D8) 9. ✅ **Immutable append-only** audit log (D9) 10. ✅ **FullCalendar** with FULL scope (sensors, photos, feeding) (D10) 11. ✅ **WebSocket (SockJS/STOMP)** + Web Push API (D11) 12. ✅ **3-month free trial** → tiered pricing (D12) 13. ✅ **Basic PWA** (manifest + offline page) in Sprint 6 (D13) --- ## 1. 👤 Club Member (End User) *"I'm a regular member of a cannabis social club. I want to use the app on my phone, get notified about my distributions, and trust that my data is protected under DSGVO."* ### Findings | # | Type | Observation | |---|------|-------------| | 1 | ✅ Positive | **PWA installable** — I can add CannaManage to my home screen and use it like a native app. Standalone mode, themed green, proper icons. This makes daily access much faster than bookmarking a URL. | | 2 | ✅ Positive | **Real-time distribution notifications** — When staff records a distribution for me, I get an instant notification via WebSocket. No more checking manually. The bell icon shows unread count. | | 3 | ✅ Positive | **Push notifications even when app is closed** — Web Push with opt-in means I get notified about quota warnings and batch recalls even with the browser closed. Critical for recalls. | | 4 | ✅ Positive | **DSGVO transparency** — I can see exactly what data is stored about me, export everything as ZIP (Art. 15), and request full deletion (Art. 17). The consent flow is clear: accept or leave. | | 5 | ✅ Positive | **Offline resilience** — If I lose signal, I see an offline page instead of a browser error. Not full offline mode, but graceful degradation. | | 6 | ⚠️ Minor | **No grow calendar visibility for members** — The grow calendar appears to be admin-only. As a member, I'd love to see what's currently growing (without sensitive details) — builds trust and community engagement. | | 7 | ⚠️ Minor | **Notification preferences not detailed** — Can I choose which notifications I receive? The plan mentions opt-in for push but doesn't describe granular notification preferences (e.g., only recalls, not every distribution). | | 8 | ✅ Positive | **Consent versioning** — If the privacy policy changes, I'm re-prompted. I'm not silently bound to new terms. | ### Scores | Dimension | Score | Rationale | |-----------|-------|-----------| | Precision | 88% | Notification events are explicitly listed (quota warning, batch recall, new distribution). PWA manifest is precisely defined. However, notification *preferences* and member-facing grow visibility are unspecified. | | Correctness | 92% | WebSocket + STOMP is a correct real-time architecture for this scale. Web Push with VAPID is the right standard. DSGVO implementation follows Arts. 6, 7, 15, 17 correctly. | | Usability | 86% | PWA + push + bell icon covers my daily needs well. Missing granular notification control and member-facing grow calendar slightly reduce the experience. | | Usefulness | 90% | The jump from "no notifications, no DSGVO, no mobile" to "push notifications, full DSGVO compliance, installable PWA" is massive. This sprint makes the app genuinely usable for daily members. | **Composite Score: 89%** ### Recommended Fixes - Add a note about notification preferences (future sprint or a simple "mute all" toggle) - Consider a read-only grow progress view for members (e.g., "Northern Lights — Flowering 🌸" without quantities) --- ## 2. 🏢 Club Owner / Vorstand (Business Owner) *"I run the Anbauvereinigung. I need Stripe payments to fund operations, an audit log for Behörde inspections, the grow calendar to manage our cultivation, and a reliable production deployment."* ### Findings | # | Type | Observation | |---|------|-------------| | 1 | ✅ Positive | **Stripe handles all billing complexity** — SEPA mandates, dunning, invoicing, billing portal, payment method updates — all offloaded to Stripe. I don't need to manually chase payments or generate invoices. | | 2 | ✅ Positive | **Tiered pricing is clear** — Starter (€29/50 members) vs. Pro (€79/unlimited) gives me room to grow. The 14-day free trial lowers the barrier for new clubs. | | 3 | ✅ Positive | **Immutable audit log for Behörde** — PDF export of the audit log is exactly what inspectors need. Every distribution, member change, and batch action is logged with server timestamps. Immutability (`@Immutable` + no DELETE) ensures credibility. | | 4 | ✅ Positive | **Grow calendar (FULL scope)** — Stage tracking from seedling to curing, batch linking for traceability, sensor data for environmental control, photo documentation for compliance evidence. This is a complete cultivation management tool. | | 5 | ✅ Positive | **Production deployment on existing infrastructure** — No new server to provision. Co-hosted on the existing IONOS VPS means lower cost and immediate availability. Gitea Actions CI/CD means I don't depend on GitHub. | | 6 | ⚠️ Medium | **Pricing discrepancy in plan** — Section 0 (Decisions) says "≤30 members, €19/mo" and "≤100 members, €49/mo" but Section 3.3 (Stripe Architecture) says "up to 50 members, €29/month" and "unlimited members, €79/month". These need to be reconciled before implementation. | | 7 | ⚠️ Medium | **Trial period discrepancy** — Decision D12 says "3-month free trial" but the Stripe code shows `setTrialPeriodDays(14L)` (14 days). Which is it? This directly affects my club's onboarding strategy. | | 8 | ⚠️ Minor | **No grace period defined** — What happens when a payment fails? How many retry attempts before lockout? The plan mentions `invoice.payment_failed` shows a warning but doesn't specify the dunning timeline (Stripe default is 3 attempts over ~14 days, but this should be documented). | | 9 | ✅ Positive | **Subscription enforcement middleware** — `SubscriptionFilter.java` blocks API access when subscription expires. No free-riding after trial ends. | | 10 | ✅ Positive | **Harvest → batch traceability** — Linking a grow entry to a batch creates the full chain: grow → batch → distribution → member. This is exactly what regulators want to see. | ### Scores | Dimension | Score | Rationale | |-----------|-------|-----------| | Precision | 82% | The pricing/trial discrepancies are concerning — they need resolution before implementation. Grow calendar and audit log are precisely specified. Dunning behavior is under-specified. | | Correctness | 88% | Stripe architecture follows best practices (webhook signature verification, idempotent handlers, billing portal). Audit log immutability approach is correct. Grow stages and batch linking are logically sound. | | Usability | 90% | Self-service billing portal, one-click PDF export for audits, visual grow calendar — all reduce my operational burden significantly. | | Usefulness | 93% | This sprint delivers the three hardest requirements for going live: payments (to sustain the business), audit (for legal compliance), and grow tracking (for cultivation management). | **Composite Score: 88%** ### Must Fix Before Implementation - 🔴 **Reconcile pricing tiers** — D12 says €19/€49 (30/100 members), Section 3.3 says €29/€79 (50/unlimited). Pick one and update throughout. - 🔴 **Reconcile trial period** — D12 says 3 months, code says 14 days. Which is the actual business decision? - Add dunning timeline documentation (how many retries, what interval, when lockout) --- ## 3. 💻 Developer (Technical) *"I'm building this on IONOS with Docker + Nginx, Gitea Actions for CI/CD, Stripe webhooks, WebSockets, and sensor ingestion. Is the architecture sound?"* ### Findings | # | Type | Observation | |---|------|-------------| | 1 | ✅ Positive | **Docker Compose prod is well-structured** — env vars via `.env`, proper health checks with `start_period`, restart policies, named volumes for pgdata. Production-ready configuration. | | 2 | ✅ Positive | **Nginx config is comprehensive** — TLS termination, HTTP/2, HSTS, rate limiting (30r/s with burst), WebSocket upgrade, security headers. The Stripe webhook path correctly skips rate limiting. | | 3 | ✅ Positive | **Gitea Actions CI/CD is practical** — Self-hosted, no external dependencies, build → Docker → deploy locally. Simple and fast for a single-server deployment. | | 4 | ⚠️ Medium | **Gitea Actions `cd /opt/cannamanage` in deploy step** — The deploy step assumes the runner IS the production server (running locally). This works but means the CI runner has root-level access to production. If the Gitea instance is compromised, production is compromised. Consider SSH-based deployment even for local deploys (separation of concerns). | | 5 | ⚠️ Medium | **No blue-green or rolling deploy** — `docker compose up -d --remove-orphans` causes downtime during image pull + container restart. For a single VPS this might be acceptable, but even a simple `docker compose pull && docker compose up -d` split would reduce downtime to seconds. | | 6 | ✅ Positive | **Stripe webhook signature verification** — `Webhook.constructEvent(payload, sigHeader, webhookSecret)` is the correct Stripe SDK pattern. Prevents webhook spoofing. | | 7 | ⚠️ Minor | **WebSocket auth not specified** — `WebSocketConfig` uses `setAllowedOriginPatterns("*")`. In production, this should be restricted to the actual domain. Also, how is the WebSocket connection authenticated? STOMP interceptor with JWT? Session cookie? | | 8 | ⚠️ Minor | **Sensor data ingestion endpoint unclear** — The plan mentions "API endpoint for IoT devices" for sensor readings but doesn't specify authentication for IoT devices. Are sensors authenticated with API keys? Device tokens? This is a different auth flow from user JWT. | | 9 | ✅ Positive | **Backup strategy is simple and effective** — `pg_dump | gzip` with cron rotation. For a single-VPS deployment with <1000 users, this is the right approach. No over-engineering. | | 10 | ✅ Positive | **Flyway migrations are well-sequenced** — V6 (consent + audit), V7 (stripe), V8 (grow), V9 (notifications). Clear dependency ordering. | | 11 | ⚠️ Minor | **Phase 1 header says "Hetzner VPS" but body says "IONOS VPS"** — The Phase 1 heading reads "Production Deployment (Hetzner VPS)" but the decision says IONOS. Inconsistency in the document. | | 12 | ✅ Positive | **Service worker scope is appropriately limited** — "Basic in Sprint 6" (offline page + asset caching) avoids the complexity of full offline sync, which would require IndexedDB and conflict resolution. | | 13 | ⚠️ Minor | **Photo upload size limit (10MB) but no storage strategy** — Where are grow photos stored? Local filesystem? S3-compatible object storage? For a single VPS, local disk works initially but needs a plan for growth. | ### Scores | Dimension | Score | Rationale | |-----------|-------|-----------| | Precision | 85% | Core architecture is well-specified (Docker, Nginx, Stripe, Flyway). Gaps in WebSocket auth, sensor device auth, photo storage, and deploy strategy reduce precision. | | Correctness | 87% | Docker Compose patterns, Nginx configuration, Stripe SDK usage, and STOMP/SockJS are all technically correct. The `allowedOriginPatterns("*")` and missing auth layers are correctness concerns for production. | | Usability | 88% | Gitea Actions CI/CD is developer-friendly. Single `docker compose up` for local dev. Flyway handles migrations automatically. Clear phase separation makes implementation order obvious. | | Usefulness | 91% | The plan delivers a complete production stack: deploy pipeline, payment processing, real-time notifications, and grow management — all on a single affordable VPS. Pragmatic engineering. | **Composite Score: 88%** ### Recommended Fixes - 🔴 Fix Phase 1 heading: "Hetzner VPS" → "IONOS VPS" - Specify WebSocket authentication mechanism (STOMP CONNECT frame with JWT or session-based) - Restrict `setAllowedOriginPatterns` to `cannamanage.plate-software.de` in production - Document sensor device authentication strategy (API key-based recommended) - Add photo storage strategy note (local filesystem initially, `/opt/cannamanage/uploads/`, with future migration path to S3) - Consider `docker compose pull && docker compose up -d` two-step deploy to minimize downtime --- ## 4. 🛡️ Datenschutzbeauftragter (Privacy/DSGVO Officer) *"I ensure this application complies with DSGVO. I check consent flows, data exports, erasure rights, data minimization, and third-party data processing agreements."* ### Findings | # | Type | Observation | |---|------|-------------| | 1 | ✅ Positive | **Art. 6 Rechtsgrundlage** — Consent-first approach (Art. 6 Abs. 1 lit. a) is implemented. Users must explicitly accept before using the app. Consent entity stores timestamp, IP, user agent, and policy version. | | 2 | ✅ Positive | **Art. 7 Einwilligung** — Consent is freely given (user can decline and is logged out), specific (separate consent types: PRIVACY_POLICY, TERMS_OF_SERVICE, DATA_PROCESSING), informed (links to full Datenschutzerklärung), and unambiguous ("Akzeptieren" button). Withdrawal is as easy as giving (revoke in settings). | | 3 | ✅ Positive | **Art. 15 Auskunftsrecht** — `GET /api/v1/dsgvo/export` returns a ZIP with all personal data. The implementation collects user profile, distributions, quota history, consent records, and login history. | | 4 | ✅ Positive | **Art. 17 Recht auf Löschung** — `DELETE /api/v1/dsgvo/erasure` with appropriate exemption: "Some data must be retained for legal compliance (tax records, 10-year retention)." This correctly implements the Art. 17(3)(b) exception for legal obligations. | | 5 | ⚠️ Medium | **Art. 28 Auftragsverarbeitung (Stripe)** — Stripe is a data processor handling payment data. The plan doesn't mention the AVV (Auftragsverarbeitungsvertrag) with Stripe. This is a legal requirement. Stripe provides a standard DPA — it must be accepted and documented. | | 6 | ⚠️ Medium | **IP address storage in consent** — Storing IP addresses in the consent record is legitimate for proof of consent (Art. 7(1)), but the Datenschutzerklärung must explicitly mention this. IP addresses are personal data (EuGH C-582/14). Ensure retention period is defined. | | 7 | ⚠️ Medium | **Audit log contains PII** — `actor_name` ("Patrick Plate"), `ip_address`, and `description` (may contain member names in distribution descriptions like "25g Northern Lights an Max Mustermann ausgegeben"). The audit log is immutable (no deletion), which conflicts with Art. 17. This needs a documented legal basis (Art. 6(1)(c) — legal obligation for compliance records) and a defined retention period. | | 8 | ⚠️ Minor | **Data minimization (Art. 5(1)(c))** — The consent entity stores `user_agent` (full browser string). Is the full user agent necessary? A truncated version or just the browser name would satisfy proof requirements while minimizing data. | | 9 | ✅ Positive | **Consent versioning** — When the privacy policy changes, users are re-prompted. This correctly implements the requirement for renewed consent when purposes change. | | 10 | ⚠️ Minor | **No cookie banner mentioned** — The consent modal handles app usage consent, but if any analytics cookies or third-party scripts are used (Uptime Kuma external check?), a separate cookie consent mechanism may be needed under ePrivacy/TTDSG. If no cookies beyond session cookies → document this. | | 11 | ✅ Positive | **Erasure with anonymization** — "Anonymize distributions (keep aggregates for compliance)" correctly balances Art. 17 deletion with legal retention. Aggregate data is no longer personal data. | ### Scores | Dimension | Score | Rationale | |-----------|-------|-----------| | Precision | 84% | Core DSGVO flows (consent, export, erasure) are well-specified. Missing: AVV documentation with Stripe, audit log retention periods, IP storage justification in Datenschutzerklärung. | | Correctness | 86% | Arts. 6, 7, 15, 17 are correctly implemented. The Art. 17(3)(b) exception for legal obligations is correctly applied. However, the audit log immutability vs. Art. 17 tension needs explicit legal basis documentation. | | Usability | 90% | The consent flow is user-friendly: clear modal, accept/decline, revoke in settings, export as ZIP. Privacy settings page gives users full control. | | Usefulness | 88% | The DSGVO implementation covers all critical requirements for launch. The few gaps (AVV, retention periods, audit log legal basis) are documentation tasks, not code changes. | **Composite Score: 87%** ### Must Fix Before Launch - 🔴 **Document Stripe AVV** — Accept Stripe's DPA, reference it in the Datenschutzerklärung as a data processor (Art. 28) - 🔴 **Define audit log retention period** — Document the legal basis (Art. 6(1)(c) + §257 HGB 10-year retention for business records) and add it to the Datenschutzerklärung - 🟡 Mention IP address storage in the Datenschutzerklärung (why, how long, legal basis) - 🟡 Consider truncating `user_agent` to browser name + version only - Clarify cookie usage: if only session cookies, add a "no tracking cookies" statement to the privacy policy --- ## 5. 🔒 Security Auditor *"I audit the security posture of this application before it goes live. I check PCI compliance, SEPA data handling, webhook verification, TLS configuration, secret management, and backup encryption."* ### Findings | # | Type | Observation | |---|------|-------------| | 1 | ✅ Positive | **Stripe handles PCI compliance** — By using Stripe Checkout (hosted payment page) and Payment Element (Stripe.js), card data never touches our servers. PCI SAQ-A eligible. No card numbers in our DB. | | 2 | ✅ Positive | **Webhook signature verification** — `Webhook.constructEvent(payload, sigHeader, webhookSecret)` validates the HMAC signature from Stripe. Rejects tampered or replayed webhook events. | | 3 | ✅ Positive | **TLS configuration** — Let's Encrypt certificate, HTTP→HTTPS redirect, HSTS with 2-year max-age. SSL Labs A+ should be achievable with these settings. | | 4 | ✅ Positive | **Security headers** — HSTS, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin. Good baseline. | | 5 | ⚠️ Medium | **Missing CSP header** — Content-Security-Policy is not in the Nginx config. Without CSP, XSS attacks can load arbitrary external scripts. Add at minimum: `default-src 'self'; script-src 'self' https://js.stripe.com; frame-src https://js.stripe.com;` | | 6 | ⚠️ Medium | **Secrets in Docker env vars** — `STRIPE_SECRET_KEY`, `JWT_SECRET`, `DB_PASSWORD` are passed as environment variables. While this is common with Docker Compose, the `.env` file on the server must be root-only readable (chmod 600). The plan doesn't mention file permissions or secret rotation. | | 7 | ⚠️ Medium | **Backup encryption missing** — `pg_dump | gzip` creates unencrypted backups. If the VPS is compromised, all historical data is exposed. Add `| gpg --encrypt --recipient backup@cannamanage.de` or use `pg_dump ... | openssl enc -aes-256-cbc`. | | 8 | ⚠️ Minor | **Rate limiting only on `/api/`** — The `/` (frontend) path has no rate limiting. While Next.js SSR is less sensitive, a determined attacker could DOS the frontend rendering. Consider a lower global rate limit (e.g., 100r/s). | | 9 | ✅ Positive | **Stripe webhook path excluded from rate limiting** — Correct decision. Stripe retries on 429 with exponential backoff, but rate-limiting webhooks can cause payment state drift. | | 10 | ⚠️ Minor | **No fail2ban or IP blocking** — If the rate limiter triggers repeatedly for an IP, there's no mechanism to ban that IP temporarily. Consider adding `limit_req_status 429` + fail2ban watching for 429s in access.log. | | 11 | ✅ Positive | **Audit log immutability** — `@Immutable` annotation + no DELETE in repository. Server-generated timestamps prevent client manipulation. JSONB details field preserves before/after state for forensics. | | 12 | ⚠️ Medium | **No DB-level DELETE prevention on audit_event** — Hibernate `@Immutable` is application-level only. A compromised backend or direct DB access can still DELETE records. Add a PostgreSQL trigger: `CREATE RULE no_delete_audit AS ON DELETE TO audit_event DO INSTEAD NOTHING;` or use a `REVOKE DELETE` on the app DB user. | | 13 | ⚠️ Minor | **SEPA data handling** — SEPA bank details (IBAN) are handled by Stripe, not stored locally. Good. But the plan should explicitly state "No IBAN data touches our servers" as a security control statement. | | 14 | ✅ Positive | **JWT for API auth** — `JWT_SECRET` from env, Spring Security integration for API endpoints. Standard approach for SPA backends. | | 15 | ⚠️ Minor | **No JWT rotation or expiry mentioned** — The plan uses `JWT_SECRET` but doesn't specify token expiry, refresh token strategy, or secret rotation cadence. For a go-live, at minimum define token TTL (e.g., 15min access + 7d refresh). | ### Scores | Dimension | Score | Rationale | |-----------|-------|-----------| | Precision | 83% | TLS, Stripe, and webhook verification are precise. Missing: CSP header definition, backup encryption, secret file permissions, DB-level audit protection, JWT lifecycle. | | Correctness | 85% | The security architecture is fundamentally sound (Stripe handles PCI, TLS terminates at Nginx, rate limiting exists). But application-level immutability without DB enforcement is incomplete. Missing CSP is a significant gap. | | Usability | 91% | From a security operations standpoint: automated TLS renewal, simple backup cron, Docker restart policies, and Uptime Kuma alerting make ongoing security operations manageable. | | Usefulness | 87% | The plan delivers a production-ready security baseline. The gaps are hardening measures that should be addressed before public launch but don't block development. | **Composite Score: 87%** ### Must Fix Before Launch - 🔴 **Add Content-Security-Policy header** — at minimum: `default-src 'self'; script-src 'self' https://js.stripe.com; frame-src https://js.stripe.com https://hooks.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;` - 🔴 **Encrypt backups** — Add GPG or AES-256 encryption to the backup script - 🔴 **Add DB-level audit protection** — PostgreSQL RULE or REVOKE DELETE on audit_event for the app user - 🟡 Document `.env` file permissions (chmod 600, root-only) - 🟡 Define JWT token lifecycle (access TTL, refresh strategy, secret rotation) - 🟡 Add explicit "No IBAN/card data stored locally" security control statement --- ## 6. 🌱 Cultivation Manager (Grower) *"I manage the club's grows from seed to harvest. I need to track plant stages, monitor environment (temperature/humidity), document with photos, plan feeding schedules, and link harvests to inventory batches."* ### Findings | # | Type | Observation | |---|------|-------------| | 1 | ✅ Positive | **Full lifecycle tracking** — Six stages (Seedling → Vegetative → Flowering → Harvest → Drying → Curing) cover the complete cannabis grow cycle. Color-coded calendar blocks give me instant visual status of all active grows. | | 2 | ✅ Positive | **Harvest → batch linking** — This is the killer feature for compliance. When I harvest, I link directly to a batch entity, creating full traceability: seed → plant → harvest → batch → distribution → member. Inspectors love this. | | 3 | ✅ Positive | **Sensor data** — Temperature and humidity readings with configurable threshold alerts (e.g., temp > 30°C → notification). This lets me react quickly to environmental issues even when I'm not in the grow room. | | 4 | ✅ Positive | **Photo documentation** — Timeline view with captions per grow entry. This serves both my own record-keeping (disease identification, growth progress) and compliance evidence for inspectors. | | 5 | ✅ Positive | **Feeding schedule** — Nutrient name, amount, frequency, and calendar overlay showing feeding days. This replaces my spreadsheet and integrates feeding with the grow timeline. | | 6 | ⚠️ Medium | **No multi-grow-room support** — The `GrowEntry` entity has no `room` or `location` field. In a real club, we might have multiple grow rooms (veg room, flower room, drying room). Entries should be groupable by location. | | 7 | ⚠️ Medium | **Sensor readings are per-grow-entry, not per-room** — In practice, a temperature sensor monitors a room, not a single plant. Multiple grows share the same room environment. The data model should allow sensor readings at a room level, shared across grow entries in that room. | | 8 | ⚠️ Minor | **No yield tracking compared to expected** — The entity has `expected_yield_grams` and `actual_yield_grams`, which is great. But the plan doesn't mention a yield comparison/analytics view. Over time, I want to see yield trends per strain. | | 9 | ⚠️ Minor | **No recurring grow templates** — If I grow Northern Lights every 3 months with the same feeding schedule, I'd love to create a template and duplicate it. Not essential for v1, but worth noting. | | 10 | ✅ Positive | **FullCalendar with drag-resize** — Adjusting end dates by dragging is intuitive. Starting a new grow by clicking a date is natural. The de locale (German month names, Monday start) is correct for our users. | | 11 | ✅ Positive | **Audit log integration** — Grow entries and harvest-to-batch links are logged in the audit trail. This creates an unbroken record of cultivation activities for regulatory compliance. | | 12 | ⚠️ Minor | **No strain-specific growing guides** — It would be helpful to see default durations per stage for a given strain (e.g., Northern Lights typical flowering: 8 weeks). Auto-suggest end dates based on strain genetics. Future feature. | ### Scores | Dimension | Score | Rationale | |-----------|-------|-----------| | Precision | 84% | Core entities (GrowEntry, SensorReading, GrowPhoto, FeedingSchedule) are well-defined with specific fields. Stage enum is complete. However, the room/location dimension is missing, and sensor-to-room relationship is not modeled. | | Correctness | 85% | The stage progression, batch linking, and calendar visualization are all correct cannabis cultivation patterns. The sensor model (per-entry rather than per-room) is architecturally questionable but functionally correct for MVP. | | Usability | 88% | FullCalendar with color-coded stages, drag-resize, and click-to-create is highly usable. Photo gallery with timeline is intuitive. Feeding schedule with calendar overlay reduces context switching. | | Usefulness | 90% | This is the most comprehensive grow management I've seen in a club management app. The harvest-to-batch traceability alone justifies the feature. Sensors, photos, and feeding elevate it from a simple calendar to a real cultivation tool. | **Composite Score: 87%** ### Recommended Improvements - 🟡 Add `room` or `location` field to `GrowEntry` (simple VARCHAR, e.g., "Veg Room A", "Flower Room 1") - 🟡 Consider a `SensorDevice` entity linked to a room, with readings associated to the device rather than individual grow entries - Add yield analytics as a future enhancement (strain performance over time) - Note: templates and strain guides are clear Sprint 7+ features --- ## Summary Matrix | Persona | Precision | Correctness | Usability | Usefulness | Composite | |---------|-----------|-------------|-----------|------------|-----------| | 👤 Club Member | 88% | 92% | 86% | 90% | **89%** | | 🏢 Club Owner / Vorstand | 82% | 88% | 90% | 93% | **88%** | | 💻 Developer | 85% | 87% | 88% | 91% | **88%** | | 🛡️ Datenschutzbeauftragter | 84% | 86% | 90% | 88% | **87%** | | 🔒 Security Auditor | 83% | 85% | 91% | 87% | **87%** | | 🌱 Cultivation Manager | 84% | 85% | 88% | 90% | **87%** | | **Average** | **84%** | **87%** | **89%** | **90%** | **88%** | --- ## Verdict: 🟡 CONDITIONAL PASS (88% — below 85% target on Precision) The Sprint 6 plan is ambitious and well-structured, covering all critical go-live requirements. However, it contains internal inconsistencies (pricing, trial period, server provider naming) and security gaps (CSP, backup encryption, DB-level audit protection) that must be resolved before implementation begins. **Overall assessment:** The plan is *implementable as-is* for development purposes, but requires 4 must-fix items before production launch. --- ## Priority Fix List ### 🔴 Must Fix (Blocking launch) | # | Source | Issue | Fix | |---|--------|-------|-----| | 1 | 🏢 Owner | Pricing tier discrepancy (D12 vs §3.3) | Reconcile: pick €19/€49 or €29/€79. Update all references. | | 2 | 🏢 Owner | Trial period discrepancy (3 months vs 14 days) | Confirm business decision. Update D12 or Stripe code. | | 3 | 🔒 Security | No Content-Security-Policy header | Add CSP to Nginx config targeting Stripe JS domains. | | 4 | 🔒 Security | Backups are unencrypted | Add GPG/AES-256 encryption to backup script. | | 5 | 🔒 Security | Audit log only app-level immutable | Add PostgreSQL RULE or REVOKE DELETE on audit_event. | | 6 | 🛡️ DSGVO | No Stripe AVV documentation | Accept Stripe DPA, reference in Datenschutzerklärung. | | 7 | 🛡️ DSGVO | Audit log retention period undefined | Document legal basis (§257 HGB) + retention in privacy policy. | | 8 | 💻 Dev | Phase 1 heading says "Hetzner" instead of "IONOS" | Fix heading text. | ### 🟡 Should Fix (Before launch, non-blocking for dev) | # | Source | Issue | Fix | |---|--------|-------|-----| | 9 | 💻 Dev | WebSocket auth mechanism unspecified | Document STOMP CONNECT JWT or session-based auth. | | 10 | 💻 Dev | `allowedOriginPatterns("*")` in production | Restrict to `cannamanage.plate-software.de`. | | 11 | 💻 Dev | Photo storage location undefined | Document local filesystem path + future S3 migration. | | 12 | 💻 Dev | Sensor device auth for IoT undefined | Specify API key-based auth for devices. | | 13 | 🔒 Security | JWT lifecycle not specified | Define access token TTL (15min) + refresh token (7d). | | 14 | 🔒 Security | `.env` file permissions not documented | Add chmod 600 + root ownership requirement. | | 15 | 🛡️ DSGVO | IP address storage not in privacy policy | Add to Datenschutzerklärung with retention period. | | 16 | 🌱 Grower | No room/location field on GrowEntry | Add optional `location` VARCHAR field. | | 17 | 🌱 Grower | Sensors modeled per-entry not per-room | Consider `SensorDevice` entity linked to location. | ### 🟢 Nice to Have (Sprint 7+) | # | Source | Issue | Fix | |---|--------|-------|-----| | 18 | 👤 Member | No granular notification preferences | Add mute/category toggles in settings. | | 19 | 👤 Member | No member-visible grow progress | Read-only grow view for members. | | 20 | 🏢 Owner | Dunning timeline not documented | Add retry schedule documentation. | | 21 | 🌱 Grower | No grow templates | Duplicate-from-previous feature. | | 22 | 🌱 Grower | No yield analytics | Strain performance dashboard. | | 23 | 💻 Dev | No blue-green deploy | Two-step pull + up for zero-downtime. | | 24 | 🔒 Security | No fail2ban integration | Watch for 429s, auto-ban repeat offenders. | --- ## Recommendation **Proceed with development** after resolving the 8 🔴 must-fix items (most are documentation/config corrections, not architectural changes). The plan is solid architecturally and covers all critical go-live requirements. Target the 🟡 items during implementation as they come up naturally in each phase. **Estimated fix effort:** ~2 hours for document corrections (items 1, 2, 8), ~1 day for security hardening specs (items 3–7).