feat: initial public release

ConsentOS — a privacy-first cookie consent management platform.

Self-hosted, source-available alternative to OneTrust, Cookiebot, and
CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google
Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant
architecture with role-based access, configuration cascade
(system → org → group → site → region), dark-pattern detection in
the scanner, and a tamper-evident consent record audit trail.

This is the initial public release. Prior development history is
retained internally.

See README.md for the feature list, architecture overview, and
quick-start instructions. Licensed under the Elastic Licence 2.0 —
self-host freely; do not resell as a managed service.
This commit is contained in:
James Cottrill
2026-04-13 14:20:15 +00:00
commit fbf26453f2
341 changed files with 62807 additions and 0 deletions

68
.env.example Normal file
View File

@@ -0,0 +1,68 @@
# Application
APP_NAME=ConsentOS API
DEBUG=true
ENVIRONMENT=development
LOG_LEVEL=DEBUG
# Database
DATABASE_URL=postgresql+asyncpg://consentos:consentos@postgres:5432/consentos
DATABASE_ECHO=false
# Redis
REDIS_URL=redis://redis:6379/0
# JWT — generate with `openssl rand -hex 32` for production
JWT_SECRET_KEY=dev-secret-change-in-production
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30
# CDN — public URL where banner scripts are served. In dev the admin
# UI dog-foods the banner from its own /banner/ path, so localhost:5173
# works. In production point this at your real CDN (CloudFlare Pages,
# S3 + CloudFront, etc.) where consent-loader.js / consent-bundle.js
# are hosted.
CDN_BASE_URL=http://localhost:5173
# CORS — comma-separated list of allowed origins. Wildcards are refused
# at startup when ENVIRONMENT is not dev/test.
ALLOWED_ORIGINS=http://localhost:5173,http://localhost:8000
# Required to enable POST /api/v1/organisations/. Set to a strong random
# value (`openssl rand -hex 32`) to bootstrap your first organisation,
# then unset or rotate.
# ADMIN_BOOTSTRAP_TOKEN=
# Extra GeoIP country header — checked before the built-in list
# (cf-ipcountry, x-vercel-ip-country, x-appengine-country,
# x-country-code). Set this when you're behind a CDN or load
# balancer that uses a non-standard header, e.g. Google Cloud Load
# Balancer's x-gclb-country. Header names are case-insensitive.
# GEOIP_COUNTRY_HEADER=x-gclb-country
# Companion subdivision/state header. When both are set, the API
# pairs them to produce keys like "US-CA" or "GB-SCT" (ISO 3166-2).
# Only applies alongside GEOIP_COUNTRY_HEADER. Common names:
# cf-region-code (Cloudflare Enterprise)
# x-vercel-ip-country-region (Vercel)
# x-gclb-region (Google Cloud Load Balancer)
# cloudfront-viewer-country-region (AWS CloudFront functions)
# GEOIP_REGION_HEADER=x-gclb-region
# Local MaxMind GeoLite2-City database — used as a fallback when no
# CDN header is present. Download GeoLite2-City.mmdb from MaxMind
# (free, registration required) and mount it into the container,
# then point at it here. Without this, the API falls back to the
# external ip-api.com service which is rate-limited and should not
# be relied on in production.
# GEOIP_MAXMIND_DB_PATH=/data/GeoLite2-City.mmdb
# Initial admin bootstrap — on first startup, if the users table is
# empty and both credentials below are set, the API creates an
# organisation and an owner user so you can log in to the admin UI.
# Rotate the password via the admin UI after first login. Once any
# user exists this is a no-op, so the variables can safely remain set
# across restarts.
# INITIAL_ADMIN_EMAIL=admin@example.com
# INITIAL_ADMIN_PASSWORD=change-me-immediately
# INITIAL_ADMIN_FULL_NAME=Administrator
# INITIAL_ORG_NAME=Default Organisation
# INITIAL_ORG_SLUG=default