---
title: "v2.0.0-rc.5 — Policy Packs, Per-Policy GPC, Fallback Policies & Dev-Tools Improvements"
version: "2.0.0-rc.5"
date: 2026-03-25
description: "Release candidate introducing policy packs for regional consent resolution, per-policy GPC support, fallback policies, policy inspection, and developer-experience improvements."
tags:
  - release
  - rc
  - backend
  - react
  - nextjs
  - dev-tools
type: release
breaking: false
authors: [KayleeWilliams]
---
This RC introduces **policy packs** as the main addition in `v2.0.0-rc.5`. Instead of relying on implicit jurisdiction behavior, you can now define regional consent policies explicitly and let c15t resolve the active policy at runtime based on visitor location. `rc.5` also adds per-policy GPC handling, safer geo-failure fallbacks, policy-aware validation, and better tooling for inspecting how a policy was chosen.

## Highlights

* [Policy packs](#policy-packs) for declarative regional consent configuration
* [Per-policy GPC support](#per-policy-gpc) via `consent.gpc`
* [Fallback policies](#fallback-policies) as a safety net when geo-location fails
* [Policy fingerprints & re-prompting](#policy-fingerprints--re-prompting) to automatically resurface consent when policies change
* [Built-in presets](#built-in-presets) for Europe, California, Quebec, and more
* [Policy validation](#policy-validation) with `inspectPolicies()` for catching misconfigurations
* [Dev-tools policy panel](#dev-tools-policy-panel) for inspecting match resolution in real time
* [Snapshot tokens](#snapshot-tokens) for binding consent writes to the original policy decision
* [Naming & DX improvements](#naming--dx-improvements) across hosted/offline modes, i18n, and legal links
* [Bundled package docs](#bundled-package-docs) for version-matched local reference in `node_modules`

## New Features

### Policy Packs

Policy packs are ordered arrays of policies that control the consent model, categories, UI, and proof settings for each region. The backend resolves the right policy for each `/init` request based on the visitor's geo-location.

```ts
import { c15tInstance, policyPackPresets } from '@c15t/backend';

const c15t = c15tInstance({
  adapter,
  trustedOrigins: ['https://app.example.com'],
  policyPacks: [
    policyPackPresets.europeOptIn(),
    policyPackPresets.californiaOptOut(),
    policyPackPresets.worldNoBanner(),
  ],
});
```

Resolution follows a fixed priority: **region → country → fallback → default**. Within the same matcher type, first match wins by array order.

Each policy controls:

* **Consent model** — `opt-in`, `opt-out`, `none`, or `iab`
* **Categories** — which consent categories are in scope
* **Scope mode** — `strict` (block out-of-scope) or `permissive` (allow out-of-scope)
* **UI** — banner, dialog, or none, with per-surface action customization
* **Proof** — what to record (IP, user agent, language)
* **I18n** — language and message profile overrides

The `/init` response now includes `policy` (the resolved runtime config) and `policyDecision` (explainability metadata with `matchedBy`, fingerprint, and geo context).

> [Policy Packs Concepts](/docs/frameworks/react/concepts/policy-packs) · [Self-Host Guide](/docs/self-host/guides/policy-packs)

### Per-Policy GPC

Each policy can now individually opt in to respecting the [Global Privacy Control](https://globalprivacycontrol.org/) signal via `consent.gpc`:

```ts
{
  id: 'california',
  match: { regions: [{ country: 'US', region: 'CA' }] },
  consent: { model: 'opt-out', gpc: true },
}
```

When `gpc: true` and the visitor's browser sends a GPC signal, `marketing` and `measurement` categories are automatically denied during auto-granting. This moves GPC handling from a global behavior to an explicit per-policy setting, which is especially useful for opt-out regions like California. Opt-in policies can leave it disabled when GPC does not change the effective behavior.

### Fallback Policies

A new `match.fallback` flag provides a safety net when geo-location fails (no Cloudflare, Vercel, or AWS geo-headers). This is semantically distinct from `isDefault`:

* **`isDefault`** — catch-all for known locations that don't match specific policies ("rest of world")
* **`fallback`** — safety net for unknown locations when geo fails ("assume strictest")

```ts
{
  id: 'strict_fallback',
  match: { fallback: true, countries: [...EEA_COUNTRY_CODES] },
  consent: { model: 'opt-in' },
  ui: { mode: 'banner' },
}
```

The `europeOptIn()` and `europeIab()` presets include `fallback: true` by default, so the recommended setup protects EU users automatically — even when `disableGeoLocation` is enabled or CDN headers are missing.

### Policy Fingerprints & Re-Prompting

Each resolved policy now includes a **material policy fingerprint** — a SHA-256 hash of consent-affecting fields (model, categories, scope, allowed actions, proof settings). When you change a policy in a way that affects consent semantics, c15t automatically detects the fingerprint mismatch and re-prompts returning users.

Presentation-only changes (copy, layout, scroll lock, i18n) do not trigger re-prompts.

### Built-In Presets

Six presets cover the most common regional configurations:

| Preset               | Model     | Matches                           |
| -------------------- | --------- | --------------------------------- |
| `europeOptIn()`      | `opt-in`  | EEA + UK + geo fallback           |
| `europeIab()`        | `iab`     | EEA + UK + geo fallback (TCF 2.3) |
| `californiaOptIn()`  | `opt-in`  | US-CA region                      |
| `californiaOptOut()` | `opt-out` | US-CA region                      |
| `quebecOptIn()`      | `opt-in`  | CA-QC region                      |
| `worldNoBanner()`    | `none`    | default catch-all                 |

Matcher helpers (`policyMatchers.eu()`, `.eea()`, `.uk()`, `.iab()`, `.fallback()`, `.merge()`) make custom pack composition easy.

### Policy Validation

`inspectPolicies()` validates your policy pack before deployment, catching:

* Multiple default or fallback policies
* IAB policies without `iab.enabled: true` or with custom UI overrides
* Duplicate or missing policy IDs
* Policies with no matchers
* Missing default or fallback policies (warnings)
* Overlapping country/region matchers (warnings)

### Dev-Tools Policy Panel

The dev-tools panel now includes a full **match trace** showing the resolution path for every request: region → country → fallback → default, with MATCH/MISS indicators and the resolved policy ID. The location panel shows the active policy summary with `matchedBy` metadata.

### Snapshot Tokens

When `policySnapshot.signingKey` is configured, `/init` returns a signed JWT alongside the resolved policy. By default, invalid, expired, or missing snapshot tokens now cause `POST /subjects` to return `409 Conflict`. Deployments that prefer availability can opt into `policySnapshot.onValidationFailure = 'resolve_current'` to fall back to current write-time resolution instead.

### Bundled Package Docs

The four main c15t packages now ship version-matched Markdown docs inside the installed package itself:

* `node_modules/c15t/docs/`
* `node_modules/@c15t/react/docs/`
* `node_modules/@c15t/nextjs/docs/`
* `node_modules/@c15t/backend/docs/`

This includes generated reference material like prop and type tables, so local AI tools and developers can read the docs for the exact installed package version instead of relying on stale external docs or model knowledge.

## Improvements

* Renamed `c15t` mode to `hosted` and improved hosted vs offline documentation
* Renamed translation-facing APIs from `translations` to `i18n` across runtime types
* Improved legal links and overrides documentation
* Added v1→v2 codemods for renamed APIs and imports
* `@c15t/backend` API entrypoints flattened for better TypeScript DX
* Backend now exposes a base `/` root endpoint for health checks
* React compiler compatibility fixes
* Dark mode fix for `@c15t/ui`
* Dialog width customization and `disableAnimation` fixes
* `ConsentDialogLink` component added to `@c15t/react`
* Dev-tools UI improvements with persisted overrides and better diagnostics
* Updated Lithuanian translations
* CSS import path fixes (`.css` → `.js`)

<ContributorBlock usernames={["KayleeWilliams"]} title="Thank you to our contributors" />
