@c15t/backend 2.0.0-rc.4 → 2.0.0-rc.6

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 (327) hide show
  1. package/dist/302.js +473 -0
  2. package/dist/364.js +1140 -0
  3. package/dist/583.js +540 -0
  4. package/dist/cache.cjs +1 -1
  5. package/dist/cache.js +4 -415
  6. package/dist/core.cjs +849 -96
  7. package/dist/core.js +147 -1817
  8. package/dist/db/adapters/drizzle.cjs +1 -1
  9. package/dist/db/adapters/drizzle.js +1 -2
  10. package/dist/db/adapters/kysely.cjs +1 -1
  11. package/dist/db/adapters/kysely.js +1 -2
  12. package/dist/db/adapters/mongo.cjs +1 -1
  13. package/dist/db/adapters/mongo.js +1 -2
  14. package/dist/db/adapters/prisma.cjs +1 -1
  15. package/dist/db/adapters/prisma.js +1 -2
  16. package/dist/db/adapters/typeorm.cjs +1 -1
  17. package/dist/db/adapters/typeorm.js +1 -2
  18. package/dist/db/adapters.cjs +1 -1
  19. package/dist/db/migrator.cjs +1 -1
  20. package/dist/db/schema.cjs +38 -1
  21. package/dist/db/schema.js +33 -2
  22. package/dist/define-config.cjs +1 -1
  23. package/dist/edge.cjs +1106 -0
  24. package/dist/edge.js +190 -0
  25. package/dist/router.cjs +629 -81
  26. package/dist/router.js +1 -1509
  27. package/dist/types/index.cjs +1 -1
  28. package/{dist → dist-types}/cache/adapters/cloudflare-kv.d.ts +0 -1
  29. package/{dist → dist-types}/cache/adapters/index.d.ts +0 -1
  30. package/{dist → dist-types}/cache/adapters/memory.d.ts +0 -1
  31. package/{dist → dist-types}/cache/adapters/upstash-redis.d.ts +0 -1
  32. package/{dist → dist-types}/cache/gvl-resolver.d.ts +1 -2
  33. package/{dist → dist-types}/cache/index.d.ts +0 -1
  34. package/{dist → dist-types}/cache/keys.d.ts +0 -1
  35. package/{dist → dist-types}/cache/types.d.ts +0 -1
  36. package/{dist → dist-types}/core.d.ts +8 -1
  37. package/{dist → dist-types}/db/migrator/index.d.ts +0 -1
  38. package/{dist → dist-types}/db/registry/consent-policy.d.ts +0 -1
  39. package/{dist → dist-types}/db/registry/consent-purpose.d.ts +0 -1
  40. package/{dist → dist-types}/db/registry/domain.d.ts +0 -1
  41. package/{dist → dist-types}/db/registry/index.d.ts +22 -2
  42. package/dist-types/db/registry/runtime-policy-decision.d.ts +60 -0
  43. package/{dist → dist-types}/db/registry/subject.d.ts +0 -1
  44. package/{dist → dist-types}/db/registry/types.d.ts +1 -2
  45. package/{dist → dist-types}/db/registry/utils/generate-id.d.ts +0 -1
  46. package/{dist → dist-types}/db/registry/utils.d.ts +0 -1
  47. package/{dist → dist-types}/db/schema/1.0.0/audit-log.d.ts +0 -1
  48. package/{dist → dist-types}/db/schema/1.0.0/consent-policy.d.ts +0 -1
  49. package/{dist → dist-types}/db/schema/1.0.0/consent-purpose.d.ts +0 -1
  50. package/{dist → dist-types}/db/schema/1.0.0/consent-record.d.ts +0 -1
  51. package/{dist → dist-types}/db/schema/1.0.0/consent.d.ts +2 -3
  52. package/{dist → dist-types}/db/schema/1.0.0/domain.d.ts +0 -1
  53. package/{dist → dist-types}/db/schema/1.0.0/index.d.ts +0 -1
  54. package/{dist → dist-types}/db/schema/1.0.0/subject.d.ts +0 -1
  55. package/{dist → dist-types}/db/schema/2.0.0/audit-log.d.ts +2 -3
  56. package/{dist → dist-types}/db/schema/2.0.0/consent-policy.d.ts +2 -3
  57. package/{dist → dist-types}/db/schema/2.0.0/consent-purpose.d.ts +2 -3
  58. package/{dist → dist-types}/db/schema/2.0.0/consent.d.ts +6 -3
  59. package/{dist → dist-types}/db/schema/2.0.0/domain.d.ts +2 -3
  60. package/{dist → dist-types}/db/schema/2.0.0/index.d.ts +432 -17
  61. package/dist-types/db/schema/2.0.0/runtime-policy-decision.d.ts +23 -0
  62. package/{dist → dist-types}/db/schema/2.0.0/subject.d.ts +2 -3
  63. package/{dist → dist-types}/db/schema/index.d.ts +862 -33
  64. package/{dist → dist-types}/db/tenant-scope.d.ts +0 -1
  65. package/{dist → dist-types}/define-config.d.ts +0 -1
  66. package/dist-types/edge/index.d.ts +5 -0
  67. package/dist-types/edge/init-handler.d.ts +38 -0
  68. package/dist-types/edge/resolve-consent.d.ts +80 -0
  69. package/dist-types/edge/types.d.ts +13 -0
  70. package/{dist → dist-types}/handlers/consent/check.handler.d.ts +0 -1
  71. package/{src/handlers/consent/index.ts → dist-types/handlers/consent/index.d.ts} +0 -1
  72. package/{dist → dist-types}/handlers/init/geo.d.ts +2 -3
  73. package/{dist → dist-types}/handlers/init/index.d.ts +4 -5
  74. package/dist-types/handlers/init/policy.d.ts +26 -0
  75. package/dist-types/handlers/init/resolve-init.d.ts +44 -0
  76. package/dist-types/handlers/init/translations.d.ts +48 -0
  77. package/dist-types/handlers/policy/snapshot.d.ts +99 -0
  78. package/{src/handlers/status/index.ts → dist-types/handlers/status/index.d.ts} +0 -1
  79. package/{dist → dist-types}/handlers/status/status.handler.d.ts +0 -1
  80. package/{dist → dist-types}/handlers/subject/get.handler.d.ts +0 -1
  81. package/{src/handlers/subject/index.ts → dist-types/handlers/subject/index.d.ts} +0 -1
  82. package/{dist → dist-types}/handlers/subject/list.handler.d.ts +0 -1
  83. package/{dist → dist-types}/handlers/subject/patch.handler.d.ts +0 -1
  84. package/{dist → dist-types}/handlers/subject/post.handler.d.ts +12 -1
  85. package/{dist → dist-types}/handlers/utils/consent-enrichment.d.ts +0 -1
  86. package/{dist → dist-types}/init.d.ts +0 -1
  87. package/{dist → dist-types}/middleware/auth/index.d.ts +0 -1
  88. package/{dist → dist-types}/middleware/auth/validate-api-key.d.ts +0 -1
  89. package/{dist → dist-types}/middleware/cors/cors.d.ts +0 -1
  90. package/{src/middleware/cors/index.ts → dist-types/middleware/cors/index.d.ts} +0 -1
  91. package/{dist → dist-types}/middleware/cors/is-origin-trusted.d.ts +1 -2
  92. package/{dist → dist-types}/middleware/cors/process-cors.d.ts +0 -1
  93. package/{dist → dist-types}/middleware/openapi/config.d.ts +0 -1
  94. package/{dist → dist-types}/middleware/openapi/handlers.d.ts +0 -1
  95. package/{src/middleware/openapi/index.ts → dist-types/middleware/openapi/index.d.ts} +0 -1
  96. package/{dist → dist-types}/middleware/process-ip/index.d.ts +0 -1
  97. package/dist-types/policies/builder.d.ts +127 -0
  98. package/dist-types/policies/defaults.d.ts +2 -0
  99. package/dist-types/policies/matchers.d.ts +3 -0
  100. package/{dist → dist-types}/router.d.ts +0 -1
  101. package/{dist → dist-types}/routes/consent.d.ts +0 -1
  102. package/{src/routes/index.ts → dist-types/routes/index.d.ts} +0 -1
  103. package/{dist → dist-types}/routes/init.d.ts +0 -1
  104. package/{dist → dist-types}/routes/status.d.ts +0 -1
  105. package/{dist → dist-types}/routes/subject.d.ts +0 -1
  106. package/{dist → dist-types}/types/api.d.ts +0 -1
  107. package/{dist → dist-types}/types/index.d.ts +110 -6
  108. package/dist-types/utils/background.d.ts +6 -0
  109. package/{dist → dist-types}/utils/create-telemetry-options.d.ts +0 -1
  110. package/{dist → dist-types}/utils/env.d.ts +0 -1
  111. package/{dist → dist-types}/utils/extract-error-message.d.ts +0 -1
  112. package/{dist → dist-types}/utils/instrumentation.d.ts +0 -1
  113. package/{dist → dist-types}/utils/logger.d.ts +1 -2
  114. package/{dist → dist-types}/utils/metrics.d.ts +0 -1
  115. package/dist-types/version.d.ts +1 -0
  116. package/docs/README.md +49 -0
  117. package/docs/api/configuration.md +197 -0
  118. package/docs/api/endpoints.md +211 -0
  119. package/docs/guides/caching.md +85 -0
  120. package/docs/guides/database-setup.md +128 -0
  121. package/docs/guides/edge-deployment.md +248 -0
  122. package/docs/guides/framework-integration.md +142 -0
  123. package/docs/guides/iab-tcf.md +89 -0
  124. package/docs/guides/observability.md +96 -0
  125. package/docs/guides/policy-packs.md +396 -0
  126. package/docs/quickstart.md +129 -0
  127. package/package.json +45 -31
  128. package/.turbo/turbo-build.log +0 -49
  129. package/CHANGELOG.md +0 -123
  130. package/dist/cache/adapters/cloudflare-kv.d.ts.map +0 -1
  131. package/dist/cache/adapters/index.d.ts.map +0 -1
  132. package/dist/cache/adapters/memory.d.ts.map +0 -1
  133. package/dist/cache/adapters/upstash-redis.d.ts.map +0 -1
  134. package/dist/cache/gvl-resolver.d.ts.map +0 -1
  135. package/dist/cache/index.d.ts.map +0 -1
  136. package/dist/cache/keys.d.ts.map +0 -1
  137. package/dist/cache/types.d.ts.map +0 -1
  138. package/dist/core.d.ts.map +0 -1
  139. package/dist/db/adapters/drizzle.d.ts +0 -2
  140. package/dist/db/adapters/drizzle.d.ts.map +0 -1
  141. package/dist/db/adapters/index.d.ts +0 -2
  142. package/dist/db/adapters/index.d.ts.map +0 -1
  143. package/dist/db/adapters/kysely.d.ts +0 -2
  144. package/dist/db/adapters/kysely.d.ts.map +0 -1
  145. package/dist/db/adapters/mongo.d.ts +0 -2
  146. package/dist/db/adapters/mongo.d.ts.map +0 -1
  147. package/dist/db/adapters/prisma.d.ts +0 -2
  148. package/dist/db/adapters/prisma.d.ts.map +0 -1
  149. package/dist/db/adapters/typeorm.d.ts +0 -2
  150. package/dist/db/adapters/typeorm.d.ts.map +0 -1
  151. package/dist/db/migrator/index.d.ts.map +0 -1
  152. package/dist/db/registry/consent-policy.d.ts.map +0 -1
  153. package/dist/db/registry/consent-purpose.d.ts.map +0 -1
  154. package/dist/db/registry/domain.d.ts.map +0 -1
  155. package/dist/db/registry/index.d.ts.map +0 -1
  156. package/dist/db/registry/subject.d.ts.map +0 -1
  157. package/dist/db/registry/types.d.ts.map +0 -1
  158. package/dist/db/registry/utils/generate-id.d.ts.map +0 -1
  159. package/dist/db/registry/utils.d.ts.map +0 -1
  160. package/dist/db/schema/1.0.0/audit-log.d.ts.map +0 -1
  161. package/dist/db/schema/1.0.0/consent-policy.d.ts.map +0 -1
  162. package/dist/db/schema/1.0.0/consent-purpose.d.ts.map +0 -1
  163. package/dist/db/schema/1.0.0/consent-record.d.ts.map +0 -1
  164. package/dist/db/schema/1.0.0/consent.d.ts.map +0 -1
  165. package/dist/db/schema/1.0.0/domain.d.ts.map +0 -1
  166. package/dist/db/schema/1.0.0/index.d.ts.map +0 -1
  167. package/dist/db/schema/1.0.0/subject.d.ts.map +0 -1
  168. package/dist/db/schema/2.0.0/audit-log.d.ts.map +0 -1
  169. package/dist/db/schema/2.0.0/consent-policy.d.ts.map +0 -1
  170. package/dist/db/schema/2.0.0/consent-purpose.d.ts.map +0 -1
  171. package/dist/db/schema/2.0.0/consent.d.ts.map +0 -1
  172. package/dist/db/schema/2.0.0/domain.d.ts.map +0 -1
  173. package/dist/db/schema/2.0.0/index.d.ts.map +0 -1
  174. package/dist/db/schema/2.0.0/subject.d.ts.map +0 -1
  175. package/dist/db/schema/index.d.ts.map +0 -1
  176. package/dist/db/tenant-scope.d.ts.map +0 -1
  177. package/dist/define-config.d.ts.map +0 -1
  178. package/dist/handlers/consent/check.handler.d.ts.map +0 -1
  179. package/dist/handlers/consent/index.d.ts +0 -12
  180. package/dist/handlers/consent/index.d.ts.map +0 -1
  181. package/dist/handlers/init/geo.d.ts.map +0 -1
  182. package/dist/handlers/init/index.d.ts.map +0 -1
  183. package/dist/handlers/init/translations.d.ts +0 -26
  184. package/dist/handlers/init/translations.d.ts.map +0 -1
  185. package/dist/handlers/status/index.d.ts +0 -7
  186. package/dist/handlers/status/index.d.ts.map +0 -1
  187. package/dist/handlers/status/status.handler.d.ts.map +0 -1
  188. package/dist/handlers/subject/get.handler.d.ts.map +0 -1
  189. package/dist/handlers/subject/index.d.ts +0 -10
  190. package/dist/handlers/subject/index.d.ts.map +0 -1
  191. package/dist/handlers/subject/list.handler.d.ts.map +0 -1
  192. package/dist/handlers/subject/patch.handler.d.ts.map +0 -1
  193. package/dist/handlers/subject/post.handler.d.ts.map +0 -1
  194. package/dist/handlers/utils/consent-enrichment.d.ts.map +0 -1
  195. package/dist/init.d.ts.map +0 -1
  196. package/dist/middleware/auth/index.d.ts.map +0 -1
  197. package/dist/middleware/auth/validate-api-key.d.ts.map +0 -1
  198. package/dist/middleware/cors/cors.d.ts.map +0 -1
  199. package/dist/middleware/cors/index.d.ts +0 -30
  200. package/dist/middleware/cors/index.d.ts.map +0 -1
  201. package/dist/middleware/cors/is-origin-trusted.d.ts.map +0 -1
  202. package/dist/middleware/cors/process-cors.d.ts.map +0 -1
  203. package/dist/middleware/openapi/config.d.ts.map +0 -1
  204. package/dist/middleware/openapi/handlers.d.ts.map +0 -1
  205. package/dist/middleware/openapi/index.d.ts +0 -12
  206. package/dist/middleware/openapi/index.d.ts.map +0 -1
  207. package/dist/middleware/process-ip/index.d.ts.map +0 -1
  208. package/dist/router.d.ts.map +0 -1
  209. package/dist/routes/consent.d.ts.map +0 -1
  210. package/dist/routes/index.d.ts +0 -10
  211. package/dist/routes/index.d.ts.map +0 -1
  212. package/dist/routes/init.d.ts.map +0 -1
  213. package/dist/routes/status.d.ts.map +0 -1
  214. package/dist/routes/subject.d.ts.map +0 -1
  215. package/dist/types/api.d.ts.map +0 -1
  216. package/dist/types/index.d.ts.map +0 -1
  217. package/dist/utils/create-telemetry-options.d.ts.map +0 -1
  218. package/dist/utils/env.d.ts.map +0 -1
  219. package/dist/utils/extract-error-message.d.ts.map +0 -1
  220. package/dist/utils/index.d.ts +0 -4
  221. package/dist/utils/index.d.ts.map +0 -1
  222. package/dist/utils/instrumentation.d.ts.map +0 -1
  223. package/dist/utils/logger.d.ts.map +0 -1
  224. package/dist/utils/metrics.d.ts.map +0 -1
  225. package/dist/version.d.ts +0 -2
  226. package/dist/version.d.ts.map +0 -1
  227. package/knip.json +0 -31
  228. package/rslib.config.ts +0 -93
  229. package/src/cache/adapters/cloudflare-kv.ts +0 -71
  230. package/src/cache/adapters/index.ts +0 -22
  231. package/src/cache/adapters/memory.ts +0 -111
  232. package/src/cache/adapters/upstash-redis.ts +0 -113
  233. package/src/cache/gvl-resolver.ts +0 -289
  234. package/src/cache/index.ts +0 -34
  235. package/src/cache/keys.ts +0 -68
  236. package/src/cache/types.ts +0 -66
  237. package/src/core.ts +0 -369
  238. package/src/db/migrator/index.ts +0 -80
  239. package/src/db/registry/consent-policy.test.ts +0 -451
  240. package/src/db/registry/consent-policy.ts +0 -82
  241. package/src/db/registry/consent-purpose.test.ts +0 -428
  242. package/src/db/registry/consent-purpose.ts +0 -61
  243. package/src/db/registry/domain.test.ts +0 -445
  244. package/src/db/registry/domain.ts +0 -91
  245. package/src/db/registry/index.ts +0 -14
  246. package/src/db/registry/subject.test.ts +0 -371
  247. package/src/db/registry/subject.ts +0 -126
  248. package/src/db/registry/types.ts +0 -10
  249. package/src/db/registry/utils/generate-id.test.ts +0 -216
  250. package/src/db/registry/utils/generate-id.ts +0 -133
  251. package/src/db/registry/utils.ts +0 -133
  252. package/src/db/schema/1.0.0/audit-log.ts +0 -15
  253. package/src/db/schema/1.0.0/consent-policy.ts +0 -14
  254. package/src/db/schema/1.0.0/consent-purpose.ts +0 -14
  255. package/src/db/schema/1.0.0/consent-record.ts +0 -10
  256. package/src/db/schema/1.0.0/consent.ts +0 -20
  257. package/src/db/schema/1.0.0/domain.ts +0 -12
  258. package/src/db/schema/1.0.0/index.ts +0 -48
  259. package/src/db/schema/1.0.0/subject.ts +0 -11
  260. package/src/db/schema/2.0.0/audit-log.ts +0 -18
  261. package/src/db/schema/2.0.0/consent-policy.ts +0 -28
  262. package/src/db/schema/2.0.0/consent-purpose.ts +0 -12
  263. package/src/db/schema/2.0.0/consent.ts +0 -28
  264. package/src/db/schema/2.0.0/domain.ts +0 -12
  265. package/src/db/schema/2.0.0/index.ts +0 -47
  266. package/src/db/schema/2.0.0/subject.ts +0 -13
  267. package/src/db/schema/index.ts +0 -15
  268. package/src/db/tenant-scope.test.ts +0 -747
  269. package/src/db/tenant-scope.ts +0 -103
  270. package/src/define-config.ts +0 -19
  271. package/src/handlers/consent/check.handler.ts +0 -126
  272. package/src/handlers/init/geo.test.ts +0 -317
  273. package/src/handlers/init/geo.ts +0 -195
  274. package/src/handlers/init/index.test.ts +0 -205
  275. package/src/handlers/init/index.ts +0 -114
  276. package/src/handlers/init/translations.test.ts +0 -121
  277. package/src/handlers/init/translations.ts +0 -69
  278. package/src/handlers/status/status.handler.test.ts +0 -155
  279. package/src/handlers/status/status.handler.ts +0 -51
  280. package/src/handlers/subject/get.handler.ts +0 -92
  281. package/src/handlers/subject/list.handler.ts +0 -92
  282. package/src/handlers/subject/patch.handler.ts +0 -119
  283. package/src/handlers/subject/post.handler.test.ts +0 -294
  284. package/src/handlers/subject/post.handler.ts +0 -268
  285. package/src/handlers/utils/consent-enrichment.test.ts +0 -380
  286. package/src/handlers/utils/consent-enrichment.ts +0 -218
  287. package/src/init.test.ts +0 -122
  288. package/src/init.ts +0 -88
  289. package/src/middleware/auth/index.ts +0 -11
  290. package/src/middleware/auth/validate-api-key.test.ts +0 -86
  291. package/src/middleware/auth/validate-api-key.ts +0 -107
  292. package/src/middleware/cors/cors.test.ts +0 -135
  293. package/src/middleware/cors/cors.ts +0 -186
  294. package/src/middleware/cors/is-origin-trusted.test.ts +0 -164
  295. package/src/middleware/cors/is-origin-trusted.ts +0 -130
  296. package/src/middleware/cors/process-cors.ts +0 -91
  297. package/src/middleware/openapi/config.ts +0 -29
  298. package/src/middleware/openapi/handlers.ts +0 -34
  299. package/src/middleware/process-ip/index.test.ts +0 -193
  300. package/src/middleware/process-ip/index.ts +0 -199
  301. package/src/router.ts +0 -15
  302. package/src/routes/consent.ts +0 -52
  303. package/src/routes/init.ts +0 -105
  304. package/src/routes/status.ts +0 -46
  305. package/src/routes/subject.ts +0 -152
  306. package/src/types/api.ts +0 -48
  307. package/src/types/index.ts +0 -391
  308. package/src/utils/create-telemetry-options.test.ts +0 -286
  309. package/src/utils/create-telemetry-options.ts +0 -229
  310. package/src/utils/env.ts +0 -84
  311. package/src/utils/extract-error-message.ts +0 -21
  312. package/src/utils/instrumentation.test.ts +0 -183
  313. package/src/utils/instrumentation.ts +0 -194
  314. package/src/utils/logger.ts +0 -41
  315. package/src/utils/metrics.test.ts +0 -311
  316. package/src/utils/metrics.ts +0 -402
  317. package/src/utils/telemetry-pii.test.ts +0 -323
  318. package/src/version.ts +0 -2
  319. package/tsconfig.json +0 -11
  320. package/vitest.config.ts +0 -28
  321. /package/{src/db/adapters/drizzle.ts → dist-types/db/adapters/drizzle.d.ts} +0 -0
  322. /package/{src/db/adapters/index.ts → dist-types/db/adapters/index.d.ts} +0 -0
  323. /package/{src/db/adapters/kysely.ts → dist-types/db/adapters/kysely.d.ts} +0 -0
  324. /package/{src/db/adapters/mongo.ts → dist-types/db/adapters/mongo.d.ts} +0 -0
  325. /package/{src/db/adapters/prisma.ts → dist-types/db/adapters/prisma.d.ts} +0 -0
  326. /package/{src/db/adapters/typeorm.ts → dist-types/db/adapters/typeorm.d.ts} +0 -0
  327. /package/{src/utils/index.ts → dist-types/utils/index.d.ts} +0 -0
@@ -0,0 +1,211 @@
1
+ ---
2
+ title: API Endpoints
3
+ description: Full reference for every c15t consent backend endpoint.
4
+ ---
5
+ All endpoints are relative to your configured `basePath` (e.g. `/api/c15t`).
6
+
7
+ > ℹ️ **Info:**
8
+ > The backend auto-generates interactive API docs at \{basePath}/docs using your OpenAPI spec. Visit this URL in a browser to explore endpoints with a visual UI.
9
+
10
+ ## GET /init
11
+
12
+ Returns the initial consent state for a client. This is the first call made by the frontend SDKs.
13
+
14
+ **Response:**
15
+
16
+ ```json
17
+ {
18
+ "jurisdiction": "GDPR",
19
+ "location": {
20
+ "countryCode": "DE",
21
+ "regionCode": "BY"
22
+ },
23
+ "translations": {
24
+ "language": "de",
25
+ "translations": { "...": "..." }
26
+ },
27
+ "branding": "c15t",
28
+ "gvl": null
29
+ }
30
+ ```
31
+
32
+ |Field|Description|
33
+ |--|--|
34
+ |`jurisdiction`|Detected regulation (`GDPR`, `UK_GDPR`, `CCPA`, etc.)|
35
+ |`location`|Geo-location from IP address|
36
+ |`translations`|Server-side translations based on `Accept-Language`|
37
+ |`branding`|Branding configuration|
38
+ |`gvl`|Global Vendor List (if IAB TCF is enabled)|
39
+
40
+ ## GET /status
41
+
42
+ Health check endpoint. Returns server version and client info.
43
+
44
+ **Response:**
45
+
46
+ ```json
47
+ {
48
+ "version": "1.8.0",
49
+ "timestamp": "2026-02-11T12:00:00.000Z",
50
+ "client": {
51
+ "ip": "192.168.1.0",
52
+ "acceptLanguage": "en-US",
53
+ "userAgent": "Mozilla/5.0 ...",
54
+ "region": { "countryCode": "US", "regionCode": "CA" }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## POST /subjects
60
+
61
+ Records a consent event. This is an append-only operation — every call creates a new consent record.
62
+
63
+ **Request:**
64
+
65
+ ```json
66
+ {
67
+ "type": "cookie_banner",
68
+ "subjectId": "sub_abc123",
69
+ "domain": "example.com",
70
+ "preferences": {
71
+ "necessary": true,
72
+ "measurement": true,
73
+ "marketing": false
74
+ },
75
+ "givenAt": 1707648000000,
76
+ "metadata": {}
77
+ }
78
+ ```
79
+
80
+ |Field|Type|Required|Description|
81
+ |--|--|--|--|
82
+ |`type`|string|Yes|`cookie_banner`, `privacy_policy`, `dpa`, `terms_and_conditions`, `marketing_communications`, `age_verification`, `other`|
83
+ |`subjectId`|string|Yes|Client-generated subject identifier|
84
+ |`domain`|string|Yes|Domain where consent was given|
85
+ |`preferences`|object|No|Consent category preferences (for `cookie_banner` type)|
86
+ |`givenAt`|number|Yes|Epoch timestamp|
87
+ |`policyId`|string|No|Associated policy ID|
88
+ |`metadata`|object|No|Arbitrary metadata|
89
+ |`externalSubjectId`|string|No|External user ID for cross-device linking|
90
+ |`identityProvider`|string|No|Identity provider name|
91
+
92
+ **Response:**
93
+
94
+ ```json
95
+ {
96
+ "subject": { "id": "sub_abc123", "...": "..." },
97
+ "consent": { "id": "con_xyz789", "type": "cookie_banner", "...": "..." }
98
+ }
99
+ ```
100
+
101
+ ## GET /subjects/:id
102
+
103
+ Retrieves consent status for a subject.
104
+
105
+ **Query Parameters:**
106
+
107
+ |Parameter|Description|
108
+ |--|--|
109
+ |`type`|Comma-separated consent types to filter (e.g. `cookie_banner,privacy_policy`)|
110
+
111
+ **Response:**
112
+
113
+ ```json
114
+ {
115
+ "subject": { "id": "sub_abc123" },
116
+ "consents": [
117
+ {
118
+ "id": "con_xyz789",
119
+ "type": "cookie_banner",
120
+ "givenAt": "2026-02-11T12:00:00.000Z",
121
+ "jurisdiction": "GDPR",
122
+ "preferences": { "necessary": true, "measurement": true }
123
+ }
124
+ ],
125
+ "isValid": true
126
+ }
127
+ ```
128
+
129
+ ## PATCH /subjects/:id
130
+
131
+ Links a subject to an external user ID for cross-device consent resolution.
132
+
133
+ **Request:**
134
+
135
+ ```json
136
+ {
137
+ "externalId": "user_12345",
138
+ "identityProvider": "auth0"
139
+ }
140
+ ```
141
+
142
+ **Response:**
143
+
144
+ ```json
145
+ {
146
+ "subject": {
147
+ "id": "sub_abc123",
148
+ "externalId": "user_12345",
149
+ "identityProvider": "auth0"
150
+ }
151
+ }
152
+ ```
153
+
154
+ ## GET /consents/check
155
+
156
+ Check consent status by external ID — useful for cross-device consent resolution.
157
+
158
+ **Query Parameters:**
159
+
160
+ |Parameter|Description|
161
+ |--|--|
162
+ |`externalId`|The external user ID to look up|
163
+ |`type`|Comma-separated consent types|
164
+
165
+ **Response:**
166
+
167
+ ```json
168
+ {
169
+ "found": true,
170
+ "consents": [
171
+ { "id": "con_xyz789", "type": "cookie_banner", "...": "..." }
172
+ ]
173
+ }
174
+ ```
175
+
176
+ ## GET /subjects (Authenticated)
177
+
178
+ List subjects by external ID. Requires an API key.
179
+
180
+ **Headers:**
181
+
182
+ ```
183
+ Authorization: Bearer sk_live_abc123
184
+ ```
185
+
186
+ **Query Parameters:**
187
+
188
+ |Parameter|Description|
189
+ |--|--|
190
+ |`externalId`|The external user ID to search|
191
+
192
+ **Response:**
193
+
194
+ ```json
195
+ {
196
+ "subjects": [
197
+ { "id": "sub_abc123", "externalId": "user_12345" }
198
+ ]
199
+ }
200
+ ```
201
+
202
+ > ℹ️ **Info:**
203
+ > This endpoint requires API key authentication. Configure API keys in the apiKeys option. The key is passed via the Authorization: Bearer header using timing-safe comparison.
204
+
205
+ ## GET /spec.json
206
+
207
+ Returns the OpenAPI 3.1 specification for the consent API.
208
+
209
+ ## GET /docs
210
+
211
+ Serves the interactive API documentation UI (Scalar).
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Caching
3
+ description: Add a caching layer to your self-hosted c15t backend for production performance.
4
+ ---
5
+ The backend includes a caching layer for frequently accessed data like GVL (Global Vendor List) lookups and custom server-side translations. By default, an in-memory cache is used — suitable for single-instance deployments. For production with multiple instances, plug in Redis or Cloudflare KV so all instances share the same cache.
6
+
7
+ ## In-Memory (Default)
8
+
9
+ No configuration needed. The default memory cache has a 5-minute TTL and is suitable for development and single-instance deployments.
10
+
11
+ ## Upstash Redis
12
+
13
+ ```ts title="c15t.ts"
14
+ import { c15tInstance } from '@c15t/backend';
15
+ import { createUpstashRedisAdapter } from '@c15t/backend/cache';
16
+
17
+ export const c15t = c15tInstance({
18
+ // ...
19
+ cache: {
20
+ adapter: createUpstashRedisAdapter({
21
+ url: process.env.UPSTASH_REDIS_REST_URL,
22
+ token: process.env.UPSTASH_REDIS_REST_TOKEN,
23
+ }),
24
+ },
25
+ });
26
+ ```
27
+
28
+ If you already have an Upstash Redis client, pass it directly:
29
+
30
+ ```ts
31
+ import { createUpstashRedisAdapterFromClient } from '@c15t/backend/cache';
32
+ import { Redis } from '@upstash/redis';
33
+
34
+ const redis = new Redis({
35
+ url: process.env.UPSTASH_REDIS_REST_URL,
36
+ token: process.env.UPSTASH_REDIS_REST_TOKEN,
37
+ });
38
+
39
+ const adapter = createUpstashRedisAdapterFromClient(redis);
40
+ ```
41
+
42
+ ## Cloudflare KV
43
+
44
+ For Cloudflare Workers deployments:
45
+
46
+ ```ts title="worker.ts"
47
+ import { c15tInstance } from '@c15t/backend';
48
+ import { createCloudflareKVAdapter } from '@c15t/backend/cache';
49
+
50
+ export default {
51
+ async fetch(request: Request, env: Env) {
52
+ const c15t = c15tInstance({
53
+ // ...
54
+ cache: {
55
+ adapter: createCloudflareKVAdapter(env.GVL_CACHE),
56
+ },
57
+ });
58
+
59
+ return c15t.handler(request);
60
+ },
61
+ };
62
+ ```
63
+
64
+ ## Custom Cache Adapter
65
+
66
+ Implement the `CacheAdapter` interface to use any cache backend:
67
+
68
+ ```ts
69
+ import type { CacheAdapter } from '@c15t/backend/cache';
70
+
71
+ const customCache: CacheAdapter = {
72
+ async get<T>(key: string): Promise<T | null> {
73
+ // retrieve from your cache
74
+ },
75
+ async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {
76
+ // store in your cache
77
+ },
78
+ async delete(key: string): Promise<void> {
79
+ // remove from cache
80
+ },
81
+ async has(key: string): Promise<boolean> {
82
+ // check existence
83
+ },
84
+ };
85
+ ```
@@ -0,0 +1,128 @@
1
+ ---
2
+ title: Database Setup
3
+ description: Configure a database adapter for your self-hosted c15t backend.
4
+ ---
5
+ The c15t backend requires a database to store consent records, subjects, audit logs, and policies. Five adapters are supported — choose the one that matches your existing stack.
6
+
7
+ ## Adapters
8
+
9
+ ### Kysely
10
+
11
+ ```ts title="c15t.ts"
12
+ import { c15tInstance } from '@c15t/backend';
13
+ import { kyselyAdapter } from '@c15t/backend/db/adapters/kysely';
14
+ import { Kysely, PostgresDialect } from 'kysely';
15
+ import { Pool } from 'pg';
16
+
17
+ const db = new Kysely({
18
+ dialect: new PostgresDialect({
19
+ pool: new Pool({ connectionString: process.env.DATABASE_URL }),
20
+ }),
21
+ });
22
+
23
+ export const c15t = c15tInstance({
24
+ adapter: kyselyAdapter(db),
25
+ trustedOrigins: ['https://example.com'],
26
+ });
27
+ ```
28
+
29
+ ### Drizzle
30
+
31
+ ```ts title="c15t.ts"
32
+ import { c15tInstance } from '@c15t/backend';
33
+ import { drizzleAdapter } from '@c15t/backend/db/adapters/drizzle';
34
+ import { drizzle } from 'drizzle-orm/node-postgres';
35
+
36
+ const db = drizzle(process.env.DATABASE_URL);
37
+
38
+ export const c15t = c15tInstance({
39
+ adapter: drizzleAdapter(db),
40
+ trustedOrigins: ['https://example.com'],
41
+ });
42
+ ```
43
+
44
+ ### Prisma
45
+
46
+ ```ts title="c15t.ts"
47
+ import { c15tInstance } from '@c15t/backend';
48
+ import { prismaAdapter } from '@c15t/backend/db/adapters/prisma';
49
+ import { PrismaClient } from '@prisma/client';
50
+
51
+ const prisma = new PrismaClient();
52
+
53
+ export const c15t = c15tInstance({
54
+ adapter: prismaAdapter(prisma),
55
+ trustedOrigins: ['https://example.com'],
56
+ });
57
+ ```
58
+
59
+ ### TypeORM
60
+
61
+ ```ts title="c15t.ts"
62
+ import { c15tInstance } from '@c15t/backend';
63
+ import { typeormAdapter } from '@c15t/backend/db/adapters/typeorm';
64
+ import { DataSource } from 'typeorm';
65
+
66
+ const dataSource = new DataSource({
67
+ type: 'postgres',
68
+ url: process.env.DATABASE_URL,
69
+ });
70
+
71
+ export const c15t = c15tInstance({
72
+ adapter: typeormAdapter(dataSource),
73
+ trustedOrigins: ['https://example.com'],
74
+ });
75
+ ```
76
+
77
+ ### MongoDB
78
+
79
+ ```ts title="c15t.ts"
80
+ import { c15tInstance } from '@c15t/backend';
81
+ import { mongoAdapter } from '@c15t/backend/db/adapters/mongo';
82
+ import { MongoClient } from 'mongodb';
83
+
84
+ const client = new MongoClient(process.env.MONGODB_URI);
85
+
86
+ export const c15t = c15tInstance({
87
+ adapter: mongoAdapter(client),
88
+ trustedOrigins: ['https://example.com'],
89
+ });
90
+ ```
91
+
92
+ ## Migrations
93
+
94
+ To create & update the database you can use the migrator which is included in the CLI:
95
+
96
+ |Package manager|Command|
97
+ |:--|:--|
98
+ |npm|`npx @c15t/cli@rc`|
99
+ |pnpm|`pnpm dlx @c15t/cli@rc`|
100
+ |yarn|`yarn dlx @c15t/cli@rc`|
101
+ |bun|`bunx @c15t/cli@rc`|
102
+
103
+ ## Table Prefix
104
+
105
+ If you share a database with other applications, use `tablePrefix` to namespace the c15t tables:
106
+
107
+ ```ts
108
+ c15tInstance({
109
+ adapter: kyselyAdapter(db),
110
+ tablePrefix: 'c15t_',
111
+ trustedOrigins: ['https://example.com'],
112
+ });
113
+ ```
114
+
115
+ This prefixes all table names (e.g. `c15t_subject`, `c15t_consent`, `c15t_audit_log`).
116
+
117
+ ## Schema Overview
118
+
119
+ The backend creates and manages these tables:
120
+
121
+ |Table|Purpose|
122
+ |--|--|
123
+ |`subject`|Consent subjects (users/devices). Tracks identification status and external IDs.|
124
+ |`consent`|Append-only consent records. Stores preferences, jurisdiction, IP, and timestamps.|
125
+ |`domain`|Domain/website configuration for multi-domain setups.|
126
+ |`consent_policy`|Versioned consent policies.|
127
+ |`consent_purpose`|Maps consent purposes to categories.|
128
+ |`audit_log`|Immutable log of all consent-related actions.|
@@ -0,0 +1,248 @@
1
+ ---
2
+ title: Edge Deployment
3
+ description: Run consent policy resolution at the edge for faster initial banner loads.
4
+ ---
5
+ The `/init` endpoint determines consent policy from geo headers, resolves translations, and optionally fetches the GVL. None of this requires a database. The `@c15t/backend/edge` export lets you run this logic in edge runtimes (Vercel Middleware, Cloudflare Workers, Deno Deploy) so the consent banner resolves from the nearest PoP instead of round-tripping to your origin.
6
+
7
+ ```
8
+ Standard: Browser → Origin (single region) → c15tInstance(/init) → Response
9
+ Edge: Browser → Edge (nearest PoP) → c15tEdgeInit → Response
10
+ ```
11
+
12
+ ## When to use this
13
+
14
+ * Your origin server is in a single region and users are globally distributed
15
+ * You want the consent banner to appear as fast as possible (edge latency is typically 10-50ms vs 100-300ms to origin)
16
+ * You already use edge middleware for other purposes (auth, redirects, A/B testing)
17
+
18
+ You do **not** need this if:
19
+
20
+ * Your origin is already multi-region or on a platform like Cloudflare Workers
21
+ * Consent banner latency is not a concern for your use case
22
+
23
+ ## Setup
24
+
25
+ ### 1. Extract shared config
26
+
27
+ Keep your policy configuration in a shared file so both the edge handler and origin handler stay in sync:
28
+
29
+ ```ts title="lib/consent-config.ts"
30
+ import type { C15TEdgeOptions } from '@c15t/backend/edge';
31
+
32
+ export const consentConfig = {
33
+ trustedOrigins: ['https://myapp.com'],
34
+ policyPacks: [
35
+ {
36
+ id: 'eu_gdpr',
37
+ match: { countries: ['DE', 'FR', 'IT', 'ES', 'NL', 'PL'] },
38
+ consent: { model: 'opt-in' },
39
+ ui: { mode: 'banner' },
40
+ },
41
+ {
42
+ id: 'us_ca',
43
+ match: { regions: [{ country: 'US', region: 'CA' }] },
44
+ consent: { model: 'opt-out' },
45
+ ui: { mode: 'banner' },
46
+ },
47
+ ],
48
+ policySnapshot: {
49
+ signingKey: process.env.SNAPSHOT_KEY!,
50
+ },
51
+ } satisfies C15TEdgeOptions;
52
+ ```
53
+
54
+ ### 2. Create edge middleware
55
+
56
+ **Vercel Middleware**
57
+
58
+ ```ts title="middleware.ts"
59
+ import { c15tEdgeInit } from '@c15t/backend/edge';
60
+ import { consentConfig } from './lib/consent-config';
61
+
62
+ const initHandler = c15tEdgeInit(consentConfig);
63
+
64
+ export async function middleware(request: Request) {
65
+ const url = new URL(request.url);
66
+ if (url.pathname === '/api/c15t/init') {
67
+ return initHandler(request);
68
+ }
69
+ }
70
+
71
+ export const config = {
72
+ matcher: '/api/c15t/init',
73
+ };
74
+ ```
75
+
76
+ **Cloudflare Workers**
77
+
78
+ ```ts title="worker.ts"
79
+ import { c15tEdgeInit } from '@c15t/backend/edge';
80
+ import { consentConfig } from './lib/consent-config';
81
+
82
+ const initHandler = c15tEdgeInit(consentConfig);
83
+
84
+ export default {
85
+ async fetch(request: Request) {
86
+ const url = new URL(request.url);
87
+ if (url.pathname === '/api/c15t/init') {
88
+ return initHandler(request);
89
+ }
90
+ // Forward other requests to origin
91
+ return fetch(request);
92
+ },
93
+ };
94
+ ```
95
+
96
+ **Deno Deploy**
97
+
98
+ ```ts title="main.ts"
99
+ import { c15tEdgeInit } from '@c15t/backend/edge';
100
+ import { consentConfig } from './lib/consent-config.ts';
101
+
102
+ const initHandler = c15tEdgeInit(consentConfig);
103
+
104
+ Deno.serve(async (request) => {
105
+ const url = new URL(request.url);
106
+ if (url.pathname === '/api/c15t/init') {
107
+ return initHandler(request);
108
+ }
109
+ return new Response('Not found', { status: 404 });
110
+ });
111
+ ```
112
+
113
+ ### 3. Keep your origin handler unchanged
114
+
115
+ The origin API route still handles all database-dependent endpoints (`/subjects`, `/consents`, `/status`). The only difference is that `/init` requests no longer reach the origin — they're intercepted at the edge.
116
+
117
+ ```ts title="app/api/c15t/[[...path]]/route.ts"
118
+ import { c15tInstance } from '@c15t/backend';
119
+ import { consentConfig } from '@/lib/consent-config';
120
+
121
+ const c15t = c15tInstance({
122
+ adapter: yourDbAdapter,
123
+ ...consentConfig,
124
+ });
125
+
126
+ export const { GET, POST } = c15t;
127
+ ```
128
+
129
+ ## Configuration
130
+
131
+ `c15tEdgeInit` accepts `C15TEdgeOptions` — the same fields as `c15tInstance` minus the database-related options (`adapter`, `tablePrefix`, `basePath`, `openapi`, `ipAddress`, `apiKeys`, `background`).
132
+
133
+ |Option|Required|Description|
134
+ |--|--|--|
135
+ |`trustedOrigins`|Yes|Allowed CORS origins — must match your origin handler|
136
+ |`policyPacks`|No|Regional policy configuration|
137
+ |`policySnapshot`|No|Signing key for policy snapshot tokens|
138
+ |`iab`|No|IAB TCF configuration|
139
+ |`i18n`|No|Translation profiles|
140
+ |`branding`|No|Banner branding (default: `"c15t"`)|
141
+ |`appName`|No|Application name (default: `"c15t"`)|
142
+ |`tenantId`|No|Tenant ID for multi-tenant deployments|
143
+ |`cache`|No|External cache adapter for GVL|
144
+ |`disableGeoLocation`|No|Disable geo-location detection|
145
+ |`telemetry`|No|OpenTelemetry configuration|
146
+ |`logger`|No|Logger configuration|
147
+
148
+ > ℹ️ **Info:**
149
+ > The edge handler and origin handler must share the same policyPacks, policySnapshot, trustedOrigins, iab, and i18n configuration. If they diverge, /init will return policies that don't match what the database endpoints expect. Use a shared config file as shown above.
150
+
151
+ ## How it works
152
+
153
+ The edge handler:
154
+
155
+ 1. **Reads geo headers** — Vercel sets `x-vercel-ip-country` and `x-vercel-ip-country-region` automatically. Cloudflare sets `cf-ipcountry`. The handler checks all common provider headers.
156
+ 2. **Resolves jurisdiction** — Maps the country/region to a jurisdiction code (GDPR, CCPA, UK\_GDPR, etc.)
157
+ 3. **Matches a policy pack** — Finds the first matching policy for the visitor's location
158
+ 4. **Resolves translations** — Picks the right language from `Accept-Language` and your i18n config
159
+ 5. **Signs a snapshot token** — Creates a JWT proving which policy was served (if `policySnapshot` is configured)
160
+ 6. **Handles CORS** — Validates the `Origin` header against `trustedOrigins` and sets appropriate headers
161
+
162
+ All of this uses the same functions as the full `c15tInstance` — the edge handler is not a reimplementation, it's the same code without the database layer.
163
+
164
+ ## Caching considerations
165
+
166
+ Edge isolates have short-lived memory. The in-memory GVL cache resets on each cold start. For production:
167
+
168
+ * **Bundle GVL translations** using `iab.bundled` to avoid fetch latency entirely
169
+ * **Use an external cache** (Upstash Redis, Cloudflare KV) via the `cache.adapter` option to share cached data across isolates — see the [Caching guide](/docs/self-host/guides/caching) for setup
170
+
171
+ ## Custom consent cookie — resolveConsent
172
+
173
+ > ℹ️ **Info:**
174
+ > Experimental — this API may change in future versions.
175
+
176
+ If you manage your own consent cookie and just need to know **which categories to load** for a given visitor, use `resolveConsent` instead of `c15tEdgeInit`. It's a lightweight, fully synchronous function that returns the matched policy and default consent state — no translations, GVL, branding, or snapshot tokens.
177
+
178
+ ```ts title="middleware.ts"
179
+ import { resolveConsent } from '@c15t/backend/edge';
180
+
181
+ const policyPacks = [
182
+ {
183
+ id: 'eu_gdpr',
184
+ match: { countries: ['DE', 'FR', 'IT', 'ES'] },
185
+ consent: {
186
+ model: 'opt-in',
187
+ categories: ['necessary', 'marketing', 'measurement'],
188
+ },
189
+ ui: { mode: 'banner' },
190
+ },
191
+ {
192
+ id: 'us_default',
193
+ match: { isDefault: true },
194
+ consent: {
195
+ model: 'opt-out',
196
+ categories: ['necessary', 'marketing', 'measurement'],
197
+ gpc: true,
198
+ },
199
+ ui: { mode: 'banner' },
200
+ },
201
+ ];
202
+
203
+ export function middleware(request: Request) {
204
+ const consent = resolveConsent(request, { policyPacks });
205
+
206
+ // consent.model → "opt-in" | "opt-out" | "none" | "iab"
207
+ // consent.showBanner → true
208
+ // consent.jurisdiction → "GDPR"
209
+ // consent.gpc → false
210
+ // consent.defaults → {
211
+ // necessary: { granted: true, required: true },
212
+ // marketing: { granted: false, required: false },
213
+ // measurement: { granted: false, required: false },
214
+ // }
215
+
216
+ // Read your own cookie, merge with consent.defaults,
217
+ // decide which scripts/tags to load
218
+ }
219
+ ```
220
+
221
+ ### Default consent by model
222
+
223
+ |Model|`necessary`|Other categories|Notes|
224
+ |--|--|--|--|
225
+ |`opt-in`|granted, required|**not granted**|Unless listed in `preselectedCategories`|
226
+ |`opt-out`|granted, required|**granted**|GPC signal can override `marketing`/`measurement` to not granted|
227
+ |`none`|granted, required|**granted**|No banner shown|
228
+
229
+ ### `resolveConsent` vs `c15tEdgeInit`
230
+
231
+ ||`resolveConsent`|`c15tEdgeInit`|
232
+ |--|--|--|
233
+ |**Use case**|Custom consent cookie|Drop-in `/init` replacement|
234
+ |**Sync**|Yes|No (async — signs JWT, fetches GVL)|
235
+ |**Returns**|Policy + default consent state|Full `/init` JSON payload|
236
+ |**CORS**|Not handled (your middleware)|Built-in|
237
+ |**Translations**|Not included|Included|
238
+ |**Snapshot token**|Not included|Included|
239
+
240
+ ## What stays on the origin
241
+
242
+ Only `/init` moves to the edge. These endpoints still require the origin server:
243
+
244
+ * `POST /subjects` — creates/updates consent subjects (needs DB)
245
+ * `POST /consents` — records consent decisions (needs DB)
246
+ * `GET /status` — checks current consent status (needs DB)
247
+
248
+ The edge handler is a single-purpose optimization for the one endpoint that doesn't need persistent storage.