From 3e926fee0e6f5f31cfcfbe67db6d89ac01848ad7 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Thu, 11 Dec 2025 13:09:11 -0500 Subject: [PATCH] feat: Add GDPR compliance kit with cookie consent and privacy templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Privacy policy template with GDPR Art. 13/14 requirements - Cookie consent React component with granular controls - GDPR-compliant analytics wrapper for Vercel/GA - Netcup DPA Annex 3 completion guide - Records of Processing Activities (ROPA) template - High-priority backlog task for deployment to all sites πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- NETCUP_DPA_ANNEX3_GUIDE.md | 154 ++++++++++ PRIVACY_POLICY_TEMPLATE.md | 229 +++++++++++++++ README.md | 221 ++++++++++++++ RECORDS_OF_PROCESSING_ACTIVITIES.md | 206 +++++++++++++ backlog/config.yml | 15 + ... Deploy-GDPR-compliance-to-all-websites.md | 25 ++ components/CookieConsent.tsx | 275 ++++++++++++++++++ components/GDPRAnalytics.tsx | 122 ++++++++ 8 files changed, 1247 insertions(+) create mode 100644 NETCUP_DPA_ANNEX3_GUIDE.md create mode 100644 PRIVACY_POLICY_TEMPLATE.md create mode 100644 README.md create mode 100644 RECORDS_OF_PROCESSING_ACTIVITIES.md create mode 100644 backlog/config.yml create mode 100644 backlog/tasks/task-high.1 - Deploy-GDPR-compliance-to-all-websites.md create mode 100644 components/CookieConsent.tsx create mode 100644 components/GDPRAnalytics.tsx diff --git a/NETCUP_DPA_ANNEX3_GUIDE.md b/NETCUP_DPA_ANNEX3_GUIDE.md new file mode 100644 index 0000000..e37757f --- /dev/null +++ b/NETCUP_DPA_ANNEX3_GUIDE.md @@ -0,0 +1,154 @@ +# Netcup DPA Annex 3 - Processing Specifications Guide + +This guide helps you fill out Annex 3 of the Netcup Data Processing Agreement. + +## Your Specific Situation + +Based on your infrastructure: +- **Hosting Provider**: netcup GmbH (Germany) +- **CDN/Security**: Cloudflare +- **Newsletter**: Listmonk (self-hosted on Netcup) +- **Analytics**: Vercel Analytics + +--- + +## Section 1: Subject (Nature & Purpose) of the Processing + +**Recommended text to enter:** + +``` +Web hosting and delivery of websites and web applications. This includes: +- Serving static and dynamic web content to visitors +- Processing contact form submissions +- Managing newsletter subscriptions (via self-hosted Listmonk) +- Collecting anonymized website analytics +- Storing user-generated content where applicable +``` + +--- + +## Section 2: Duration of the Processing + +This is automatically determined by your contract term with Netcup. + +--- + +## Section 3: Location of the Processing + +The location is determined by your Netcup server location. For your RS 8000 G12 Pro: +- **Primary Location**: Nuremberg, Germany (EU) +- **Additional Processing**: Via Cloudflare's global network (with EU data residency options) + +--- + +## Section 4: Categories of Data Subjects + +**Check the following boxes:** + +- [x] **Customers** - if you have any e-commerce or client portals +- [x] **Interested parties** - potential customers visiting your sites +- [ ] **Suppliers** - only if you process supplier data +- [x] **Visitors to the website** - all website visitors +- [ ] **Employees of the Client** - only if you have employee data on the sites +- [ ] **External employees** - only if applicable +- [ ] **Data processors, other processors** - only if you subcontract +- [x] **Newsletter subscribers** - you use Listmonk + +**Additional data subjects (if any):** +``` +Event attendees (if you host events/conferences) +Community members (if you have user accounts) +``` + +--- + +## Section 5: Categories of Personal Data + +**Check the following boxes:** + +- [x] **Name data** - contact forms, newsletter signups +- [ ] **Date of birth** - only if you collect this +- [ ] **Bank and payment data** - only if you handle payments directly +- [ ] **Location and geographic information data** - only if you track location +- [ ] **Education data** - only if relevant to your sites +- [ ] **Traffic data** - only if you log detailed traffic +- [ ] **Data relevant to criminal law** - NO +- [x] **Contact and address data** - contact forms +- [ ] **Customer contract data** - only if you have customer portals +- [ ] **Login and authentication** - only if you have user accounts +- [ ] **Preference and behavior data** - only if you track preferences +- [ ] **Motion profile data** - NO +- [ ] **Photo, video, or audio data** - only if you store media + +**Additional data types:** +``` +Email addresses +IP addresses (anonymized for analytics) +Browser/device information (anonymized) +Cookie consent preferences +``` + +--- + +## Special Categories of Data (Art. 9 GDPR) + +**IMPORTANT**: Select the first option unless you specifically process sensitive data. + +- [x] **No special categories of personal data ("sensitive data") according to Art 9 GDPR are processed.** + +If any of your sites deal with health, religion, political opinions, biometric data, etc., you would need to check the second option and specify which categories. + +--- + +## Complete Form Example + +Here's how your completed Annex 3 should look: + +### 1. Subject Matter +``` +Web hosting and content delivery for multiple websites and web applications including: +- Static and dynamic website hosting +- Newsletter subscription management (Listmonk) +- Contact form processing +- Anonymized web analytics collection +- Content management systems +``` + +### 4. Data Subjects (check these): +- [x] Interested parties +- [x] Visitors to the website +- [x] Newsletter subscribers +- [x] Customers (if applicable) + +### 5. Personal Data Categories (check these): +- [x] Name data +- [x] Contact and address data + +**Additional data:** +``` +Email addresses +IP addresses (anonymized) +Browser user agent information +Cookie consent preferences +Website usage data (anonymized) +``` + +### Special Categories: +- [x] No special categories of personal data are processed + +--- + +## After Submitting + +1. **Save a copy** of the completed agreement for your records +2. **Date it** when you submit +3. **Review annually** to ensure it still accurately reflects your processing activities + +--- + +## Tips + +1. **Be conservative** - only check categories you actually process +2. **When in doubt, exclude** - you can always add categories later +3. **Keep it updated** - if you add new features that collect data, update the DPA +4. **Document everything** - maintain your own Records of Processing Activities (ROPA) diff --git a/PRIVACY_POLICY_TEMPLATE.md b/PRIVACY_POLICY_TEMPLATE.md new file mode 100644 index 0000000..e66404d --- /dev/null +++ b/PRIVACY_POLICY_TEMPLATE.md @@ -0,0 +1,229 @@ +# Privacy Policy + +**Last Updated: [DATE]** + +## 1. Introduction + +Welcome to [WEBSITE_NAME] ("we," "our," or "us"). We are committed to protecting your personal data and respecting your privacy in accordance with the General Data Protection Regulation (GDPR) and other applicable data protection laws. + +This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website [WEBSITE_URL] (the "Site"). + +## 2. Data Controller + +The data controller responsible for your personal data is: + +**Jeff Emmett** +23 Birchpark Dr +L3M 4M9 Grimsby, Canada + +Email: [CONTACT_EMAIL] + +## 3. What Data We Collect + +### 3.1 Data You Provide to Us + +We may collect the following categories of personal data that you voluntarily provide: + +- **Contact Information**: Name, email address when you contact us or subscribe to our newsletter +- **Communication Data**: Content of messages you send us through contact forms or email + +### 3.2 Data Collected Automatically + +When you visit our Site, we may automatically collect: + +- **Technical Data**: IP address (anonymized), browser type, operating system, device type +- **Usage Data**: Pages visited, time spent on pages, referring website, click patterns +- **Cookie Data**: See our Cookie Policy section below + +### 3.3 Data We Do NOT Collect + +We do not collect: +- Special category data (health, religion, political opinions, etc.) +- Financial/payment data (unless you make a purchase, handled by third-party processors) +- Data from children under 16 years of age + +## 4. How We Use Your Data + +We process your personal data for the following purposes and legal bases: + +| Purpose | Legal Basis (GDPR Art. 6) | +|---------|--------------------------| +| Responding to your inquiries | Legitimate interest / Contract performance | +| Sending newsletters (if subscribed) | Consent | +| Website analytics and improvement | Legitimate interest / Consent | +| Security and fraud prevention | Legitimate interest | +| Legal compliance | Legal obligation | + +## 5. Newsletter & Email Communications + +If you subscribe to our newsletter: +- We use **Listmonk** (self-hosted) to manage subscriptions +- You can unsubscribe at any time using the link in every email +- We will never share your email with third parties for marketing +- Legal basis: Your explicit consent (GDPR Art. 6(1)(a)) + +## 6. Cookies and Tracking + +### 6.1 What Are Cookies? + +Cookies are small text files stored on your device when you visit websites. We use cookies to: +- Remember your preferences (e.g., cookie consent choice) +- Understand how you use our website (analytics) + +### 6.2 Types of Cookies We Use + +| Cookie Type | Purpose | Duration | Consent Required? | +|-------------|---------|----------|-------------------| +| **Strictly Necessary** | Essential for site functionality | Session | No | +| **Analytics** | Understand site usage patterns | 1 year | Yes | +| **Preferences** | Remember your settings | 1 year | Yes | + +### 6.3 Analytics + +We use [Vercel Analytics / Plausible / other] to understand how visitors interact with our Site. This service: +- [Collects anonymized usage data / Collects IP addresses] +- [Does not use cookies / Uses first-party cookies] +- Data is processed in [location] + +### 6.4 Managing Cookies + +You can manage cookies through: +- Our cookie consent banner (appears on first visit) +- Your browser settings +- Links at the bottom of our pages + +To opt-out of analytics, you can: +- Click "Reject" on our cookie consent banner +- Use browser extensions like uBlock Origin or Privacy Badger + +## 7. Data Sharing and Third Parties + +We may share your data with: + +### 7.1 Infrastructure Providers (Data Processors) + +| Provider | Service | Location | DPA | +|----------|---------|----------|-----| +| **netcup GmbH** | Web hosting infrastructure | Germany (EU) | Yes | +| **Cloudflare, Inc.** | CDN, security, DNS | Global (US company, EU processing) | Yes | +| **Vercel Inc.** | Analytics | US | Yes | + +### 7.2 We Never: +- Sell your personal data +- Share data with advertisers +- Transfer data without appropriate safeguards + +### 7.3 International Transfers + +Some of our service providers are based outside the EU/EEA. When we transfer data internationally, we ensure appropriate safeguards such as: +- EU Standard Contractual Clauses (SCCs) +- Data Processing Agreements +- Adequacy decisions where applicable + +## 8. Data Retention + +We retain your personal data only for as long as necessary: + +| Data Type | Retention Period | +|-----------|-----------------| +| Contact form submissions | 2 years | +| Newsletter subscriptions | Until you unsubscribe + 30 days | +| Analytics data | 14 months | +| Server logs | 14 days | + +## 9. Your Rights Under GDPR + +You have the following rights regarding your personal data: + +### 9.1 Right of Access (Art. 15) +Request a copy of your personal data we hold. + +### 9.2 Right to Rectification (Art. 16) +Request correction of inaccurate or incomplete data. + +### 9.3 Right to Erasure (Art. 17) +Request deletion of your data ("right to be forgotten"). + +### 9.4 Right to Restrict Processing (Art. 18) +Request limitation of how we process your data. + +### 9.5 Right to Data Portability (Art. 20) +Receive your data in a structured, commonly used format. + +### 9.6 Right to Object (Art. 21) +Object to processing based on legitimate interests, including profiling. + +### 9.7 Right to Withdraw Consent (Art. 7) +Withdraw consent at any time (does not affect prior lawful processing). + +### 9.8 How to Exercise Your Rights + +To exercise any of these rights, contact us at: +- Email: [CONTACT_EMAIL] +- Subject line: "GDPR Data Request - [Your Right]" + +We will respond within **30 days** of receiving your request. We may ask for identification to verify your identity. + +### 9.9 Right to Lodge a Complaint + +If you believe we have violated your data protection rights, you have the right to lodge a complaint with a supervisory authority. Since our hosting is in Germany, you may contact: + +**Landesbeauftragte fΓΌr den Datenschutz und die Informationsfreiheit Baden-WΓΌrttemberg** +Website: https://www.baden-wuerttemberg.datenschutz.de/ + +Or your local data protection authority. + +## 10. Security Measures + +We implement appropriate technical and organizational measures to protect your data: + +- **Encryption**: All data transmitted via HTTPS/TLS +- **Access Controls**: Limited access to personal data +- **Infrastructure Security**: ISO 27001 certified data centers (netcup/Anexia) +- **Regular Updates**: Security patches and updates applied promptly + +## 11. Children's Privacy + +Our Site is not intended for children under 16 years of age. We do not knowingly collect personal data from children. If you believe we have collected data from a child, please contact us immediately. + +## 12. Changes to This Privacy Policy + +We may update this Privacy Policy from time to time. We will notify you of any material changes by: +- Posting the new policy on this page +- Updating the "Last Updated" date +- [Sending an email notification for significant changes] + +## 13. Contact Us + +If you have any questions about this Privacy Policy or our data practices, please contact us: + +**Jeff Emmett** +Email: [CONTACT_EMAIL] +Website: [WEBSITE_URL] + +--- + +## Appendix A: Specific Processing Activities for [WEBSITE_NAME] + +### Data Processing Summary + +**Categories of Data Subjects:** +- [ ] Website visitors +- [ ] Newsletter subscribers +- [ ] Contact form users +- [ ] Customers/clients +- [ ] Other: _______________ + +**Categories of Personal Data:** +- [ ] Name +- [ ] Email address +- [ ] IP address (anonymized) +- [ ] Usage/analytics data +- [ ] Other: _______________ + +**Special Categories of Data (Art. 9):** +- [x] No special categories processed + +--- + +*This privacy policy template is provided for informational purposes. Consider consulting with a legal professional to ensure full compliance with applicable laws.* diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad2a250 --- /dev/null +++ b/README.md @@ -0,0 +1,221 @@ +# GDPR Compliance Kit + +A comprehensive toolkit for making your websites GDPR compliant. + +## Contents + +``` +gdpr-compliance-kit/ +β”œβ”€β”€ README.md # This file +β”œβ”€β”€ PRIVACY_POLICY_TEMPLATE.md # Customizable privacy policy +β”œβ”€β”€ NETCUP_DPA_ANNEX3_GUIDE.md # Guide for Netcup DPA form +β”œβ”€β”€ RECORDS_OF_PROCESSING_ACTIVITIES.md # ROPA template (Art. 30) +└── components/ + β”œβ”€β”€ CookieConsent.tsx # React cookie banner component + └── GDPRAnalytics.tsx # Consent-aware analytics wrapper +``` + +## Quick Start + +### 1. Add Cookie Consent to Your Site + +Copy the components to your Next.js project: + +```bash +cp components/CookieConsent.tsx your-project/components/ +cp components/GDPRAnalytics.tsx your-project/components/ +``` + +Add to your `app/layout.tsx`: + +```tsx +import { CookieConsent } from "@/components/CookieConsent"; +import { GDPRAnalytics } from "@/components/GDPRAnalytics"; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + + + ); +} +``` + +**Remove** the standard Analytics import from your layout: +```diff +- import { Analytics } from "@vercel/analytics/next"; +... +- ++ +``` + +### 2. Create Your Privacy Policy + +1. Copy `PRIVACY_POLICY_TEMPLATE.md` to your project +2. Replace all `[PLACEHOLDERS]` with your actual info: + - `[DATE]` - Current date + - `[WEBSITE_NAME]` - Your site name + - `[WEBSITE_URL]` - Your domain + - `[CONTACT_EMAIL]` - Your contact email +3. Review and customize based on your specific data processing +4. Add as a page at `/privacy` on your site + +### 3. Complete Netcup DPA + +1. Log into Netcup CCP +2. Go to Master Data β†’ Agreement on contract data processing +3. Follow `NETCUP_DPA_ANNEX3_GUIDE.md` to fill out the form +4. Submit the agreement +5. Save a copy for your records + +### 4. Maintain Records of Processing + +1. Customize `RECORDS_OF_PROCESSING_ACTIVITIES.md` with your specific activities +2. Store it securely (not publicly accessible) +3. Review and update annually or when processing changes + +## Component Details + +### CookieConsent.tsx + +Features: +- Three-tier consent (Necessary, Analytics, Preferences) +- Customizable appearance via Tailwind classes +- Stores consent in localStorage +- Provides hooks for checking consent status + +**Custom styling:** +The component uses Tailwind CSS classes. Customize by editing the className props. + +**Integration with other consent managers:** +The component stores consent in localStorage under `gdpr_consent`. You can read this from other parts of your app: + +```tsx +const consent = JSON.parse(localStorage.getItem('gdpr_consent') || '{}'); +if (consent.analytics) { + // Load analytics +} +``` + +### GDPRAnalytics.tsx + +Wraps Vercel Analytics and only loads it after consent: + +```tsx +// Instead of: +import { Analytics } from "@vercel/analytics/react"; + + +// Use: +import { GDPRAnalytics } from "@/components/GDPRAnalytics"; + +``` + +For Google Analytics, use `GDPRGoogleAnalytics`: +```tsx + +``` + +### ConsentGate Component + +Conditionally render content based on consent: + +```tsx +import { ConsentGate } from "@/components/CookieConsent"; + +function MyComponent() { + return ( + Analytics disabled

}> + +
+ ); +} +``` + +## Checklist + +### Technical Implementation +- [ ] Cookie consent banner added to all sites +- [ ] Analytics wrapped with consent check +- [ ] Privacy policy page created at `/privacy` +- [ ] Cookie settings accessible from footer +- [ ] Contact forms have privacy notice + +### Legal/Administrative +- [ ] Netcup DPA Annex 3 completed and submitted +- [ ] Records of Processing Activities created +- [ ] Privacy policy reviewed for accuracy +- [ ] Data retention periods documented +- [ ] Data breach response plan documented + +### Ongoing Maintenance +- [ ] Annual privacy policy review scheduled +- [ ] ROPA review scheduled +- [ ] Processor agreements reviewed +- [ ] Staff awareness training (if applicable) + +## Deployment Script + +To add GDPR compliance to all your sites at once: + +```bash +#!/bin/bash +# deploy-gdpr.sh + +SITES=( + "mycofi-earth-website" + "canvas-website" + # Add more sites +) + +for site in "${SITES[@]}"; do + echo "Adding GDPR components to $site..." + cp components/CookieConsent.tsx /opt/websites/$site/components/ + cp components/GDPRAnalytics.tsx /opt/websites/$site/components/ + echo "Done with $site" +done +``` + +## FAQ + +**Q: Do I need a cookie banner if I don't use cookies?** +A: If you use ANY analytics (even Vercel Analytics), you should have consent. Many analytics tools set cookies or use similar tracking technologies. + +**Q: What about Cloudflare's cookies?** +A: Cloudflare sets some strictly necessary cookies for security (like `__cf_bm`). These don't require consent but should be mentioned in your privacy policy. + +**Q: Do I need a DPO?** +A: Likely not. A Data Protection Officer is required only if: +- You're a public authority +- Your core activities involve large-scale monitoring +- You process special categories of data at large scale + +**Q: What if someone requests data deletion?** +A: You have 30 days to respond. Delete from: +1. Your databases +2. Backups (when they rotate) +3. Notify any processors (they should delete too) + +**Q: Is this enough for full GDPR compliance?** +A: This covers the major technical requirements. Full compliance also requires: +- Organizational measures (policies, training) +- Proper contracts with processors +- Breach response procedures +- Ongoing compliance monitoring + +Consider consulting a legal professional for your specific situation. + +## Resources + +- [GDPR Full Text](https://gdpr-info.eu/) +- [ICO Guide to GDPR](https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/) +- [Netcup DPA Information](https://www.netcup.de/dsgvo-gdpr-processing) +- [Cloudflare GDPR](https://www.cloudflare.com/gdpr/introduction/) + +## License + +This kit is provided as-is for informational purposes. Not legal advice. Consult with a legal professional for your specific compliance needs. diff --git a/RECORDS_OF_PROCESSING_ACTIVITIES.md b/RECORDS_OF_PROCESSING_ACTIVITIES.md new file mode 100644 index 0000000..6bf6448 --- /dev/null +++ b/RECORDS_OF_PROCESSING_ACTIVITIES.md @@ -0,0 +1,206 @@ +# Records of Processing Activities (ROPA) + +**Data Controller:** Jeff Emmett +**Last Updated:** [DATE] +**Version:** 1.0 + +This document fulfills the requirement under GDPR Article 30 to maintain records of processing activities. + +--- + +## Overview + +| Item | Details | +|------|---------| +| **Controller Name** | Jeff Emmett | +| **Controller Address** | 23 Birchpark Dr, L3M 4M9 Grimsby, Canada | +| **Contact Email** | [YOUR_EMAIL] | +| **Data Protection Officer** | Not required (< 250 employees, no large-scale processing) | +| **EU Representative** | Not required (processing not regular/large-scale) | + +--- + +## Processing Activity 1: Website Hosting & Analytics + +| Field | Details | +|-------|---------| +| **Activity Name** | Website Hosting and Analytics | +| **Purpose** | Hosting websites, collecting anonymized usage analytics to improve user experience | +| **Legal Basis** | Legitimate Interest (Art. 6(1)(f)) for basic hosting; Consent (Art. 6(1)(a)) for analytics | +| **Data Subjects** | Website visitors | +| **Personal Data Categories** | IP address (anonymized), browser type, pages visited, referrer URL, device type | +| **Special Categories** | None | +| **Data Sources** | Direct collection via website | +| **Recipients** | netcup GmbH (hosting), Cloudflare Inc (CDN), Vercel Inc (analytics) | +| **Third Country Transfers** | USA (Cloudflare, Vercel) - protected by SCCs/DPA | +| **Retention Period** | Server logs: 14 days; Analytics: 14 months | +| **Security Measures** | TLS encryption, access controls, ISO 27001 certified infrastructure | + +### Websites Covered: +- jeffemmett.com +- mycofi.earth +- bondingcurve.tech +- convictionvoting.xyz +- decolonizeti.me +- [Add all your domains] + +--- + +## Processing Activity 2: Newsletter Subscriptions + +| Field | Details | +|-------|---------| +| **Activity Name** | Newsletter Management | +| **Purpose** | Sending newsletters and updates to subscribers | +| **Legal Basis** | Consent (Art. 6(1)(a)) - explicit opt-in | +| **Data Subjects** | Newsletter subscribers | +| **Personal Data Categories** | Email address, name (optional), subscription date, open/click tracking | +| **Special Categories** | None | +| **Data Sources** | Direct collection via subscription forms | +| **Recipients** | Self-hosted (Listmonk on netcup infrastructure) | +| **Third Country Transfers** | None (self-hosted in Germany) | +| **Retention Period** | Until unsubscribe + 30 days | +| **Security Measures** | TLS encryption, authentication required, database encryption | + +### Consent Mechanism: +- Double opt-in required +- Clear unsubscribe link in every email +- Consent records stored with timestamp + +--- + +## Processing Activity 3: Contact Form Submissions + +| Field | Details | +|-------|---------| +| **Activity Name** | Contact Form Processing | +| **Purpose** | Responding to inquiries from website visitors | +| **Legal Basis** | Legitimate Interest (Art. 6(1)(f)) / Pre-contractual measures (Art. 6(1)(b)) | +| **Data Subjects** | People who submit contact forms | +| **Personal Data Categories** | Name, email address, message content | +| **Special Categories** | None | +| **Data Sources** | Direct submission via website forms | +| **Recipients** | Self-hosted email (or specify email provider) | +| **Third Country Transfers** | Depends on email provider | +| **Retention Period** | 2 years after last communication | +| **Security Measures** | TLS encryption, spam filtering | + +--- + +## Processing Activity 4: User Accounts (if applicable) + +| Field | Details | +|-------|---------| +| **Activity Name** | User Account Management | +| **Purpose** | Providing authenticated access to services | +| **Legal Basis** | Contract performance (Art. 6(1)(b)) | +| **Data Subjects** | Registered users | +| **Personal Data Categories** | Email, username, hashed password, account settings | +| **Special Categories** | None | +| **Data Sources** | User registration | +| **Recipients** | Self-hosted only | +| **Third Country Transfers** | None | +| **Retention Period** | Account lifetime + 30 days after deletion request | +| **Security Measures** | Password hashing (bcrypt), session management, 2FA optional | + +--- + +## Data Processors (Sub-processors) + +| Processor | Service | Location | DPA Signed | Contact | +|-----------|---------|----------|------------|---------| +| netcup GmbH | Web hosting infrastructure | Germany | Yes (online) | support@netcup.de | +| Cloudflare, Inc. | CDN, DNS, DDoS protection | USA (with EU options) | Yes (standard) | privacy@cloudflare.com | +| Vercel Inc. | Web analytics | USA | Yes (ToS) | privacy@vercel.com | + +--- + +## Technical and Organizational Measures (TOMs) + +### Confidentiality +- [x] TLS/SSL encryption for all websites +- [x] Access controls for server infrastructure +- [x] SSH key authentication (no password auth) +- [x] Firewall and network segmentation + +### Integrity +- [x] Regular backups +- [x] Version control for code +- [x] Audit logging + +### Availability +- [x] Redundant infrastructure +- [x] DDoS protection (Cloudflare) +- [x] Monitoring and alerting + +### Resilience +- [x] Disaster recovery procedures +- [x] Regular backup testing + +--- + +## Data Subject Rights Procedures + +### Access Requests (Art. 15) +1. Receive request via email +2. Verify identity +3. Compile data within 30 days +4. Provide data in machine-readable format + +### Erasure Requests (Art. 17) +1. Receive request via email +2. Verify identity +3. Delete from: databases, backups (when rotated), analytics +4. Confirm deletion within 30 days + +### Portability Requests (Art. 20) +1. Receive request via email +2. Verify identity +3. Export data as JSON/CSV +4. Provide within 30 days + +--- + +## Data Breach Response Plan + +### Detection +- Monitoring systems in place +- Log analysis for anomalies + +### Assessment (within 24 hours) +1. Identify scope of breach +2. Assess risk to data subjects +3. Document findings + +### Notification (within 72 hours if required) +1. Notify supervisory authority if risk to rights/freedoms +2. Notify affected individuals if high risk +3. Document all actions + +### Recovery +1. Contain breach +2. Remediate vulnerabilities +3. Review and update security measures + +--- + +## Review Schedule + +| Review Type | Frequency | Last Review | Next Review | +|-------------|-----------|-------------|-------------| +| ROPA Update | Annually | [DATE] | [DATE + 1 year] | +| Security Audit | Annually | [DATE] | [DATE + 1 year] | +| Processor Review | Annually | [DATE] | [DATE + 1 year] | +| Privacy Policy Review | Annually | [DATE] | [DATE + 1 year] | + +--- + +## Change Log + +| Date | Version | Changes | Author | +|------|---------|---------|--------| +| [DATE] | 1.0 | Initial creation | Jeff Emmett | + +--- + +*This document should be kept up to date and reviewed at least annually or whenever there are significant changes to processing activities.* diff --git a/backlog/config.yml b/backlog/config.yml new file mode 100644 index 0000000..68ed4bd --- /dev/null +++ b/backlog/config.yml @@ -0,0 +1,15 @@ +project_name: "GDPR Compliance Kit" +default_status: "To Do" +statuses: ["To Do", "In Progress", "Done"] +labels: [] +milestones: [] +date_format: yyyy-mm-dd +max_column_width: 20 +default_editor: "nvim" +auto_open_browser: true +default_port: 6420 +remote_operations: true +auto_commit: false +bypass_git_hooks: false +check_active_branches: true +active_branch_days: 30 diff --git a/backlog/tasks/task-high.1 - Deploy-GDPR-compliance-to-all-websites.md b/backlog/tasks/task-high.1 - Deploy-GDPR-compliance-to-all-websites.md new file mode 100644 index 0000000..ea8fca4 --- /dev/null +++ b/backlog/tasks/task-high.1 - Deploy-GDPR-compliance-to-all-websites.md @@ -0,0 +1,25 @@ +--- +id: task-high.1 +title: Deploy GDPR compliance to all websites +status: To Do +assignee: [] +created_date: '2025-12-11 18:04' +labels: [] +dependencies: [] +parent_task_id: task-high +--- + +## Description + + +Deploy cookie consent banners, privacy policies, and GDPR-compliant analytics to all hosted websites. Complete Netcup DPA Annex 3 form. + + +## Acceptance Criteria + +- [ ] #1 Cookie consent component deployed to all sites +- [ ] #2 Privacy policy pages created for each domain +- [ ] #3 Netcup DPA Annex 3 form completed and submitted +- [ ] #4 Analytics wrapped with consent checks +- [ ] #5 ROPA document stored securely + diff --git a/components/CookieConsent.tsx b/components/CookieConsent.tsx new file mode 100644 index 0000000..f5913b6 --- /dev/null +++ b/components/CookieConsent.tsx @@ -0,0 +1,275 @@ +"use client"; + +import { useState, useEffect } from "react"; +import Link from "next/link"; + +type ConsentState = { + necessary: boolean; + analytics: boolean; + preferences: boolean; + timestamp: string; +}; + +const CONSENT_COOKIE_NAME = "gdpr_consent"; +const CONSENT_VERSION = "1.0"; + +export function CookieConsent() { + const [showBanner, setShowBanner] = useState(false); + const [showDetails, setShowDetails] = useState(false); + const [consent, setConsent] = useState({ + necessary: true, // Always required + analytics: false, + preferences: false, + timestamp: "", + }); + + useEffect(() => { + // Check if consent has already been given + const savedConsent = localStorage.getItem(CONSENT_COOKIE_NAME); + if (savedConsent) { + try { + const parsed = JSON.parse(savedConsent); + setConsent(parsed); + // Apply saved consent settings + applyConsent(parsed); + } catch { + // Invalid consent data, show banner + setShowBanner(true); + } + } else { + // No consent yet, show banner + setShowBanner(true); + } + }, []); + + const applyConsent = (consentState: ConsentState) => { + // Enable/disable analytics based on consent + if (consentState.analytics) { + // Enable analytics (e.g., Vercel Analytics, Plausible, etc.) + window.localStorage.setItem("va_disabled", "false"); + // If using Google Analytics, you would enable it here + // window.gtag?.('consent', 'update', { analytics_storage: 'granted' }); + } else { + // Disable analytics + window.localStorage.setItem("va_disabled", "true"); + // window.gtag?.('consent', 'update', { analytics_storage: 'denied' }); + } + }; + + const saveConsent = (consentState: ConsentState) => { + const consentWithTimestamp = { + ...consentState, + timestamp: new Date().toISOString(), + version: CONSENT_VERSION, + }; + localStorage.setItem(CONSENT_COOKIE_NAME, JSON.stringify(consentWithTimestamp)); + setConsent(consentWithTimestamp); + applyConsent(consentWithTimestamp); + setShowBanner(false); + }; + + const acceptAll = () => { + saveConsent({ + necessary: true, + analytics: true, + preferences: true, + timestamp: "", + }); + }; + + const rejectAll = () => { + saveConsent({ + necessary: true, + analytics: false, + preferences: false, + timestamp: "", + }); + }; + + const saveCustom = () => { + saveConsent(consent); + }; + + if (!showBanner) return null; + + return ( +
+
+ {!showDetails ? ( + // Simple banner view +
+
+

We value your privacy

+

+ We use cookies to improve your experience and analyze site usage. + You can choose which cookies to accept.{" "} + + Learn more + +

+
+
+ + + +
+
+ ) : ( + // Detailed settings view +
+
+

Cookie Settings

+ +
+ +
+ {/* Necessary Cookies */} +
+
+
+

Strictly Necessary

+ Always Active +
+

+ Essential for the website to function properly. These cannot be disabled. +

+
+ +
+ + {/* Analytics Cookies */} +
+
+

Analytics

+

+ Help us understand how visitors interact with our website by collecting anonymous usage data. +

+
+ setConsent({ ...consent, analytics: e.target.checked })} + className="mt-1 h-4 w-4 rounded border-border cursor-pointer" + aria-label="Enable analytics cookies" + /> +
+ + {/* Preference Cookies */} +
+
+

Preferences

+

+ Remember your settings and preferences like theme choice and language. +

+
+ setConsent({ ...consent, preferences: e.target.checked })} + className="mt-1 h-4 w-4 rounded border-border cursor-pointer" + aria-label="Enable preference cookies" + /> +
+
+ +
+ + +
+
+ )} +
+
+ ); +} + +// Hook to check consent status from other components +export function useGDPRConsent() { + const [consent, setConsent] = useState(null); + + useEffect(() => { + const savedConsent = localStorage.getItem(CONSENT_COOKIE_NAME); + if (savedConsent) { + try { + setConsent(JSON.parse(savedConsent)); + } catch { + setConsent(null); + } + } + }, []); + + return { + hasConsented: consent !== null, + analyticsEnabled: consent?.analytics ?? false, + preferencesEnabled: consent?.preferences ?? false, + resetConsent: () => { + localStorage.removeItem(CONSENT_COOKIE_NAME); + window.location.reload(); + }, + }; +} + +// Component to conditionally render based on consent +export function ConsentGate({ + children, + type, + fallback, +}: { + children: React.ReactNode; + type: "analytics" | "preferences"; + fallback?: React.ReactNode; +}) { + const { analyticsEnabled, preferencesEnabled } = useGDPRConsent(); + + const hasConsent = type === "analytics" ? analyticsEnabled : preferencesEnabled; + + if (!hasConsent) { + return fallback || null; + } + + return <>{children}; +} diff --git a/components/GDPRAnalytics.tsx b/components/GDPRAnalytics.tsx new file mode 100644 index 0000000..194be04 --- /dev/null +++ b/components/GDPRAnalytics.tsx @@ -0,0 +1,122 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Analytics } from "@vercel/analytics/react"; + +const CONSENT_COOKIE_NAME = "gdpr_consent"; + +/** + * GDPR-Compliant Analytics Wrapper + * + * This component wraps Vercel Analytics (or any analytics provider) + * and only loads it if the user has consented to analytics cookies. + * + * Usage in your layout.tsx: + * + * import { GDPRAnalytics } from "@/components/GDPRAnalytics"; + * + * export default function RootLayout({ children }) { + * return ( + * + * + * {children} + * + * + * + * ); + * } + */ +export function GDPRAnalytics() { + const [analyticsEnabled, setAnalyticsEnabled] = useState(false); + + useEffect(() => { + const checkConsent = () => { + const savedConsent = localStorage.getItem(CONSENT_COOKIE_NAME); + if (savedConsent) { + try { + const parsed = JSON.parse(savedConsent); + setAnalyticsEnabled(parsed.analytics === true); + } catch { + setAnalyticsEnabled(false); + } + } + }; + + // Check on mount + checkConsent(); + + // Listen for consent changes + const handleStorageChange = (e: StorageEvent) => { + if (e.key === CONSENT_COOKIE_NAME) { + checkConsent(); + } + }; + + window.addEventListener("storage", handleStorageChange); + return () => window.removeEventListener("storage", handleStorageChange); + }, []); + + // Only render Analytics if user has consented + if (!analyticsEnabled) { + return null; + } + + return ; +} + +/** + * Alternative: Google Analytics 4 GDPR wrapper + * + * If you use Google Analytics instead of Vercel Analytics, + * use this component. + */ +export function GDPRGoogleAnalytics({ measurementId }: { measurementId: string }) { + const [analyticsEnabled, setAnalyticsEnabled] = useState(false); + + useEffect(() => { + const savedConsent = localStorage.getItem(CONSENT_COOKIE_NAME); + if (savedConsent) { + try { + const parsed = JSON.parse(savedConsent); + setAnalyticsEnabled(parsed.analytics === true); + } catch { + setAnalyticsEnabled(false); + } + } + }, []); + + useEffect(() => { + if (!analyticsEnabled) return; + + // Load Google Analytics script dynamically + const script = document.createElement("script"); + script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`; + script.async = true; + document.head.appendChild(script); + + // Initialize gtag + window.dataLayer = window.dataLayer || []; + function gtag(...args: unknown[]) { + window.dataLayer.push(args); + } + gtag("js", new Date()); + gtag("config", measurementId, { + anonymize_ip: true, // GDPR requirement + cookie_flags: "SameSite=None;Secure", + }); + + return () => { + document.head.removeChild(script); + }; + }, [analyticsEnabled, measurementId]); + + return null; +} + +// Type declaration for Google Analytics +declare global { + interface Window { + dataLayer: unknown[]; + gtag?: (...args: unknown[]) => void; + } +}