Consenti

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

ts
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

SectionDescriptionAccess
DashboardConsent overview, timeline chart, country breakdown, GPC statsAll users
ProfilesCreate / edit / copy / delete / activate / deactivate consent profilesAll users
Profile HistoryVersion history viewer — compare locale JSON per versionAll users
Cookie TemplatesReusable cookie/purpose definitionsAll users
UI TemplatesReusable banner + modal layout settingsAll users
ConsentsBrowse, filter, and export consent records; per-visitor historyAll users
VisitorsVisitor list with geographic data (IPs are SHA-256 hashed, never raw)All users
UsersAdmin user management with tenant scopingAll users
RolesRBAC roles and fine-grained permission assignmentAll users
SitesMulti-tenant site managementSuperadmin only
TCF VendorsIAB Global Vendor List (when tcf.enabled: true)All users
Audit LogImmutable log of all admin actionsAll users
Settings / APIAPI keys, branding, OpenAPI docsSuperadmin only
ℹ️The Sites and API/Settings menu items are hidden for non-superadmin users. TCF Vendors is hidden unless tcf.enabled: true.

Auth modes

ModeConfigDescription
localauth: { mode: 'local', adminEmail, adminPassword }Built-in email + password. Passwords hashed with scrypt via node:crypto. Brute-force protection built in.
jwtauth: { mode: 'jwt', jwtSecret }Validate externally issued JWTs (HS256). Your auth service issues tokens; Consenti verifies them.
oidcauth: { mode: 'oidc', oidc: { issuer, clientId, ... } }OpenID Connect Authorization Code + PKCE. Supports RS256 / ES256 from any OIDC provider (Auth0, Keycloak, Google).
samlauth: { mode: 'saml', saml: { issuer, entryPoint, cert, ... } }SAML 2.0 SP-initiated SSO. Requires samlify peer dep.
customauth: { 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

FieldTypeDescription
namestringHuman-readable profile name
defaultLocaleBCP 47 (searchable select)Locale used when visitor locale is unavailable; written as default.json
complianceGroupradio card gridRegulation group for geo-routing — selects one of the 8 built-in groups
gpcMode'ignore' | 'honor' | 'strict'GPC signal handling. Overrides the compliance group default.
darkModebooleanEnable dark mode in the consent banner
hidePoweredBybooleanHide "Powered by Consenti" badge
allowReceiptbooleanAllow visitors to download a PDF consent receipt
allowedOriginsstring[]Allowlisted domains for CORS on this profile's consent endpoints
complianceConfigRecord<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:

  1. Click Activate in the profile list or call POST /admin/profiles/:id/activate
  2. Consenti copies locale JSON files from ${profileId}/${version}/ to ${complianceGroup}/
  3. 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

nginx.conf
nginx
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;
}
ℹ️When behind a reverse proxy that terminates HTTPS, add the proxy IP to trustedProxies in createConsenti() so that IP hashing and rate limiting use the real client IP from X-Forwarded-For.