# Simple Privacy Policy Builder — simple-privacy-policy-builder

Generate a clear, jurisdiction-aware privacy policy from a guided form. Start from a vetted template and optionally let an LLM refine and localize the result for EU/UK/US state flags.

## Features
- Template-first policy generation with optional AI polish (low-temp, conservative edits)
- Split layout: form inputs (left) and live preview (right) with print-friendly view
- Rich inputs covering business type, data categories, cookies/analytics/ads, processors, GDPR bases, retention, children’s data, and jurisdiction flags (EU, UK, CA/CO/CT/VA/UT)
- History of runs (no auth) stored for quick recall; per-run HTML and Markdown outputs
- Exports: CSV and JSON of your history and generated policies
- Optional cron job to refresh/update policy templates
- Built-in rate limiting (10 requests/min per IP) and freemium quotas (5 free, 100 pro)
- Security hardening: strict CSP, CSRF protection, prepared statements, output escaping
- Light UI with blue accent; accessible and printer-friendly

## Requirements
- PHP 8.2+ with extensions: pdo_mysql, mbstring, json, openssl
- MySQL 8.x
- Web server (Nginx/Apache) or PHP built-in server for development
- OpenAI API key only if AI is enabled (optional)

## Quick Start

1) Create the database and tables (names are illustrative; adjust as needed):
```sql
CREATE DATABASE privacy_builder CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE privacy_builder;

CREATE TABLE templates (
  id INT AUTO_INCREMENT PRIMARY KEY,
  `key` VARCHAR(64) NOT NULL,
  `version` INT NOT NULL DEFAULT 1,
  locale VARCHAR(10) NOT NULL DEFAULT 'en',
  content_md LONGTEXT NOT NULL,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE KEY (`key`, locale)
);

CREATE TABLE policies (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  company VARCHAR(120) NOT NULL,
  website_url VARCHAR(200) NOT NULL,
  locale VARCHAR(10) NOT NULL DEFAULT 'en',
  jurisdictions JSON NULL,
  policy_md LONGTEXT NOT NULL,
  policy_html LONGTEXT NOT NULL
);

CREATE TABLE runs (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  ip VARBINARY(16) NOT NULL,
  ua VARCHAR(255) NULL,
  inputs JSON NOT NULL,
  used_ai TINYINT(1) NOT NULL DEFAULT 0,
  policy_id BIGINT NULL,
  duration_ms INT NULL,
  status VARCHAR(24) NOT NULL DEFAULT 'ok',
  INDEX (created_at),
  FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE SET NULL
);

CREATE TABLE rate_limits (
  `key` VARBINARY(64) NOT NULL,
  window_start TIMESTAMP NOT NULL,
  `count` INT NOT NULL DEFAULT 0,
  PRIMARY KEY (`key`, window_start)
);
```

2) Configure environment variables (e.g., a .env file or server env). See the config table below for all options.

3) Seed or refresh templates (optional but recommended). You can insert a base template into `templates` with key like "privacy_base" (locale "en"). Optionally enable the cron endpoint to refresh templates over time.

4) Run locally for development:
```bash
php -S 127.0.0.1:8000
```
Open http://127.0.0.1:8000

5) Deploy to production:
- Point your vhost to the app directory; route all requests to index.php
- Enforce HTTPS, HSTS, and the CSP noted below
- Set secure session cookies and environment variables via server config or a secret .env outside webroot

6) Optional cron for template refresh:
```bash
# every 12 hours
0 */12 * * * curl -fsS "https://your-app.example.com/cron/refresh-templates?token=${CRON_SECRET}" >/dev/null
```

## Configuration

Environment variables (prefixes APP_DB_* and OPENAI_* emphasized):

| Variable | Required | Default | Description |
|---|---|---|---|
| APP_ENV | no | production | deployment mode: production or development |
| APP_URL | yes | — | public base URL (used in canonical links, CSP) |
| APP_SECRET | yes | — | random 32+ char secret for CSRF nonces/hashing |
| APP_DB_HOST | yes | 127.0.0.1 | MySQL host |
| APP_DB_PORT | no | 3306 | MySQL port |
| APP_DB_NAME | yes | privacy_builder | MySQL database name |
| APP_DB_USER | yes | — | MySQL user |
| APP_DB_PASS | yes | — | MySQL password |
| APP_DB_CHARSET | no | utf8mb4 | DB charset |
| APP_RATE_LIMIT_PER_MIN | no | 10 | requests per minute per IP |
| APP_FREE_QUOTA | no | 5 | free run quota per IP/day (no auth) |
| APP_PRO_QUOTA | no | 100 | pro run quota per IP/day (toggle via server-side flag) |
| AI_ENABLED | no | true | globally enable/disable AI features |
| OPENAI_API_KEY | no | — | required only if AI is used |
| OPENAI_MODEL | no | gpt-4o-mini | model for refinement/localization |
| OPENAI_TEMPERATURE | no | 0.2 | sampling temperature for AI polish |
| OPENAI_API_BASE | no | https://api.openai.com/v1 | override API base if needed |
| CSP_ENABLED | no | true | enable Content Security Policy header |
| CSP_POLICY | no | — | override CSP; see Security section for default |
| CRON_SECRET | no | — | token required by cron endpoint |
| SESSION_NAME | no | PRIVSESS | custom session cookie name |
| SESSION_SECURE | no | true | send session cookie only over HTTPS |
| SESSION_SAMESITE | no | Lax | SameSite for session cookie |
| EXPORT_MAX_ROWS | no | 1000 | cap for CSV/JSON export rows |

Notes:
- With no auth, quotas and rate limits are applied per IP + session and are best-effort.
- AI is also controlled by the per-request “Use AI” checkbox input; both must be enabled (env + user) to call the API.

## How it works

- You fill in inputs: company_name, website_url, contact_email, business_type, effective_date, data_categories[], uses_cookies, uses_analytics (+ provider), uses_ads, processors, legal_basis[], data_retention, children, gdpr, uk_gdpr, ccpa_cpra, co_cpa, ct_ctdpa, va_vcdpa, ut_ucpa, opt_out_url, dpo_email, use_ai.
- The app merges inputs into a maintained Markdown template, rendering HTML for live preview and a print view.
- If AI is enabled and selected, the app sends template + inputs + jurisdiction flags to OpenAI for refinement and localization (temperature 0.2), then returns improved Markdown and HTML.
- Each run is stored in the database for history. You can export your history as CSV/JSON.

## Security measures

- Content Security Policy (CSP)
  - Default policy (if CSP_POLICY not set):
    - default-src 'self'
    - script-src 'self'
    - style-src 'self' 'unsafe-inline'
    - img-src 'self' data:
    - connect-src 'self'
    - frame-ancestors 'none'
    - base-uri 'self'
    - form-action 'self'
    - upgrade-insecure-requests
  - Adjust if serving assets from a CDN (add the CDN origin).
- CSRF protection
  - Per-request token stored in session and emitted as a hidden field; validated on POST. Double-submit cookie is used as a backup for non-cookieable contexts.
- Prepared statements and output escaping
  - All DB access uses PDO prepared statements; all dynamic HTML is escaped for its context (text, attribute, URL).
- Rate limiting and quotas
  - 10 requests/min/IP stored in rate_limits table; 429 Too Many Requests with Retry-After on excess.
  - Freemium quotas enforced per IP/day; configurable via APP_FREE_QUOTA/APP_PRO_QUOTA.
- Additional headers
  - Referrer-Policy: strict-origin-when-cross-origin
  - X-Content-Type-Options: nosniff
  - Permissions-Policy: geolocation=(), microphone=(), camera=()
  - HSTS recommended in production.
- Validation
  - Server-side validation and max lengths match inputs; URL/email formats checked; analytics provider guarded by allowlist.

## Exports

- History exports:
  - CSV: includes timestamp, company, website, used_ai, jurisdiction flags, summary fields
  - JSON: includes full inputs, rendered Markdown/HTML, and metadata
- Policy outputs:
  - Download Markdown or HTML per run
- Export size is capped by EXPORT_MAX_ROWS to protect performance.

## Optional AI behavior and fallback mode

- When enabled and requested by the user:
  - AI refines and localizes the template output using the provided flags and business context.
  - Temperature is low (0.2) for deterministic, compliance-friendly edits.
- Fallbacks:
  - If OPENAI_API_KEY is missing, request limit hit, or API error occurs, the app returns the template-based policy and surfaces a non-blocking notice.
  - No requests to AI are made unless both AI_ENABLED=true and the user checks “Use AI”.
- Data handling:
  - Only the necessary template, inputs, and flags are sent to the AI. Sensitive secrets are never included.

## UI and usage tips

- Split form/preview layout; fields autosave to the current session until generated.
- Print view removes chrome and uses a serif stack for readability.
- History lets you revisit, re-export, and print past policies.
- Not legal advice: this tool helps draft starter policies; consult counsel for your specific obligations.

## Acceptance checklist

- [ ] Title includes app name and slug
- [ ] Summary explains problem and outcome
- [ ] Features list includes form, preview, history, exports, AI, cron, rate limits, and security
- [ ] Requirements: PHP 8.2+, MySQL 8
- [ ] Quick start: DB creation, env vars, local run, and deploy guidance
- [ ] Config table documents APP_DB_* and OPENAI_* (and related) variables
- [ ] Security measures: CSP, CSRF, prepared statements, output escaping, and rate limiting
- [ ] Exports: CSV and JSON described
- [ ] Optional AI behavior and fallback mode documented
- [ ] License included

## License

MIT (c) Your Name Here. Replace with your organization’s preferred license if needed.