Backend — Admin Dashboard
Consenti ships a full admin SPA (Preact + Tailwind CSS) baked directly into the @consenti/api package. No separate deployment or build step is needed — it is served from dist/dashboard/ inside the package.
Enabling the dashboard
createConsenti({
storage: { driver: 'node:sqlite', path: './consenti-data' },
auth: { mode: 'local', adminEmail: '[email protected]', adminPassword: process.env.CONSENTI_ADMIN_PASSWORD! },
dashboard: true, // ← enables the SPA
})The dashboard is then available at /consenti/ (or your configured basePath).
Dashboard sections
| Section | Description | Access |
|---|---|---|
| Dashboard | Consent overview, timeline chart, country breakdown, GPC stats | All users |
| Profiles | Create / edit / copy / delete / activate / deactivate consent profiles | All users |
| Profile History | Version history viewer — compare locale JSON per version | All users |
| Cookie Templates | Reusable cookie/purpose definitions | All users |
| UI Templates | Reusable banner + modal layout settings | All users |
| Consents | Browse, filter, and export consent records; per-visitor history | All users |
| Visitors | Visitor list with geographic data (IPs are SHA-256 hashed, never raw) | All users |
| Users | Admin user management with tenant scoping | All users |
| Roles | RBAC roles and fine-grained permission assignment | All users |
| Sites | Multi-tenant site management | Superadmin only |
| TCF Vendors | IAB Global Vendor List (when tcf.enabled: true) | All users |
| Audit Log | Immutable log of all admin actions | All users |
| Settings / API | API keys, branding, OpenAPI docs | Superadmin only |
tcf.enabled: true.Auth modes
| Mode | Config | Description |
|---|---|---|
local | auth: { mode: 'local', adminEmail, adminPassword } | Built-in email + password. Passwords hashed with scrypt via node:crypto. Brute-force protection built in. |
jwt | auth: { mode: 'jwt', jwtSecret } | Validate externally issued JWTs (HS256). Your auth service issues tokens; Consenti verifies them. |
oidc | auth: { mode: 'oidc', oidc: { issuer, clientId, ... } } | OpenID Connect Authorization Code + PKCE. Supports RS256 / ES256 from any OIDC provider (Auth0, Keycloak, Google). |
saml | auth: { mode: 'saml', saml: { issuer, entryPoint, cert, ... } } | SAML 2.0 SP-initiated SSO. Requires samlify peer dep. |
custom | auth: { mode: 'custom', validateUser: async (req) => AdminUser | null } | Bring your own authentication. Consenti still enforces RBAC on the returned user's roles. |
Profile creation wizard
Creating a profile follows a 4-step wizard:
Step 1 — Profile metadata
| Field | Type | Description |
|---|---|---|
name | string | Human-readable profile name |
defaultLocale | BCP 47 (searchable select) | Locale used when visitor locale is unavailable; written as default.json |
complianceGroup | radio card grid | Regulation group for geo-routing — selects one of the 8 built-in groups |
gpcMode | 'ignore' | 'honor' | 'strict' | GPC signal handling. Overrides the compliance group default. |
darkMode | boolean | Enable dark mode in the consent banner |
hidePoweredBy | boolean | Hide "Powered by Consenti" badge |
allowReceipt | boolean | Allow visitors to download a PDF consent receipt |
allowedOrigins | string[] | Allowlisted domains for CORS on this profile's consent endpoints |
complianceConfig | Record<string, string> | Per-compliance extra config (e.g. DPDPA data fiduciary name). Only shown when the compliance group requires it. |
Step 2 — Cookie Template
Select or create a Cookie Template. Clicking Next calls POST /admin/profiles/validatewith the template's cookies and the chosen compliance group:
- Compliance errors (red): block advancing to Step 3 until resolved
- Compliance warnings (amber): show an acknowledgment checkbox — must be checked to proceed, but do not block saving
Step 3 — UI Template and locale authoring
Select or create a UI Template. New templates start blank; click Load Defaults in the amber callout to populate a starter structure.
- + Add locale opens a searchable BCP 47 locale selector
- Non-default locale tabs show the default locale's copy as placeholder text in heading, subheading, and category-heading inputs
- Import / Export: JSON exports all locale tabs as
locales.json; CSV exports the current locale tab; Import accepts both formats and auto-creates missing locale tabs
Step 4 — Content and readability
Enter localised copy (heading, body HTML) for the main banner, GPC banner, and preference modal categories. Inline advisory warnings appear when:
- Heading exceeds 80 characters
- Average sentence length in body exceeds 25 words
- Total word count in body exceeds 150 words
These are informational only — the profile can still be saved.
Profile activation and hot-serve
Profiles are inactive after creation. To serve them via geo-routing:
- Click Activate in the profile list or call
POST /admin/profiles/:id/activate - Consenti copies locale JSON files from
${profileId}/${version}/to${complianceGroup}/ - The static file route serves these files immediately — zero DB on the hot path
Only one profile per compliance group can be active. Activating a new profile automatically deactivates the existing one.
Profile version history
Every profile save creates an immutable snapshot. The version history page shows:
- Left panel: list of all versions with dates
- Right panel: prettified JSON for the selected version; locale switcher dropdown
Template save safety
When saving a Cookie Template or UI Template that is used by one or more profiles, a confirmation dialog lists the affected profiles. You must confirm before changes are applied.
- Deleting a Cookie Template used by active profiles is blocked (422) — deactivate the profiles first
- Removing cookies from a Cookie Template is blocked if any profile's compliance group requires those cookies
Serving behind a reverse proxy
location /consenti/ {
proxy_pass http://localhost:3001/consenti/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}trustedProxies in createConsenti() so that IP hashing and rate limiting use the real client IP from X-Forwarded-For.