@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.
Files changed (336) hide show
  1. package/README.md +3 -3
  2. package/dist/302.js +473 -0
  3. package/dist/583.js +540 -0
  4. package/dist/915.js +1771 -0
  5. package/dist/cache.cjs +5 -5
  6. package/dist/cache.js +4 -415
  7. package/dist/core.cjs +1356 -120
  8. package/dist/core.js +163 -1981
  9. package/dist/db/adapters/drizzle.cjs +1 -1
  10. package/dist/db/adapters/drizzle.js +1 -2
  11. package/dist/db/adapters/kysely.cjs +1 -1
  12. package/dist/db/adapters/kysely.js +1 -2
  13. package/dist/db/adapters/mongo.cjs +1 -1
  14. package/dist/db/adapters/mongo.js +1 -2
  15. package/dist/db/adapters/prisma.cjs +1 -1
  16. package/dist/db/adapters/prisma.js +1 -2
  17. package/dist/db/adapters/typeorm.cjs +1 -1
  18. package/dist/db/adapters/typeorm.js +1 -2
  19. package/dist/db/adapters.cjs +1 -1
  20. package/dist/db/migrator.cjs +1 -1
  21. package/dist/db/schema.cjs +43 -3
  22. package/dist/db/schema.js +35 -4
  23. package/dist/define-config.cjs +1 -1
  24. package/dist/edge.cjs +1106 -0
  25. package/dist/edge.js +190 -0
  26. package/dist/router.cjs +885 -123
  27. package/dist/router.js +1 -1507
  28. package/dist/{types.cjs → types/index.cjs} +1 -1
  29. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  30. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  31. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  32. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  33. package/{dist → dist-types}/cache/gvl-resolver.d.ts +0 -1
  34. package/{dist → dist-types}/cache/index.d.ts +0 -1
  35. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  36. package/{dist → dist-types}/cache/types.d.ts +0 -1
  37. package/{dist → dist-types}/core.d.ts +8 -1
  38. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  39. package/dist-types/db/registry/consent-policy.d.ts +78 -0
  40. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  41. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  42. package/dist-types/db/registry/index.d.ts +118 -0
  43. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  44. package/{dist → dist-types}/db/registry/subject.d.ts +0 -2
  45. package/{dist → dist-types}/db/registry/types.d.ts +1 -1
  46. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  47. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  48. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  49. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  50. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  51. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  52. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +1 -2
  53. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  54. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -32
  55. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -2
  56. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +1 -2
  57. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +3 -3
  58. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +1 -2
  59. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +7 -2
  60. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +1 -2
  61. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +455 -28
  62. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  63. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +1 -3
  64. package/{dist → dist-types}/db/schema/index.d.ts +908 -86
  65. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  66. package/dist-types/define-config.d.ts +17 -0
  67. package/dist-types/edge/index.d.ts +5 -0
  68. package/dist-types/edge/init-handler.d.ts +40 -0
  69. package/dist-types/edge/resolve-consent.d.ts +80 -0
  70. package/dist-types/edge/types.d.ts +13 -0
  71. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  72. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  73. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  74. package/{dist → dist-types}/handlers/init/index.d.ts +2 -3
  75. package/dist-types/handlers/init/policy.d.ts +26 -0
  76. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  77. package/dist-types/handlers/init/translations.d.ts +48 -0
  78. package/dist-types/handlers/legal-document/current.handler.d.ts +11 -0
  79. package/dist-types/handlers/legal-document/snapshot.d.ts +39 -0
  80. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  81. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  82. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  83. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +3 -2
  84. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  85. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +3 -2
  86. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -2
  87. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  88. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +3 -1
  89. package/{dist → dist-types}/init.d.ts +4 -7
  90. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  91. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  92. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  93. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  94. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +0 -1
  95. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  96. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  97. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  98. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  99. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  100. package/dist-types/policies/builder.d.ts +127 -0
  101. package/dist-types/policies/defaults.d.ts +2 -0
  102. package/dist-types/policies/matchers.d.ts +3 -0
  103. package/{dist → dist-types}/router.d.ts +0 -1
  104. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  105. package/{dist → dist-types}/routes/index.d.ts +1 -1
  106. package/{dist → dist-types}/routes/init.d.ts +0 -1
  107. package/dist-types/routes/legal-document.d.ts +7 -0
  108. package/{dist → dist-types}/routes/status.d.ts +0 -1
  109. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  110. package/{dist → dist-types}/types/api.d.ts +0 -1
  111. package/dist-types/types/index.d.ts +464 -0
  112. package/dist-types/utils/background.d.ts +6 -0
  113. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +1 -2
  114. package/{dist → dist-types}/utils/env.d.ts +0 -1
  115. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  116. package/{dist → dist-types}/utils/instrumentation.d.ts +2 -3
  117. package/{dist → dist-types}/utils/logger.d.ts +0 -1
  118. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  119. package/dist-types/version.d.ts +1 -0
  120. package/docs/README.md +49 -0
  121. package/docs/api/configuration.md +208 -0
  122. package/docs/api/endpoints.md +211 -0
  123. package/docs/guides/caching.md +85 -0
  124. package/docs/guides/database-setup.md +128 -0
  125. package/docs/guides/edge-deployment.md +251 -0
  126. package/docs/guides/framework-integration.md +142 -0
  127. package/docs/guides/iab-tcf.md +89 -0
  128. package/docs/guides/observability.md +96 -0
  129. package/docs/guides/policy-packs.md +396 -0
  130. package/docs/quickstart.md +129 -0
  131. package/package.json +53 -39
  132. package/.turbo/turbo-build.log +0 -49
  133. package/CHANGELOG.md +0 -89
  134. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  135. package/dist/cache/adapters/index.d.ts.map +0 -1
  136. package/dist/cache/adapters/memory.d.ts.map +0 -1
  137. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  138. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  139. package/dist/cache/index.d.ts.map +0 -1
  140. package/dist/cache/keys.d.ts.map +0 -1
  141. package/dist/cache/types.d.ts.map +0 -1
  142. package/dist/core.d.ts.map +0 -1
  143. package/dist/db/adapters/drizzle.d.ts +0 -2
  144. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  145. package/dist/db/adapters/index.d.ts +0 -2
  146. package/dist/db/adapters/index.d.ts.map +0 -1
  147. package/dist/db/adapters/kysely.d.ts +0 -2
  148. package/dist/db/adapters/kysely.d.ts.map +0 -1
  149. package/dist/db/adapters/mongo.d.ts +0 -2
  150. package/dist/db/adapters/mongo.d.ts.map +0 -1
  151. package/dist/db/adapters/prisma.d.ts +0 -2
  152. package/dist/db/adapters/prisma.d.ts.map +0 -1
  153. package/dist/db/adapters/typeorm.d.ts +0 -2
  154. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  155. package/dist/db/migrator/index.d.ts.map +0 -1
  156. package/dist/db/registry/consent-policy.d.ts +0 -23
  157. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  158. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  159. package/dist/db/registry/domain.d.ts.map +0 -1
  160. package/dist/db/registry/index.d.ts +0 -57
  161. package/dist/db/registry/index.d.ts.map +0 -1
  162. package/dist/db/registry/subject.d.ts.map +0 -1
  163. package/dist/db/registry/types.d.ts.map +0 -1
  164. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  165. package/dist/db/registry/utils.d.ts.map +0 -1
  166. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  167. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  168. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  169. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  170. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  171. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  172. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  173. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  174. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  175. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  176. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  177. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  178. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  179. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  180. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  181. package/dist/db/schema/index.d.ts.map +0 -1
  182. package/dist/db/tenant-scope.d.ts.map +0 -1
  183. package/dist/define-config.d.ts +0 -5
  184. package/dist/define-config.d.ts.map +0 -1
  185. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  186. package/dist/handlers/consent/index.d.ts +0 -12
  187. package/dist/handlers/consent/index.d.ts.map +0 -1
  188. package/dist/handlers/init/geo.d.ts.map +0 -1
  189. package/dist/handlers/init/index.d.ts.map +0 -1
  190. package/dist/handlers/init/translations.d.ts +0 -28
  191. package/dist/handlers/init/translations.d.ts.map +0 -1
  192. package/dist/handlers/status/index.d.ts +0 -7
  193. package/dist/handlers/status/index.d.ts.map +0 -1
  194. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  195. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  196. package/dist/handlers/subject/index.d.ts +0 -10
  197. package/dist/handlers/subject/index.d.ts.map +0 -1
  198. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  199. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  200. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  201. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  202. package/dist/init.d.ts.map +0 -1
  203. package/dist/middleware/auth/index.d.ts.map +0 -1
  204. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  205. package/dist/middleware/cors/cors.d.ts.map +0 -1
  206. package/dist/middleware/cors/index.d.ts +0 -30
  207. package/dist/middleware/cors/index.d.ts.map +0 -1
  208. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  209. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  210. package/dist/middleware/openapi/config.d.ts.map +0 -1
  211. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  212. package/dist/middleware/openapi/index.d.ts +0 -12
  213. package/dist/middleware/openapi/index.d.ts.map +0 -1
  214. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  215. package/dist/router.d.ts.map +0 -1
  216. package/dist/routes/consent.d.ts.map +0 -1
  217. package/dist/routes/index.d.ts.map +0 -1
  218. package/dist/routes/init.d.ts.map +0 -1
  219. package/dist/routes/status.d.ts.map +0 -1
  220. package/dist/routes/subject.d.ts.map +0 -1
  221. package/dist/types/api.d.ts.map +0 -1
  222. package/dist/types/index.d.ts +0 -255
  223. package/dist/types/index.d.ts.map +0 -1
  224. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  225. package/dist/utils/env.d.ts.map +0 -1
  226. package/dist/utils/extract-error-message.d.ts.map +0 -1
  227. package/dist/utils/index.d.ts +0 -4
  228. package/dist/utils/index.d.ts.map +0 -1
  229. package/dist/utils/instrumentation.d.ts.map +0 -1
  230. package/dist/utils/logger.d.ts.map +0 -1
  231. package/dist/utils/metrics.d.ts.map +0 -1
  232. package/dist/version.d.ts +0 -2
  233. package/dist/version.d.ts.map +0 -1
  234. package/knip.json +0 -31
  235. package/rslib.config.ts +0 -93
  236. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  237. package/src/cache/adapters/index.ts +0 -22
  238. package/src/cache/adapters/memory.ts +0 -111
  239. package/src/cache/adapters/upstash-redis.ts +0 -113
  240. package/src/cache/gvl-resolver.ts +0 -289
  241. package/src/cache/index.ts +0 -34
  242. package/src/cache/keys.ts +0 -68
  243. package/src/cache/types.ts +0 -66
  244. package/src/core.ts +0 -368
  245. package/src/db/migrator/index.ts +0 -80
  246. package/src/db/registry/consent-policy.test.ts +0 -451
  247. package/src/db/registry/consent-policy.ts +0 -82
  248. package/src/db/registry/consent-purpose.test.ts +0 -428
  249. package/src/db/registry/consent-purpose.ts +0 -61
  250. package/src/db/registry/domain.test.ts +0 -445
  251. package/src/db/registry/domain.ts +0 -91
  252. package/src/db/registry/index.ts +0 -14
  253. package/src/db/registry/subject.test.ts +0 -388
  254. package/src/db/registry/subject.ts +0 -129
  255. package/src/db/registry/types.ts +0 -10
  256. package/src/db/registry/utils/generate-id.test.ts +0 -216
  257. package/src/db/registry/utils/generate-id.ts +0 -133
  258. package/src/db/registry/utils.ts +0 -133
  259. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  260. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  261. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  262. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  263. package/src/db/schema/1.0.0/consent.ts +0 -20
  264. package/src/db/schema/1.0.0/domain.ts +0 -12
  265. package/src/db/schema/1.0.0/index.ts +0 -48
  266. package/src/db/schema/1.0.0/subject.ts +0 -12
  267. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  268. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  269. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  270. package/src/db/schema/2.0.0/consent.ts +0 -26
  271. package/src/db/schema/2.0.0/domain.ts +0 -12
  272. package/src/db/schema/2.0.0/index.ts +0 -47
  273. package/src/db/schema/2.0.0/subject.ts +0 -14
  274. package/src/db/schema/index.ts +0 -15
  275. package/src/db/tenant-scope.test.ts +0 -750
  276. package/src/db/tenant-scope.ts +0 -103
  277. package/src/define-config.ts +0 -5
  278. package/src/handlers/consent/check.handler.ts +0 -126
  279. package/src/handlers/init/geo.test.ts +0 -317
  280. package/src/handlers/init/geo.ts +0 -195
  281. package/src/handlers/init/index.test.ts +0 -205
  282. package/src/handlers/init/index.ts +0 -114
  283. package/src/handlers/init/translations.test.ts +0 -121
  284. package/src/handlers/init/translations.ts +0 -72
  285. package/src/handlers/status/status.handler.test.ts +0 -155
  286. package/src/handlers/status/status.handler.ts +0 -51
  287. package/src/handlers/subject/get.handler.ts +0 -93
  288. package/src/handlers/subject/list.handler.ts +0 -93
  289. package/src/handlers/subject/patch.handler.ts +0 -122
  290. package/src/handlers/subject/post.handler.test.ts +0 -294
  291. package/src/handlers/subject/post.handler.ts +0 -254
  292. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  293. package/src/handlers/utils/consent-enrichment.ts +0 -218
  294. package/src/init.test.ts +0 -126
  295. package/src/init.ts +0 -87
  296. package/src/middleware/auth/index.ts +0 -11
  297. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  298. package/src/middleware/auth/validate-api-key.ts +0 -107
  299. package/src/middleware/cors/cors.test.ts +0 -135
  300. package/src/middleware/cors/cors.ts +0 -186
  301. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  302. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  303. package/src/middleware/cors/process-cors.ts +0 -91
  304. package/src/middleware/openapi/config.ts +0 -29
  305. package/src/middleware/openapi/handlers.ts +0 -34
  306. package/src/middleware/process-ip/index.test.ts +0 -195
  307. package/src/middleware/process-ip/index.ts +0 -199
  308. package/src/router.ts +0 -15
  309. package/src/routes/consent.ts +0 -52
  310. package/src/routes/index.ts +0 -10
  311. package/src/routes/init.ts +0 -102
  312. package/src/routes/status.ts +0 -46
  313. package/src/routes/subject.ts +0 -152
  314. package/src/types/api.ts +0 -48
  315. package/src/types/index.ts +0 -288
  316. package/src/utils/create-telemetry-options.test.ts +0 -302
  317. package/src/utils/create-telemetry-options.ts +0 -229
  318. package/src/utils/env.ts +0 -84
  319. package/src/utils/extract-error-message.ts +0 -21
  320. package/src/utils/instrumentation.test.ts +0 -185
  321. package/src/utils/instrumentation.ts +0 -196
  322. package/src/utils/logger.ts +0 -41
  323. package/src/utils/metrics.test.ts +0 -323
  324. package/src/utils/metrics.ts +0 -402
  325. package/src/utils/telemetry-pii.test.ts +0 -325
  326. package/src/version.ts +0 -2
  327. package/tsconfig.json +0 -11
  328. package/vitest.config.ts +0 -28
  329. /package/dist/{types.js → types/index.js} +0 -0
  330. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  331. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  332. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  333. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  334. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  335. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  336. /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