@c15t/backend 2.0.0-rc.0 → 2.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/302.js +473 -0
- package/dist/583.js +540 -0
- package/dist/915.js +1771 -0
- package/dist/cache.cjs +5 -5
- package/dist/cache.js +4 -415
- package/dist/core.cjs +1356 -120
- package/dist/core.js +163 -1981
- package/dist/db/adapters/drizzle.cjs +1 -1
- package/dist/db/adapters/drizzle.js +1 -2
- package/dist/db/adapters/kysely.cjs +1 -1
- package/dist/db/adapters/kysely.js +1 -2
- package/dist/db/adapters/mongo.cjs +1 -1
- package/dist/db/adapters/mongo.js +1 -2
- package/dist/db/adapters/prisma.cjs +1 -1
- package/dist/db/adapters/prisma.js +1 -2
- package/dist/db/adapters/typeorm.cjs +1 -1
- package/dist/db/adapters/typeorm.js +1 -2
- package/dist/db/adapters.cjs +1 -1
- package/dist/db/migrator.cjs +1 -1
- package/dist/db/schema.cjs +43 -3
- package/dist/db/schema.js +35 -4
- package/dist/define-config.cjs +1 -1
- package/dist/edge.cjs +1106 -0
- package/dist/edge.js +190 -0
- package/dist/router.cjs +885 -123
- package/dist/router.js +1 -1507
- package/dist/{types.cjs → types/index.cjs} +1 -1
- package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
- package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
- package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
- package/{dist → dist-types}/cache/index.d.ts +0 -1
- package/{dist → dist-types}/cache/keys.d.ts +0 -1
- package/{dist → dist-types}/cache/types.d.ts +0 -1
- package/{dist → dist-types}/core.d.ts +8 -1
- package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
- package/dist-types/db/registry/consent-policy.d.ts +78 -0
- package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
- package/dist-types/db/registry/index.d.ts +118 -0
- package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
- package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
- package/{dist → dist-types}/db/registry/types.d.ts +1 -1
- package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
- package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
- package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
- package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -32
- package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
- package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +3 -3
- package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +7 -2
- package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
- package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +455 -28
- package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
- package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -3
- package/{dist → dist-types}/db/schema/index.d.ts +908 -86
- package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
- package/dist-types/define-config.d.ts +17 -0
- package/dist-types/edge/index.d.ts +5 -0
- package/dist-types/edge/init-handler.d.ts +40 -0
- package/dist-types/edge/resolve-consent.d.ts +80 -0
- package/dist-types/edge/types.d.ts +13 -0
- package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
- package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
- package/{dist → dist-types}/handlers/init/index.d.ts +2 -3
- package/dist-types/handlers/init/policy.d.ts +26 -0
- package/dist-types/handlers/init/resolve-init.d.ts +44 -0
- package/dist-types/handlers/init/translations.d.ts +48 -0
- package/dist-types/handlers/legal-document/current.handler.d.ts +11 -0
- package/dist-types/handlers/legal-document/snapshot.d.ts +39 -0
- package/dist-types/handlers/policy/snapshot.d.ts +99 -0
- package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
- package/{dist → dist-types}/handlers/subject/get.handler.d.ts +3 -2
- package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
- package/{dist → dist-types}/handlers/subject/list.handler.d.ts +3 -2
- package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
- package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
- package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
- package/{dist → dist-types}/init.d.ts +4 -7
- package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
- package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
- package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
- package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
- package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
- package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
- package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
- package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
- package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
- package/dist-types/policies/builder.d.ts +127 -0
- package/dist-types/policies/defaults.d.ts +2 -0
- package/dist-types/policies/matchers.d.ts +3 -0
- package/{dist → dist-types}/router.d.ts +0 -1
- package/{dist → dist-types}/routes/consent.d.ts +0 -1
- package/{dist → dist-types}/routes/index.d.ts +1 -1
- package/{dist → dist-types}/routes/init.d.ts +0 -1
- package/dist-types/routes/legal-document.d.ts +7 -0
- package/{dist → dist-types}/routes/status.d.ts +0 -1
- package/{dist → dist-types}/routes/subject.d.ts +0 -1
- package/{dist → dist-types}/types/api.d.ts +0 -1
- package/dist-types/types/index.d.ts +464 -0
- package/dist-types/utils/background.d.ts +6 -0
- package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
- package/{dist → dist-types}/utils/env.d.ts +0 -1
- package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
- package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
- package/{dist → dist-types}/utils/logger.d.ts +0 -1
- package/{dist → dist-types}/utils/metrics.d.ts +0 -1
- package/dist-types/version.d.ts +1 -0
- package/docs/README.md +49 -0
- package/docs/api/configuration.md +208 -0
- package/docs/api/endpoints.md +211 -0
- package/docs/guides/caching.md +85 -0
- package/docs/guides/database-setup.md +128 -0
- package/docs/guides/edge-deployment.md +251 -0
- package/docs/guides/framework-integration.md +142 -0
- package/docs/guides/iab-tcf.md +89 -0
- package/docs/guides/observability.md +96 -0
- package/docs/guides/policy-packs.md +396 -0
- package/docs/quickstart.md +129 -0
- package/package.json +53 -39
- package/.turbo/turbo-build.log +0 -49
- package/CHANGELOG.md +0 -89
- package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
- package/dist/cache/adapters/index.d.ts.map +0 -1
- package/dist/cache/adapters/memory.d.ts.map +0 -1
- package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
- package/dist/cache/gvl-resolver.d.ts.map +0 -1
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/keys.d.ts.map +0 -1
- package/dist/cache/types.d.ts.map +0 -1
- package/dist/core.d.ts.map +0 -1
- package/dist/db/adapters/drizzle.d.ts +0 -2
- package/dist/db/adapters/drizzle.d.ts.map +0 -1
- package/dist/db/adapters/index.d.ts +0 -2
- package/dist/db/adapters/index.d.ts.map +0 -1
- package/dist/db/adapters/kysely.d.ts +0 -2
- package/dist/db/adapters/kysely.d.ts.map +0 -1
- package/dist/db/adapters/mongo.d.ts +0 -2
- package/dist/db/adapters/mongo.d.ts.map +0 -1
- package/dist/db/adapters/prisma.d.ts +0 -2
- package/dist/db/adapters/prisma.d.ts.map +0 -1
- package/dist/db/adapters/typeorm.d.ts +0 -2
- package/dist/db/adapters/typeorm.d.ts.map +0 -1
- package/dist/db/migrator/index.d.ts.map +0 -1
- package/dist/db/registry/consent-policy.d.ts +0 -23
- package/dist/db/registry/consent-policy.d.ts.map +0 -1
- package/dist/db/registry/consent-purpose.d.ts.map +0 -1
- package/dist/db/registry/domain.d.ts.map +0 -1
- package/dist/db/registry/index.d.ts +0 -57
- package/dist/db/registry/index.d.ts.map +0 -1
- package/dist/db/registry/subject.d.ts.map +0 -1
- package/dist/db/registry/types.d.ts.map +0 -1
- package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
- package/dist/db/registry/utils.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
- package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
- package/dist/db/schema/index.d.ts.map +0 -1
- package/dist/db/tenant-scope.d.ts.map +0 -1
- package/dist/define-config.d.ts +0 -5
- package/dist/define-config.d.ts.map +0 -1
- package/dist/handlers/consent/check.handler.d.ts.map +0 -1
- package/dist/handlers/consent/index.d.ts +0 -12
- package/dist/handlers/consent/index.d.ts.map +0 -1
- package/dist/handlers/init/geo.d.ts.map +0 -1
- package/dist/handlers/init/index.d.ts.map +0 -1
- package/dist/handlers/init/translations.d.ts +0 -28
- package/dist/handlers/init/translations.d.ts.map +0 -1
- package/dist/handlers/status/index.d.ts +0 -7
- package/dist/handlers/status/index.d.ts.map +0 -1
- package/dist/handlers/status/status.handler.d.ts.map +0 -1
- package/dist/handlers/subject/get.handler.d.ts.map +0 -1
- package/dist/handlers/subject/index.d.ts +0 -10
- package/dist/handlers/subject/index.d.ts.map +0 -1
- package/dist/handlers/subject/list.handler.d.ts.map +0 -1
- package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
- package/dist/handlers/subject/post.handler.d.ts.map +0 -1
- package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/middleware/auth/index.d.ts.map +0 -1
- package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
- package/dist/middleware/cors/cors.d.ts.map +0 -1
- package/dist/middleware/cors/index.d.ts +0 -30
- package/dist/middleware/cors/index.d.ts.map +0 -1
- package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
- package/dist/middleware/cors/process-cors.d.ts.map +0 -1
- package/dist/middleware/openapi/config.d.ts.map +0 -1
- package/dist/middleware/openapi/handlers.d.ts.map +0 -1
- package/dist/middleware/openapi/index.d.ts +0 -12
- package/dist/middleware/openapi/index.d.ts.map +0 -1
- package/dist/middleware/process-ip/index.d.ts.map +0 -1
- package/dist/router.d.ts.map +0 -1
- package/dist/routes/consent.d.ts.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/init.d.ts.map +0 -1
- package/dist/routes/status.d.ts.map +0 -1
- package/dist/routes/subject.d.ts.map +0 -1
- package/dist/types/api.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -255
- package/dist/types/index.d.ts.map +0 -1
- package/dist/utils/create-telemetry-options.d.ts.map +0 -1
- package/dist/utils/env.d.ts.map +0 -1
- package/dist/utils/extract-error-message.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/instrumentation.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/metrics.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/knip.json +0 -31
- package/rslib.config.ts +0 -93
- package/src/cache/adapters/cloudflare-kv.ts +0 -71
- package/src/cache/adapters/index.ts +0 -22
- package/src/cache/adapters/memory.ts +0 -111
- package/src/cache/adapters/upstash-redis.ts +0 -113
- package/src/cache/gvl-resolver.ts +0 -289
- package/src/cache/index.ts +0 -34
- package/src/cache/keys.ts +0 -68
- package/src/cache/types.ts +0 -66
- package/src/core.ts +0 -368
- package/src/db/migrator/index.ts +0 -80
- package/src/db/registry/consent-policy.test.ts +0 -451
- package/src/db/registry/consent-policy.ts +0 -82
- package/src/db/registry/consent-purpose.test.ts +0 -428
- package/src/db/registry/consent-purpose.ts +0 -61
- package/src/db/registry/domain.test.ts +0 -445
- package/src/db/registry/domain.ts +0 -91
- package/src/db/registry/index.ts +0 -14
- package/src/db/registry/subject.test.ts +0 -388
- package/src/db/registry/subject.ts +0 -129
- package/src/db/registry/types.ts +0 -10
- package/src/db/registry/utils/generate-id.test.ts +0 -216
- package/src/db/registry/utils/generate-id.ts +0 -133
- package/src/db/registry/utils.ts +0 -133
- package/src/db/schema/1.0.0/audit-log.ts +0 -15
- package/src/db/schema/1.0.0/consent-policy.ts +0 -14
- package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
- package/src/db/schema/1.0.0/consent-record.ts +0 -10
- package/src/db/schema/1.0.0/consent.ts +0 -20
- package/src/db/schema/1.0.0/domain.ts +0 -12
- package/src/db/schema/1.0.0/index.ts +0 -48
- package/src/db/schema/1.0.0/subject.ts +0 -12
- package/src/db/schema/2.0.0/audit-log.ts +0 -18
- package/src/db/schema/2.0.0/consent-policy.ts +0 -28
- package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
- package/src/db/schema/2.0.0/consent.ts +0 -26
- package/src/db/schema/2.0.0/domain.ts +0 -12
- package/src/db/schema/2.0.0/index.ts +0 -47
- package/src/db/schema/2.0.0/subject.ts +0 -14
- package/src/db/schema/index.ts +0 -15
- package/src/db/tenant-scope.test.ts +0 -750
- package/src/db/tenant-scope.ts +0 -103
- package/src/define-config.ts +0 -5
- package/src/handlers/consent/check.handler.ts +0 -126
- package/src/handlers/init/geo.test.ts +0 -317
- package/src/handlers/init/geo.ts +0 -195
- package/src/handlers/init/index.test.ts +0 -205
- package/src/handlers/init/index.ts +0 -114
- package/src/handlers/init/translations.test.ts +0 -121
- package/src/handlers/init/translations.ts +0 -72
- package/src/handlers/status/status.handler.test.ts +0 -155
- package/src/handlers/status/status.handler.ts +0 -51
- package/src/handlers/subject/get.handler.ts +0 -93
- package/src/handlers/subject/list.handler.ts +0 -93
- package/src/handlers/subject/patch.handler.ts +0 -122
- package/src/handlers/subject/post.handler.test.ts +0 -294
- package/src/handlers/subject/post.handler.ts +0 -254
- package/src/handlers/utils/consent-enrichment.test.ts +0 -380
- package/src/handlers/utils/consent-enrichment.ts +0 -218
- package/src/init.test.ts +0 -126
- package/src/init.ts +0 -87
- package/src/middleware/auth/index.ts +0 -11
- package/src/middleware/auth/validate-api-key.test.ts +0 -86
- package/src/middleware/auth/validate-api-key.ts +0 -107
- package/src/middleware/cors/cors.test.ts +0 -135
- package/src/middleware/cors/cors.ts +0 -186
- package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
- package/src/middleware/cors/is-origin-trusted.ts +0 -130
- package/src/middleware/cors/process-cors.ts +0 -91
- package/src/middleware/openapi/config.ts +0 -29
- package/src/middleware/openapi/handlers.ts +0 -34
- package/src/middleware/process-ip/index.test.ts +0 -195
- package/src/middleware/process-ip/index.ts +0 -199
- package/src/router.ts +0 -15
- package/src/routes/consent.ts +0 -52
- package/src/routes/index.ts +0 -10
- package/src/routes/init.ts +0 -102
- package/src/routes/status.ts +0 -46
- package/src/routes/subject.ts +0 -152
- package/src/types/api.ts +0 -48
- package/src/types/index.ts +0 -288
- package/src/utils/create-telemetry-options.test.ts +0 -302
- package/src/utils/create-telemetry-options.ts +0 -229
- package/src/utils/env.ts +0 -84
- package/src/utils/extract-error-message.ts +0 -21
- package/src/utils/instrumentation.test.ts +0 -185
- package/src/utils/instrumentation.ts +0 -196
- package/src/utils/logger.ts +0 -41
- package/src/utils/metrics.test.ts +0 -323
- package/src/utils/metrics.ts +0 -402
- package/src/utils/telemetry-pii.test.ts +0 -325
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -28
- /package/dist/{types.js → types/index.js} +0 -0
- /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
- /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
- /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
- /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
- /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
- /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
- /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Policy Packs
|
|
3
|
+
description: Configure regional policy packs on the backend and return deterministic runtime policy decisions from /init.
|
|
4
|
+
---
|
|
5
|
+
Policy packs are the backend source of truth for regional consent behavior. You pass them to `c15tInstance({ policyPacks })`, and c15t resolves a normalized runtime policy for each `/init` request based on the visitor's location.
|
|
6
|
+
|
|
7
|
+
## Why Backend Packs
|
|
8
|
+
|
|
9
|
+
* Resolve policies from real request geo data (country/region) — no client-side guessing
|
|
10
|
+
* Centralize consent behavior for all clients (web, mobile, SDK)
|
|
11
|
+
* Issue signed `policySnapshotToken` values so later consent writes stay aligned with the original decision
|
|
12
|
+
* Return explainability metadata (`policyDecision`) for audit and debugging
|
|
13
|
+
* Keep frontend packages thin by pushing legal/regional logic to one place
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```ts title="lib/c15t.ts"
|
|
18
|
+
import { c15tInstance, policyPackPresets } from '@c15t/backend';
|
|
19
|
+
|
|
20
|
+
export const c15t = c15tInstance({
|
|
21
|
+
adapter,
|
|
22
|
+
trustedOrigins: ['https://app.example.com'],
|
|
23
|
+
policyPacks: [
|
|
24
|
+
policyPackPresets.europeOptIn(),
|
|
25
|
+
policyPackPresets.californiaOptOut(),
|
|
26
|
+
policyPackPresets.worldNoBanner(),
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That gives you Europe opt-in, California opt-out, and silent everywhere else — with zero custom config.
|
|
32
|
+
|
|
33
|
+
## Custom Pack
|
|
34
|
+
|
|
35
|
+
For full control, define your own policies:
|
|
36
|
+
|
|
37
|
+
```ts title="lib/c15t.ts"
|
|
38
|
+
import {
|
|
39
|
+
c15tInstance,
|
|
40
|
+
EEA_COUNTRY_CODES,
|
|
41
|
+
policyBuilder,
|
|
42
|
+
UK_COUNTRY_CODES,
|
|
43
|
+
} from '@c15t/backend';
|
|
44
|
+
|
|
45
|
+
export const c15t = c15tInstance({
|
|
46
|
+
adapter,
|
|
47
|
+
trustedOrigins: ['https://app.example.com'],
|
|
48
|
+
policyPacks: policyBuilder.createPackWithDefault(
|
|
49
|
+
[
|
|
50
|
+
{
|
|
51
|
+
id: 'eu_opt_in',
|
|
52
|
+
countries: [...EEA_COUNTRY_CODES, ...UK_COUNTRY_CODES],
|
|
53
|
+
model: 'opt-in',
|
|
54
|
+
expiryDays: 365,
|
|
55
|
+
scopeMode: 'strict',
|
|
56
|
+
categories: ['necessary', 'measurement', 'marketing'],
|
|
57
|
+
preselectedCategories: ['necessary'],
|
|
58
|
+
uiMode: 'banner',
|
|
59
|
+
banner: {
|
|
60
|
+
allowedActions: ['accept', 'reject', 'customize'],
|
|
61
|
+
primaryActions: ['accept', 'customize'],
|
|
62
|
+
uiProfile: 'compact',
|
|
63
|
+
},
|
|
64
|
+
dialog: {
|
|
65
|
+
allowedActions: ['accept', 'reject', 'customize'],
|
|
66
|
+
},
|
|
67
|
+
i18n: { messageProfile: 'eu' },
|
|
68
|
+
proof: { storeIp: true, storeUserAgent: true, storeLanguage: true },
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'ca_opt_out',
|
|
72
|
+
regions: [{ country: 'US', region: 'CA' }],
|
|
73
|
+
model: 'opt-out',
|
|
74
|
+
expiryDays: 365,
|
|
75
|
+
uiMode: 'none',
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
{
|
|
79
|
+
id: 'default_world',
|
|
80
|
+
model: 'none',
|
|
81
|
+
uiMode: 'none',
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Builder Helpers
|
|
88
|
+
|
|
89
|
+
The backend exports three helpers for assembling packs:
|
|
90
|
+
|
|
91
|
+
|Helper|Purpose|
|
|
92
|
+
|--|--|
|
|
93
|
+
|`policyBuilder.create()`|Build a single `PolicyConfig`|
|
|
94
|
+
|`policyBuilder.createPack()`|Build an ordered array of policies|
|
|
95
|
+
|`policyBuilder.createPackWithDefault()`|Build a pack with a guaranteed default fallback|
|
|
96
|
+
|
|
97
|
+
The builder normalizes matcher input into the `PolicyConfig` shape and strips empty fields from the final payload. You can also use the raw `PolicyConfig` objects directly — the builder is a convenience, not a requirement.
|
|
98
|
+
|
|
99
|
+
## Resolution Flow
|
|
100
|
+
|
|
101
|
+
**Simplified**
|
|
102
|
+
|
|
103
|
+
1. **`GET /init` arrives** — c15t reads geo from request headers (country, region, jurisdiction)
|
|
104
|
+
2. **Resolve policy** — walks the pack in priority order: region → country → fallback (geo failure) → default
|
|
105
|
+
3. **Compute fingerprint** — generates a stable SHA-256 hash of the resolved policy
|
|
106
|
+
4. **Sign snapshot** — if `policySnapshot.signingKey` is configured, issues a JWT containing the decision
|
|
107
|
+
5. **Return response** — sends `policy`, `policyDecision`, and optional `policySnapshotToken` to the client
|
|
108
|
+
6. **Consent write** — when the user consents, `POST /subjects` validates the snapshot token and creates the consent record linked to the runtime policy decision
|
|
109
|
+
|
|
110
|
+
**Sequence Diagram**
|
|
111
|
+
|
|
112
|
+
```mermaid
|
|
113
|
+
sequenceDiagram
|
|
114
|
+
participant Client as Client
|
|
115
|
+
participant Init as GET /init
|
|
116
|
+
participant Resolver as Policy Resolver
|
|
117
|
+
participant DB as Database
|
|
118
|
+
participant Subjects as POST /subjects
|
|
119
|
+
|
|
120
|
+
Client->>Init: Request (geo headers)
|
|
121
|
+
Init->>Resolver: resolvePolicyDecision(pack, geo)
|
|
122
|
+
Resolver-->>Init: ResolvedPolicy + fingerprint
|
|
123
|
+
|
|
124
|
+
opt policySnapshot.signingKey configured
|
|
125
|
+
Init->>Init: Sign JWT (policySnapshotToken)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Init-->>Client: policy + policyDecision + token
|
|
129
|
+
|
|
130
|
+
Note over Client: User interacts with consent UI
|
|
131
|
+
|
|
132
|
+
Client->>Subjects: Consent + policySnapshotToken
|
|
133
|
+
Subjects->>Subjects: Verify JWT signature + expiry
|
|
134
|
+
|
|
135
|
+
alt Valid snapshot token
|
|
136
|
+
Subjects->>Subjects: Use token's policy decision
|
|
137
|
+
else Invalid or missing token
|
|
138
|
+
Subjects->>Resolver: Write-time resolvePolicyDecision()
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
Subjects->>DB: findOrCreate runtimePolicyDecision (deduped)
|
|
142
|
+
Subjects->>DB: Create consent record
|
|
143
|
+
Subjects-->>Client: Consent response
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Matching Rules
|
|
147
|
+
|
|
148
|
+
Backend resolution order is fixed:
|
|
149
|
+
|
|
150
|
+
1. **Region** — most specific (e.g., US-CA, CA-QC)
|
|
151
|
+
2. **Country** — (e.g., US, DE, JP)
|
|
152
|
+
3. **Fallback** — safety net when geo-location fails (no country detected). Only checked when `countryCode` is `null`.
|
|
153
|
+
4. **Default** — catch-all for known locations that don't match any specific policy
|
|
154
|
+
|
|
155
|
+
Within the same matcher type, first match wins by array order. c15t also enforces:
|
|
156
|
+
|
|
157
|
+
* Unique policy IDs
|
|
158
|
+
* At most one default policy
|
|
159
|
+
* At most one fallback policy
|
|
160
|
+
* `iab.enabled: true` when any policy uses `model: 'iab'`
|
|
161
|
+
* No custom `ui.*` overrides for IAB policies
|
|
162
|
+
|
|
163
|
+
Use `inspectPolicies()` to surface overlapping matchers and other warnings before deployment.
|
|
164
|
+
|
|
165
|
+
## Validating Your Pack
|
|
166
|
+
|
|
167
|
+
`inspectPolicies()` checks your policy pack for errors and warnings before deployment:
|
|
168
|
+
|
|
169
|
+
```ts title="scripts/validate-policies.ts"
|
|
170
|
+
import { inspectPolicies, policyPackPresets } from '@c15t/backend';
|
|
171
|
+
|
|
172
|
+
const pack = [
|
|
173
|
+
policyPackPresets.europeOptIn(),
|
|
174
|
+
policyPackPresets.californiaOptOut(),
|
|
175
|
+
policyPackPresets.worldNoBanner(),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
const result = inspectPolicies(pack, { iabEnabled: false });
|
|
179
|
+
|
|
180
|
+
if (result.errors.length > 0) {
|
|
181
|
+
console.error('Policy errors:', result.errors);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (result.warnings.length > 0) {
|
|
186
|
+
console.warn('Policy warnings:', result.warnings);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Common errors caught:
|
|
191
|
+
|
|
192
|
+
* Multiple default policies
|
|
193
|
+
* Multiple fallback policies
|
|
194
|
+
* IAB policies without `iab.enabled: true`
|
|
195
|
+
* IAB policies with custom `ui.*` overrides or `preselectedCategories`
|
|
196
|
+
* Duplicate or missing policy IDs
|
|
197
|
+
* Policies with no matchers and not marked as default or fallback
|
|
198
|
+
|
|
199
|
+
Common warnings:
|
|
200
|
+
|
|
201
|
+
* No default policy configured
|
|
202
|
+
* No fallback policy configured (geo-location failures will have no active policy)
|
|
203
|
+
* Overlapping country or region matchers across policies
|
|
204
|
+
|
|
205
|
+
## Translation Profiles
|
|
206
|
+
|
|
207
|
+
Policies integrate with backend i18n profiles through `i18n.messageProfile`:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const c15t = c15tInstance({
|
|
211
|
+
adapter,
|
|
212
|
+
trustedOrigins: ['https://app.example.com'],
|
|
213
|
+
i18n: {
|
|
214
|
+
defaultProfile: 'default',
|
|
215
|
+
messages: {
|
|
216
|
+
default: {
|
|
217
|
+
translations: {
|
|
218
|
+
en: { cookieBanner: { title: 'We use cookies' } },
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
eu: {
|
|
222
|
+
translations: {
|
|
223
|
+
en: { cookieBanner: { title: 'We use cookies for consented purposes only' } },
|
|
224
|
+
de: { cookieBanner: { title: 'Wir verwenden Cookies nur für genehmigte Zwecke' } },
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
policyPacks: [
|
|
230
|
+
{
|
|
231
|
+
id: 'eu',
|
|
232
|
+
match: { countries: ['DE', 'FR', 'IT'] },
|
|
233
|
+
consent: { model: 'opt-in' },
|
|
234
|
+
i18n: { messageProfile: 'eu' },
|
|
235
|
+
ui: { mode: 'banner' },
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`i18n.language` can also force a concrete language for a policy when needed.
|
|
242
|
+
|
|
243
|
+
When `i18n.messages` is configured, c15t keeps language selection inside the
|
|
244
|
+
languages defined for the active `messageProfile`.
|
|
245
|
+
Built-in translations still provide the base strings for the selected language,
|
|
246
|
+
but they no longer introduce additional locales beyond the ones you configured.
|
|
247
|
+
|
|
248
|
+
This means `messageProfile` controls both:
|
|
249
|
+
|
|
250
|
+
1. the policy-specific wording
|
|
251
|
+
2. the allowed language set for that policy
|
|
252
|
+
|
|
253
|
+
`defaultProfile` is only used when a policy does not set `messageProfile`.
|
|
254
|
+
Each profile can optionally define its own `fallbackLanguage` for unsupported
|
|
255
|
+
browser locales. If omitted, c15t falls back to English when available,
|
|
256
|
+
otherwise to the first configured language in that profile.
|
|
257
|
+
|
|
258
|
+
For example:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
i18n: {
|
|
262
|
+
defaultProfile: 'default',
|
|
263
|
+
messages: {
|
|
264
|
+
default: {
|
|
265
|
+
translations: {
|
|
266
|
+
en: { cookieBanner: { title: 'Privacy choices' } },
|
|
267
|
+
es: { cookieBanner: { title: 'Tus opciones de privacidad' } },
|
|
268
|
+
pt: { cookieBanner: { title: 'As suas escolhas de privacidade' } },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
eu: {
|
|
272
|
+
fallbackLanguage: 'en',
|
|
273
|
+
translations: {
|
|
274
|
+
en: { cookieBanner: { title: 'EU GDPR Consent' } },
|
|
275
|
+
fr: { cookieBanner: { title: 'Consentement RGPD' } },
|
|
276
|
+
de: { cookieBanner: { title: 'GDPR-Einwilligung' } },
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
For a policy with `i18n: { messageProfile: 'eu' }`, visitors can resolve to
|
|
284
|
+
`en`, `fr`, or `de`, but not to `es`, `pt`, or an unconfigured locale like
|
|
285
|
+
`zh`. If the browser asks for `zh`, c15t falls back to the `eu` profile's
|
|
286
|
+
`fallbackLanguage`, which would be `en` in this example.
|
|
287
|
+
|
|
288
|
+
## Snapshot Tokens
|
|
289
|
+
|
|
290
|
+
When `policySnapshot.signingKey` is configured, `/init` returns a signed JWT alongside the resolved policy. The token contains the full policy decision metadata (fingerprint, matched geo, consent model) and is validated on `POST /subjects`.
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
const c15t = c15tInstance({
|
|
294
|
+
adapter,
|
|
295
|
+
trustedOrigins: ['https://app.example.com'],
|
|
296
|
+
policySnapshot: {
|
|
297
|
+
signingKey: process.env.POLICY_SNAPSHOT_KEY,
|
|
298
|
+
ttlSeconds: 1800, // 30 minutes (default)
|
|
299
|
+
onValidationFailure: 'reject', // default
|
|
300
|
+
},
|
|
301
|
+
policyPacks: [...],
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Validation modes:
|
|
306
|
+
|
|
307
|
+
* `onValidationFailure: 'reject'` — invalid, expired, or missing tokens return `409 Conflict`. This is the default and preserves the original `/init` decision.
|
|
308
|
+
* `onValidationFailure: 'resolve_current'` — c15t falls back to resolving the current policy at write time. This favors availability over strict decision consistency.
|
|
309
|
+
|
|
310
|
+
Use `reject` when you want `/subjects` to preserve the original decision exactly. Use `resolve_current` only if your deployment prefers accepting writes even when the original snapshot cannot be verified.
|
|
311
|
+
|
|
312
|
+
## Global Privacy Control (GPC)
|
|
313
|
+
|
|
314
|
+
Each policy can opt in to respecting the [Global Privacy Control](https://globalprivacycontrol.org/) signal via `consent.gpc`:
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
{
|
|
318
|
+
id: 'california',
|
|
319
|
+
match: { regions: [{ country: 'US', region: 'CA' }] },
|
|
320
|
+
consent: { model: 'opt-out', gpc: true },
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
When `gpc: true` and the visitor's browser sends a GPC signal (`Sec-GPC: 1`), `marketing` and `measurement` categories are automatically denied during auto-granting — honoring the user's opt-out preference.
|
|
325
|
+
|
|
326
|
+
When `gpc` is `false` or omitted, the GPC signal is ignored for that policy. This is the right default for GDPR policies where consent is already opt-in and GPC is redundant.
|
|
327
|
+
|
|
328
|
+
The `californiaOptIn()` and `californiaOptOut()` presets set `gpc: true` by default. The Europe presets omit it.
|
|
329
|
+
|
|
330
|
+
## Scope Mode
|
|
331
|
+
|
|
332
|
+
Each policy can set `consent.scopeMode` to control how out-of-scope categories are handled:
|
|
333
|
+
|
|
334
|
+
* **`strict`** — categories not listed in the policy's `consent.categories` are rejected. Use this for GDPR where only explicitly scoped categories should be collected.
|
|
335
|
+
* **`permissive`** — categories not listed in the policy are allowed through. Use this for regions with lighter requirements where you don't want to block unrecognized categories.
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
{
|
|
339
|
+
id: 'eu',
|
|
340
|
+
match: { countries: ['DE'] },
|
|
341
|
+
consent: {
|
|
342
|
+
model: 'opt-in',
|
|
343
|
+
scopeMode: 'strict',
|
|
344
|
+
categories: ['necessary', 'measurement', 'marketing'],
|
|
345
|
+
},
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
When omitted, scope mode defaults to `permissive`.
|
|
350
|
+
|
|
351
|
+
## Fallback Matcher
|
|
352
|
+
|
|
353
|
+
The `match.fallback` flag provides a safety net when geo-location fails — when CDN headers are missing and `countryCode` is `null`. This is distinct from `match.isDefault`:
|
|
354
|
+
|
|
355
|
+
* **`isDefault`** — catch-all for **known** locations that don't match any specific policy ("rest of world")
|
|
356
|
+
* **`fallback`** — safety net for **unknown** locations when geo fails ("assume strictest")
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
{
|
|
360
|
+
id: 'strict_fallback',
|
|
361
|
+
match: { fallback: true, countries: [...EEA_COUNTRY_CODES] },
|
|
362
|
+
consent: { model: 'opt-in' },
|
|
363
|
+
ui: { mode: 'banner' },
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The fallback is only checked when `countryCode` is `null`. When geo works normally, resolution skips fallback entirely and uses the standard region → country → default flow.
|
|
368
|
+
|
|
369
|
+
The `europeOptIn()` and `europeIab()` presets include `fallback: true` by default, so EU-level consent applies automatically when geo headers are missing or when `disableGeoLocation` is enabled.
|
|
370
|
+
|
|
371
|
+
> ℹ️ **Info:**
|
|
372
|
+
> At most one fallback policy is allowed. Use inspectPolicies() to verify — it warns when no fallback is configured and errors when multiple are defined.
|
|
373
|
+
|
|
374
|
+
## Edge Cases
|
|
375
|
+
|
|
376
|
+
|Configuration|Result|
|
|
377
|
+
|--|--|
|
|
378
|
+
|`policyPacks` omitted|**Deprecated** — legacy mode with no runtime policy in `/init` response. Will require `policyPacks` in 2.0 GA.|
|
|
379
|
+
|`policyPacks: []`|Explicit no-banner mode|
|
|
380
|
+
|Non-empty pack, no match, no default|Implicit no-banner mode|
|
|
381
|
+
|
|
382
|
+
> ⚠️ **Warning:**
|
|
383
|
+
> Omitting policyPacks entirely is deprecated and will be removed in 2.0 GA. Set policyPacks: \[] explicitly if you want no-banner behavior.
|
|
384
|
+
|
|
385
|
+
For most production deployments, include both a default and a fallback policy so all traffic resolves deterministically.
|
|
386
|
+
|
|
387
|
+
## Frontend Alignment
|
|
388
|
+
|
|
389
|
+
If you also use offline previews on the frontend:
|
|
390
|
+
|
|
391
|
+
* Keep the backend pack canonical — it resolves from real geo data
|
|
392
|
+
* Mirror it in frontend `offlinePolicy.policyPacks` only for local development, tests, or static demos
|
|
393
|
+
* Validate the frontend preview against a real `/init` response before shipping
|
|
394
|
+
|
|
395
|
+
> ℹ️ **Info:**
|
|
396
|
+
> Frontend usage is documented in the React guide, Next.js guide, and JavaScript guide.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quickstart
|
|
3
|
+
description: Self-host the c15t consent management backend in your own infrastructure.
|
|
4
|
+
---
|
|
5
|
+
The `@c15t/backend` package gives you a fully self-hosted consent management API. It handles consent storage, geo-location, audit logging, and policy management — all on your own infrastructure.
|
|
6
|
+
|
|
7
|
+
The backend exposes a standard `(request: Request) => Promise<Response>` handler, so it works with any JavaScript runtime (Node.js, Bun, Deno, Cloudflare Workers) and any HTTP framework.
|
|
8
|
+
|
|
9
|
+
If you want a fully managed experience we recommend using [inth.com](https://inth.com).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
|Package manager|Command|
|
|
14
|
+
|:--|:--|
|
|
15
|
+
|npm|`npm install @c15t/backend`|
|
|
16
|
+
|pnpm|`pnpm add @c15t/backend`|
|
|
17
|
+
|yarn|`yarn add @c15t/backend`|
|
|
18
|
+
|bun|`bun add @c15t/backend`|
|
|
19
|
+
|
|
20
|
+
## Basic Setup
|
|
21
|
+
|
|
22
|
+
1. **Create a c15t instance**
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { c15tInstance } from '@c15t/backend';
|
|
26
|
+
import { kyselyAdapter } from '@c15t/backend/db/adapters/kysely';
|
|
27
|
+
import { db } from './db'; // your Kysely instance
|
|
28
|
+
|
|
29
|
+
export const c15t = c15tInstance({
|
|
30
|
+
appName: 'my-app',
|
|
31
|
+
basePath: '/api/c15t',
|
|
32
|
+
trustedOrigins: ['https://example.com'],
|
|
33
|
+
adapter: kyselyAdapter(db),
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The trustedOrigins array controls CORS — list every domain that will send requests to the API.
|
|
38
|
+
|
|
39
|
+
2. **Mount the handler** The c15t.handler accepts a standard Fetch API Request and returns a Response. Mount it in your framework of choice:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// Bun
|
|
43
|
+
import { c15t } from './c15t';
|
|
44
|
+
|
|
45
|
+
Bun.serve({
|
|
46
|
+
port: 3001,
|
|
47
|
+
fetch: c15t.handler,
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
// Next.js App Router
|
|
53
|
+
import { c15t } from '@/lib/c15t';
|
|
54
|
+
|
|
55
|
+
const handler = c15t.handler;
|
|
56
|
+
|
|
57
|
+
export { handler as GET, handler as POST, handler as PUT, handler as PATCH, handler as DELETE };
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// Cloudflare Workers
|
|
62
|
+
import { c15t } from './c15t';
|
|
63
|
+
|
|
64
|
+
export default {
|
|
65
|
+
async fetch(request: Request) {
|
|
66
|
+
return c15t.handler(request);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
See Framework Integration for more examples.
|
|
72
|
+
|
|
73
|
+
3. **Run database migrations** Before the backend can store consent records, your database needs the required tables.
|
|
74
|
+
|
|
75
|
+
The easiest way to migrate your database is via the c15t cli:
|
|
76
|
+
|
|
77
|
+
|Package manager|Command|
|
|
78
|
+
|:--|:--|
|
|
79
|
+
|npm|`npx @c15t/cli@rc`|
|
|
80
|
+
|pnpm|`pnpm dlx @c15t/cli@rc`|
|
|
81
|
+
|yarn|`yarn dlx @c15t/cli@rc`|
|
|
82
|
+
|bun|`bunx @c15t/cli@rc`|
|
|
83
|
+
|
|
84
|
+
See Database Setup for adapter-specific migration guides. If you plan to use policy packs with runtime audit storage, also apply the runtime policy decision migration from the Policy Packs guide.
|
|
85
|
+
|
|
86
|
+
4. **Point your frontend at the backend** Update your frontend consent manager to use your self-hosted URL:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<ConsentManagerProvider
|
|
90
|
+
options={{
|
|
91
|
+
mode: 'hosted',
|
|
92
|
+
backendURL: 'https://example.com/api/c15t',
|
|
93
|
+
consentCategories: ['necessary', 'measurement', 'marketing'],
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
5. **Verify it works** Open https\://example.com/api/c15t/status in your browser. You should see a JSON response with the server version and your client info:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"version": "1.8.0",
|
|
103
|
+
"timestamp": "2026-02-11T12:00:00.000Z",
|
|
104
|
+
"client": {
|
|
105
|
+
"ip": "192.168.1.0",
|
|
106
|
+
"acceptLanguage": "en-US",
|
|
107
|
+
"userAgent": "Mozilla/5.0 ...",
|
|
108
|
+
"region": { "countryCode": "US", "regionCode": "CA" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> ℹ️ **Info:**
|
|
114
|
+
> The backend includes auto-generated API documentation. Visit \{basePath}/docs (e.g. /api/c15t/docs) to explore all endpoints interactively.
|
|
115
|
+
|
|
116
|
+
## Optional: AI Agents
|
|
117
|
+
|
|
118
|
+
Install c15t agent skills to let AI agents help with styling, i18n, scripts & other configuration.
|
|
119
|
+
|
|
120
|
+
|Package manager|Command|
|
|
121
|
+
|:--|:--|
|
|
122
|
+
|npm|`npx @c15t/cli@rc skills`|
|
|
123
|
+
|pnpm|`pnpm dlx @c15t/cli@rc skills`|
|
|
124
|
+
|yarn|`yarn dlx @c15t/cli@rc skills`|
|
|
125
|
+
|bun|`bunx @c15t/cli@rc skills`|
|
|
126
|
+
|
|
127
|
+
See [AI Agents](/docs/ai-agents) for bundled package docs and agent skills.
|
|
128
|
+
|
|
129
|
+
## Next Steps
|