# Consenti — Full Documentation > Open-source, GDPR-style opt-in Cookie Consent & Consent Management Platform (CMP) toolkit. Zero external runtime dependencies. Apache 2.0 license. Consenti is a self-hosted, open-source consent toolkit built for developers — browser-first, with an optional backend for durable storage, dashboard authoring, and an audit log. It ships as two independent npm packages. It is not a hosted enterprise CMP like OneTrust or Cookiebot, which include dedicated legal/compliance teams and infrastructure Consenti does not replicate. - Source: https://github.com/santoshe61/consenti - npm UI: https://www.npmjs.com/package/@consenti/ui - npm API: https://www.npmjs.com/package/@consenti/api - Docs: https://consenti.dev/docs/getting-started/ - Summary (llms.txt): https://consenti.dev/llms.txt --- ## Packages - `@consenti/ui` — Browser widget. Renders the consent banner and preference modal. CSS is auto-injected at runtime — no separate CSS import needed. Handles GPC signal detection, cross-tab sync via BroadcastChannel, Google Consent Mode v2, GTM integration, i18n (locale resolution), and accessibility (focus trap, ARIA roles, targeting WCAG 2.x AA). - `@consenti/api` — Node.js backend module. Records consent to SQLite, MongoDB, PostgreSQL, or MySQL via a StorageAdapter interface. Serves a built-in Preact admin dashboard at `/consenti/`. Provides a REST API. Implements RBAC, multiple auth modes (local, OIDC, SAML), an append-only audit log (never deleted by Consenti, under any configuration), and a GDPR right-to-erasure endpoint. - `@consenti/scanner` — Local CLI, not published to npm. Crawls a site under none/reject-all/accept-all consent states and reports undeclared third-party trackers with confidence-scored classification, fully offline. --- ## Key Technical Facts - Runtime: Node 20+ (backend) / ES2020+ browsers (Chrome 80+, Firefox 74+, Safari 13.1+) - UI zero external runtime deps: `document.cookie`, `BroadcastChannel`, `crypto.subtle`, `fetch`, `localStorage` - API zero external runtime deps: `node:sqlite` (Node 22.5+), `node:crypto`, `node:fs`, `node:path` - Build: tsup (ESM + UMD for UI; CJS + ESM for API), Turborepo monorepo, npm workspaces - Security: IPs masked (last octet/80 bits zeroed) and salted before SHA-256 hashing, passwords via scrypt (native), JWT via HMAC-SHA256 (native), signed consent cookies using HMAC (server-side verification via `@consenti/api`'s `dataSigningHash`; standalone `@consenti/ui`-only signing is tamper-evidence, not tamper-proof — see SECURITY.md) - User IDs: `crypto.randomUUID()` — no PII stored - License: Apache 2.0 - Language: TypeScript strict mode throughout --- ## Compliance Coverage Status reflects how confidently Consenti tracks ongoing legal changes, not just whether code exists — same tiers as `ECOSYSTEM.md`. Maintained = actively tracked as the law changes. In development = supported today, rollout still being finished. Supported = code is complete and stable, but not tracked as closely as the Maintained tier. Partial = infrastructure provided; your own config/legal review finishes the specifics. Routing-only = the country is mapped to a compliance-group default, but not individually legal-maintained. | Regulation | Jurisdiction | Auto-resolves to | Status | |---|---|---|---| | GDPR | EU / EEA | `opt-in` | Maintained | | UK GDPR | United Kingdom | `opt-in` | Maintained | | CCPA / CPRA | California, USA | `opt-out-strict` | Maintained | | VCDPA / CPA / CTDPA | Virginia, Colorado, Connecticut | `opt-out` | Maintained | | LGPD | Brazil | `opt-in-brazil` | Maintained | | DPDPA | India | `opt-in-dpdpa` | In development — tracking India's phased 2025-2027 rules rollout | | PIPEDA / Law 25 | Canada / Quebec | `general-privacy-consent` (Quebec: `opt-in`) | Supported | | POPIA | South Africa | `general-privacy-consent` | Supported | | PDPA | Thailand | `opt-in` | Supported | | APPI | Japan | `general-privacy-consent` | Supported | | KVKK | Turkey | `opt-in` | Supported | | PIPL | China | `opt-in-china` | Routing-only — UX-template routing, not individually legal-maintained | | TCF v2.3 | IAB / Global | Vendor consent (`general-privacy-consent`) | Partial — spec-correct binary encoding needs `publisherCC` config + optional `@iabtechlabtcf/core`; simplified format otherwise. Registration only needed for programmatic/RTB ad monetization | | GPP (US National section) | Global (programmatic, US MSPA) | n/a | Partial — spec-correct binary encoding needs optional `@iabgpp/cmpapi`; simplified format otherwise. Same registration-only-for-programmatic-ads caveat as TCF | | COPPA | USA (children) | Age gate | Partial — age-gate widget included; parental verification requires your backend | | GPC | Global | Signal detection | Built-in feature, not a regulation | | Notice-only | Any | Informational | Built-in compliance group, not a regulation | --- ## Introduction Consenti is an open-source Consent Management Platform (CMP) with zero required runtime dependencies. It handles the complete consent lifecycle — banner display, preference management, GPC detection, GTM / Google Consent Mode v2 integration, and GDPR-style opt-in flows for GDPR, CCPA, CPRA, and other regulations at varying tiers of legal currency (see Compliance Coverage above). ### Frontend-only vs Full-stack The UI widget works entirely in the browser with no server needed. The backend is optional and adds server-side consent storage, a profile management dashboard, multi-tenant support, and audit logs. | | Frontend-only | With backend | |---|---|---| | Consent storage | Browser cookie or localStorage | Server database (SQLite, MongoDB, PostgreSQL) | | Profile management | Defined in code | Admin dashboard — no code changes to update copy | | Consent audit log | No | Yes — every record stored with timestamp and visitor ID | | Multi-site / multi-tenant | No | Yes | | Setup effort | One import, one constructor call | npm install @consenti/api, mounts onto your existing server | Tip: Start frontend-only. The switch to full-stack later is one config line: `api.enabled: true`. ### What happens on a first visit 1. The widget initialises and resolves the active profile (code-defined, API-fetched, or built-in default) 2. It reads any existing consent record from the browser cookie or localStorage 3. If no valid record exists → the banner appears 4. The user clicks Accept / Reject / Customize 5. Consent is saved to the browser and (if configured) POSTed to the backend 6. `consenti:consentSubmitted` fires with the full consent record 7. On every subsequent visit → the banner stays hidden unless consent has expired or the profile version changed --- ## UI Widget — Installation ### npm (recommended) ```bash npm install @consenti/ui ``` Import and initialise — no CSS import needed, styles are injected automatically: ```ts import { ConsentiSetup } from '@consenti/ui' ``` ### CDN / UMD (no build step) ```html ``` ### ESM in the browser (no bundler) ```html ``` ### CSS options | Approach | How | |---|---| | Auto-inject (default) | Nothing to do — ConsentiSetup injects a style tag on first render | | Preload CSS (optional, avoids FOUC) | `import '@consenti/ui/dist/index.css'` in your bundler entry | | Disable auto-inject (bring your own styles) | Set `core.disableCssTemplate: true` | | Token overrides only | Override `--consenti-*` CSS custom properties in your stylesheet | ### Package structure ``` @consenti/ui/ ├── dist/ │ ├── index.mjs ← ESM entry │ ├── index.umd.js ← UMD entry (CDN) │ ├── index.d.ts ← TypeScript types │ ├── index.css ← Default styles │ ├── react.mjs ← React hook │ ├── vue.mjs ← Vue composable │ └── angular.mjs ← Angular service ``` --- ## UI Widget — Quick Start (Frontend Only) ```bash npm install @consenti/ui ``` Pass an empty config and Consenti auto-detects the right compliance group from the browser's locale — GDPR for EU visitors, CCPA for California, etc. ```ts import { ConsentiSetup } from '@consenti/ui' // No CSS import needed — styles are auto-injected at runtime const widget = new ConsentiSetup({ compliance: { type: 'auto' }, // geo-based: GDPR for EU, CCPA for CA, etc. }) ``` ### Check consent before loading scripts ```ts widget.onReady((consent) => { if (consent.analytics) initAnalytics() if (consent.marketing) initAds() }) ``` ### Specific compliance mode ```ts // GDPR opt-in new ConsentiSetup({ compliance: { type: 'opt-in' } }) // CCPA opt-out new ConsentiSetup({ compliance: { type: 'opt-out' } }) // TCF v2.3 — enabled server-side via @consenti/api's compliance.tcf.enabled, not a widget compliance.type value new ConsentiSetup({ compliance: { type: 'general-privacy-consent' }, api: { enabled: true } }) ``` --- ## UI Widget — Quick Start (Frontend + Backend) ### Backend (Node.js / Express) ```bash npm install @consenti/api ``` ```ts import express from 'express' import { createConsenti } from '@consenti/api' const app = express() const consenti = createConsenti({ storage: { driver: 'node:sqlite', path: './consenti-data' }, auth: { mode: 'local', adminEmail: 'admin@example.com', adminPassword: process.env.CONSENTI_ADMIN_PASSWORD!, }, dashboard: true, }) app.use(consenti.router) app.listen(3000) // Admin dashboard → http://localhost:3000/consenti/ ``` ### Frontend — connect to backend ```ts import { ConsentiSetup } from '@consenti/ui' const widget = new ConsentiSetup({ compliance: { type: 'auto' }, api: { enabled: true, baseUrl: 'https://your-api.example.com' }, }) ``` --- ## UI Widget — Framework Integrations ### React ```ts import { useConsent } from '@consenti/ui/react' function CookieBanner() { const { consent, acceptAll, rejectAll } = useConsent() return consent ? null : } ``` ### Vue 3 ```ts import { useConsent } from '@consenti/ui/vue' const { consent, acceptAll, rejectAll } = useConsent() ``` ### Angular ```ts import { ConsentiService } from '@consenti/ui/angular' @Component({ ... }) export class AppComponent { constructor(private consenti: ConsentiService) { this.consenti.onReady(consent => { ... }) } } ``` ### Next.js App Router ```tsx // app/layout.tsx 'use client' import { useEffect } from 'react' import { ConsentiSetup } from '@consenti/ui' export default function RootLayout({ children }) { useEffect(() => { new ConsentiSetup({ compliance: { type: 'auto' } }) }, []) return {children} } ``` ### Nuxt ```ts // plugins/consenti.client.ts import { ConsentiSetup } from '@consenti/ui' export default defineNuxtPlugin(() => { new ConsentiSetup({ compliance: { type: 'auto' } }) }) ``` --- ## UI Widget — DOM Events All events are `CustomEvent`s fired on `document`: | Event | When | Detail | |---|---|---| | `consenti:bannerInitialized` | Widget ready, banner status known | `{ visible: boolean }` | | `consenti:bannerVisibility` | Banner shown or hidden | `{ visible: boolean }` | | `consenti:modalVisibility` | Preference modal shown or hidden | `{ visible: boolean }` | | `consenti:consentBeingSubmitted` | User clicked accept/reject (before write) | consent object | | `consenti:consentSubmitted` | Consent saved | full consent record | ```ts document.addEventListener('consenti:consentSubmitted', (e: CustomEvent) => { const { analytics, marketing, functional } = e.detail if (analytics) loadAnalytics() }) ``` --- ## UI Widget — Scripts, Actions & Triggers `ConsentScript` conditionally injects/removes a `