@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,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,251 @@
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) → unstable_c15tEdgeInit → Response
10
+ ```
11
+
12
+ > ℹ️ **Info:**
13
+ > The edge runtime exports in @c15t/backend/edge are unstable in 2.0. Use the unstable\_-prefixed callables and expect API changes or removal in a future release.
14
+
15
+ ## When to use this
16
+
17
+ * Your origin server is in a single region and users are globally distributed
18
+ * You want the consent banner to appear as fast as possible (edge latency is typically 10-50ms vs 100-300ms to origin)
19
+ * You already use edge middleware for other purposes (auth, redirects, A/B testing)
20
+
21
+ You do **not** need this if:
22
+
23
+ * Your origin is already multi-region or on a platform like Cloudflare Workers
24
+ * Consent banner latency is not a concern for your use case
25
+
26
+ ## Setup
27
+
28
+ ### 1. Extract shared config
29
+
30
+ Keep your policy configuration in a shared file so both the edge handler and origin handler stay in sync:
31
+
32
+ ```ts title="lib/consent-config.ts"
33
+ import type { C15TEdgeOptions } from '@c15t/backend/edge';
34
+
35
+ export const consentConfig = {
36
+ trustedOrigins: ['https://myapp.com'],
37
+ policyPacks: [
38
+ {
39
+ id: 'eu_gdpr',
40
+ match: { countries: ['DE', 'FR', 'IT', 'ES', 'NL', 'PL'] },
41
+ consent: { model: 'opt-in' },
42
+ ui: { mode: 'banner' },
43
+ },
44
+ {
45
+ id: 'us_ca',
46
+ match: { regions: [{ country: 'US', region: 'CA' }] },
47
+ consent: { model: 'opt-out' },
48
+ ui: { mode: 'banner' },
49
+ },
50
+ ],
51
+ policySnapshot: {
52
+ signingKey: process.env.SNAPSHOT_KEY!,
53
+ },
54
+ } satisfies C15TEdgeOptions;
55
+ ```
56
+
57
+ ### 2. Create edge middleware
58
+
59
+ **Vercel Middleware**
60
+
61
+ ```ts title="middleware.ts"
62
+ import { unstable_c15tEdgeInit } from '@c15t/backend/edge';
63
+ import { consentConfig } from './lib/consent-config';
64
+
65
+ const initHandler = unstable_c15tEdgeInit(consentConfig);
66
+
67
+ export async function middleware(request: Request) {
68
+ const url = new URL(request.url);
69
+ if (url.pathname === '/api/c15t/init') {
70
+ return initHandler(request);
71
+ }
72
+ }
73
+
74
+ export const config = {
75
+ matcher: '/api/c15t/init',
76
+ };
77
+ ```
78
+
79
+ **Cloudflare Workers**
80
+
81
+ ```ts title="worker.ts"
82
+ import { unstable_c15tEdgeInit } from '@c15t/backend/edge';
83
+ import { consentConfig } from './lib/consent-config';
84
+
85
+ const initHandler = unstable_c15tEdgeInit(consentConfig);
86
+
87
+ export default {
88
+ async fetch(request: Request) {
89
+ const url = new URL(request.url);
90
+ if (url.pathname === '/api/c15t/init') {
91
+ return initHandler(request);
92
+ }
93
+ // Forward other requests to origin
94
+ return fetch(request);
95
+ },
96
+ };
97
+ ```
98
+
99
+ **Deno Deploy**
100
+
101
+ ```ts title="main.ts"
102
+ import { unstable_c15tEdgeInit } from '@c15t/backend/edge';
103
+ import { consentConfig } from './lib/consent-config.ts';
104
+
105
+ const initHandler = unstable_c15tEdgeInit(consentConfig);
106
+
107
+ Deno.serve(async (request) => {
108
+ const url = new URL(request.url);
109
+ if (url.pathname === '/api/c15t/init') {
110
+ return initHandler(request);
111
+ }
112
+ return new Response('Not found', { status: 404 });
113
+ });
114
+ ```
115
+
116
+ ### 3. Keep your origin handler unchanged
117
+
118
+ 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.
119
+
120
+ ```ts title="app/api/c15t/[[...path]]/route.ts"
121
+ import { c15tInstance } from '@c15t/backend';
122
+ import { consentConfig } from '@/lib/consent-config';
123
+
124
+ const c15t = c15tInstance({
125
+ adapter: yourDbAdapter,
126
+ ...consentConfig,
127
+ });
128
+
129
+ export const { GET, POST } = c15t;
130
+ ```
131
+
132
+ ## Configuration
133
+
134
+ `unstable_c15tEdgeInit` accepts `C15TEdgeOptions` — the same fields as `c15tInstance` minus the database-related options (`adapter`, `tablePrefix`, `basePath`, `openapi`, `ipAddress`, `apiKeys`, `background`).
135
+
136
+ |Option|Required|Description|
137
+ |--|--|--|
138
+ |`trustedOrigins`|Yes|Allowed CORS origins — must match your origin handler|
139
+ |`policyPacks`|No|Regional policy configuration|
140
+ |`policySnapshot`|No|Signing key for policy snapshot tokens|
141
+ |`iab`|No|IAB TCF configuration|
142
+ |`i18n`|No|Translation profiles|
143
+ |`branding`|No|Banner branding (default: `"c15t"`)|
144
+ |`appName`|No|Application name (default: `"c15t"`)|
145
+ |`tenantId`|No|Tenant ID for multi-tenant deployments|
146
+ |`cache`|No|External cache adapter for GVL|
147
+ |`disableGeoLocation`|No|Disable geo-location detection|
148
+ |`telemetry`|No|OpenTelemetry configuration|
149
+ |`logger`|No|Logger configuration|
150
+
151
+ > ℹ️ **Info:**
152
+ > 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.
153
+
154
+ ## How it works
155
+
156
+ The edge handler:
157
+
158
+ 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.
159
+ 2. **Resolves jurisdiction** — Maps the country/region to a jurisdiction code (GDPR, CCPA, UK\_GDPR, etc.)
160
+ 3. **Matches a policy pack** — Finds the first matching policy for the visitor's location
161
+ 4. **Resolves translations** — Picks the right language from `Accept-Language` and your i18n config
162
+ 5. **Signs a snapshot token** — Creates a JWT proving which policy was served (if `policySnapshot` is configured)
163
+ 6. **Handles CORS** — Validates the `Origin` header against `trustedOrigins` and sets appropriate headers
164
+
165
+ 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.
166
+
167
+ ## Caching considerations
168
+
169
+ Edge isolates have short-lived memory. The in-memory GVL cache resets on each cold start. For production:
170
+
171
+ * **Bundle GVL translations** using `iab.bundled` to avoid fetch latency entirely
172
+ * **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
173
+
174
+ ## Custom consent cookie — unstable\_resolveConsent
175
+
176
+ > ℹ️ **Info:**
177
+ > Experimental — this API may change in future versions.
178
+
179
+ If you manage your own consent cookie and just need to know **which categories to load** for a given visitor, use `unstable_resolveConsent` instead of `unstable_c15tEdgeInit`. It's a lightweight, fully synchronous function that returns the matched policy and default consent state — no translations, GVL, branding, or snapshot tokens.
180
+
181
+ ```ts title="middleware.ts"
182
+ import { unstable_resolveConsent } from '@c15t/backend/edge';
183
+
184
+ const policyPacks = [
185
+ {
186
+ id: 'eu_gdpr',
187
+ match: { countries: ['DE', 'FR', 'IT', 'ES'] },
188
+ consent: {
189
+ model: 'opt-in',
190
+ categories: ['necessary', 'marketing', 'measurement'],
191
+ },
192
+ ui: { mode: 'banner' },
193
+ },
194
+ {
195
+ id: 'us_default',
196
+ match: { isDefault: true },
197
+ consent: {
198
+ model: 'opt-out',
199
+ categories: ['necessary', 'marketing', 'measurement'],
200
+ gpc: true,
201
+ },
202
+ ui: { mode: 'banner' },
203
+ },
204
+ ];
205
+
206
+ export function middleware(request: Request) {
207
+ const consent = unstable_resolveConsent(request, { policyPacks });
208
+
209
+ // consent.model → "opt-in" | "opt-out" | "none" | "iab"
210
+ // consent.showBanner → true
211
+ // consent.jurisdiction → "GDPR"
212
+ // consent.gpc → false
213
+ // consent.defaults → {
214
+ // necessary: { granted: true, required: true },
215
+ // marketing: { granted: false, required: false },
216
+ // measurement: { granted: false, required: false },
217
+ // }
218
+
219
+ // Read your own cookie, merge with consent.defaults,
220
+ // decide which scripts/tags to load
221
+ }
222
+ ```
223
+
224
+ ### Default consent by model
225
+
226
+ |Model|`necessary`|Other categories|Notes|
227
+ |--|--|--|--|
228
+ |`opt-in`|granted, required|**not granted**|Unless listed in `preselectedCategories`|
229
+ |`opt-out`|granted, required|**granted**|GPC signal can override `marketing`/`measurement` to not granted|
230
+ |`none`|granted, required|**granted**|No banner shown|
231
+
232
+ ### `unstable_resolveConsent` vs `unstable_c15tEdgeInit`
233
+
234
+ ||`unstable_resolveConsent`|`unstable_c15tEdgeInit`|
235
+ |--|--|--|
236
+ |**Use case**|Custom consent cookie|Drop-in `/init` replacement|
237
+ |**Sync**|Yes|No (async — signs JWT, fetches GVL)|
238
+ |**Returns**|Policy + default consent state|Full `/init` JSON payload|
239
+ |**CORS**|Not handled (your middleware)|Built-in|
240
+ |**Translations**|Not included|Included|
241
+ |**Snapshot token**|Not included|Included|
242
+
243
+ ## What stays on the origin
244
+
245
+ Only `/init` moves to the edge. These endpoints still require the origin server:
246
+
247
+ * `POST /subjects` — creates/updates consent subjects (needs DB)
248
+ * `POST /consents` — records consent decisions (needs DB)
249
+ * `GET /status` — checks current consent status (needs DB)
250
+
251
+ The edge handler is a single-purpose optimization for the one endpoint that doesn't need persistent storage.
@@ -0,0 +1,142 @@
1
+ ---
2
+ title: Framework Integration
3
+ description: Mount the c15t consent backend in any JavaScript framework or runtime.
4
+ ---
5
+ The c15t backend exposes a single handler with the signature `(request: Request) => Promise<Response>`. This is the standard Fetch API, so it works with any runtime that supports it — Node.js, Bun, Deno, and Cloudflare Workers.
6
+
7
+ ## Bun
8
+
9
+ ```ts title="server.ts"
10
+ import { c15t } from './c15t';
11
+
12
+ Bun.serve({
13
+ port: 3001,
14
+ fetch: c15t.handler,
15
+ });
16
+ ```
17
+
18
+ ## Next.js (App Router)
19
+
20
+ Create a catch-all API route that forwards requests to the handler:
21
+
22
+ ```ts title="src/app/api/c15t/[[...path]]/route.ts"
23
+ import { c15t } from '@/lib/c15t';
24
+
25
+ const handler = c15t.handler;
26
+
27
+ export { handler as GET, handler as POST, handler as PUT, handler as PATCH, handler as DELETE };
28
+ ```
29
+
30
+ > ℹ️ **Info:**
31
+ > Use Next.js rewrites to proxy frontend requests through the same domain and avoid ad-blocker issues. See the Next.js Quickstart for the rewrite configuration.
32
+
33
+ ## Hono
34
+
35
+ ```ts title="server.ts"
36
+ import { Hono } from 'hono';
37
+ import { c15t } from './c15t';
38
+
39
+ const app = new Hono();
40
+
41
+ app.all('/api/c15t/*', (c) => c15t.handler(c.req.raw));
42
+
43
+ export default app;
44
+ ```
45
+
46
+ ## Express
47
+
48
+ ```ts title="server.ts"
49
+ import express from 'express';
50
+ import { c15t } from './c15t';
51
+
52
+ const app = express();
53
+
54
+ app.all('/api/c15t/*', async (req, res) => {
55
+ const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
56
+ const headers = new Headers();
57
+ for (const [key, value] of Object.entries(req.headers)) {
58
+ if (value) headers.set(key, Array.isArray(value) ? value.join(', ') : value);
59
+ }
60
+
61
+ const request = new Request(url, {
62
+ method: req.method,
63
+ headers,
64
+ body: ['GET', 'HEAD'].includes(req.method) ? undefined : req,
65
+ });
66
+
67
+ const response = await c15t.handler(request);
68
+
69
+ res.status(response.status);
70
+ response.headers.forEach((value, key) => res.setHeader(key, value));
71
+ res.send(await response.text());
72
+ });
73
+
74
+ app.listen(3001);
75
+ ```
76
+
77
+ ## Fastify
78
+
79
+ ```ts title="server.ts"
80
+ import Fastify from 'fastify';
81
+ import { c15t } from './c15t';
82
+
83
+ const app = Fastify();
84
+
85
+ app.all('/api/c15t/*', async (req, reply) => {
86
+ const url = `${req.protocol}://${req.hostname}${req.url}`;
87
+ const request = new Request(url, {
88
+ method: req.method,
89
+ headers: new Headers(req.headers as Record<string, string>),
90
+ body: ['GET', 'HEAD'].includes(req.method) ? undefined : JSON.stringify(req.body),
91
+ });
92
+
93
+ const response = await c15t.handler(request);
94
+
95
+ reply.status(response.status);
96
+ response.headers.forEach((value, key) => reply.header(key, value));
97
+ reply.send(await response.text());
98
+ });
99
+
100
+ app.listen({ port: 3001 });
101
+ ```
102
+
103
+ ## Cloudflare Workers
104
+
105
+ ```ts title="worker.ts"
106
+ import { c15t } from './c15t';
107
+
108
+ export default {
109
+ async fetch(request: Request, env: Env) {
110
+ return c15t.handler(request);
111
+ },
112
+ };
113
+ ```
114
+
115
+ ## Deno
116
+
117
+ ```ts title="server.ts"
118
+ import { c15t } from './c15t.ts';
119
+
120
+ Deno.serve({ port: 3001 }, c15t.handler);
121
+ ```
122
+
123
+ ## CORS
124
+
125
+ The backend automatically handles CORS based on the `trustedOrigins` you configure:
126
+
127
+ ```ts
128
+ c15tInstance({
129
+ trustedOrigins: [
130
+ 'https://example.com',
131
+ 'https://*.example.com', // wildcard subdomains
132
+ ],
133
+ // ...
134
+ });
135
+ ```
136
+
137
+ Features:
138
+
139
+ * `www` variants are automatically allowed
140
+ * Wildcard subdomain matching with `*`
141
+ * `localhost` is allowed in development
142
+ * Preflight `OPTIONS` requests are handled automatically
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: IAB TCF Support
3
+ description: Enable IAB Transparency and Consent Framework (TCF) support in your self-hosted backend.
4
+ ---
5
+ The c15t backend optionally supports [IAB TCF v2.3](https://iabeurope.eu/transparency-consent-framework/), including Global Vendor List (GVL) management and TC String generation.
6
+
7
+ ## CMP Registration
8
+
9
+ [inth.com](https://inth.com) is pending validation as an IAB Europe-registered CMP for c15t. Once approved, when using inth.com as your hosted backend, the CMP ID will be automatically provided to clients — no additional configuration needed.
10
+
11
+ If you self-host and have your own CMP registration with IAB Europe, configure your CMP ID via `iab.cmpId`. This value is returned to clients in the `/init` response so they use the correct CMP identity in TC Strings.
12
+
13
+ > ℹ️ **Info:**
14
+ > A valid (non-zero) CMP ID is required for IAB TCF compliance. If neither the backend nor the client provides a CMP ID, IAB initialization will fail with an error.
15
+ >
16
+ > ℹ️ **Info:**
17
+ > If you heavily customize or build your own IAB banner or dialog (rather than using the default IABConsentBanner and IABConsentDialog components provided by c15t), you cannot use inth.com's CMP ID. You must register your own CMP with IAB Europe and configure your CMP ID via iab.cmpId.
18
+
19
+ ## Enable IAB
20
+
21
+ ```ts title="c15t.ts"
22
+ import { c15tInstance } from '@c15t/backend';
23
+
24
+ export const c15t = c15tInstance({
25
+ // ...
26
+ iab: {
27
+ enabled: true,
28
+ cmpId: 10, // your registered CMP ID (inth.com provides this automatically)
29
+ vendorIds: [755, 52, 69], // only include vendors you use
30
+ },
31
+ });
32
+ ```
33
+
34
+ The backend fetches the GVL from `https://gvl.inth.com` by default and caches it. The `/init` endpoint returns the filtered GVL and your CMP ID to the frontend.
35
+
36
+ ## Custom GVL Endpoint
37
+
38
+ Point to your own GVL mirror:
39
+
40
+ ```ts
41
+ iab: {
42
+ enabled: true,
43
+ endpoint: 'https://your-gvl-mirror.com',
44
+ vendorIds: [755, 52, 69],
45
+ },
46
+ ```
47
+
48
+ ## Bundle GVL by Language
49
+
50
+ Pre-bundle the GVL to avoid runtime fetches:
51
+
52
+ ```ts
53
+ import gvlEn from './gvl/en.json';
54
+ import gvlDe from './gvl/de.json';
55
+
56
+ iab: {
57
+ enabled: true,
58
+ bundled: {
59
+ en: gvlEn,
60
+ de: gvlDe,
61
+ },
62
+ },
63
+ ```
64
+
65
+ > ℹ️ **Info:**
66
+ > Bundling all required GVL translations can significantly increase your deployment size. On serverless environments like Cloudflare Workers — which have strict bundle size limits — this may cause deployments to fail. Consider bundling only the languages you need, or use the GVL endpoint with a cache adapter instead.
67
+
68
+ ## Custom Non-IAB Vendors
69
+
70
+ Add your own vendors alongside IAB-registered ones:
71
+
72
+ ```ts
73
+ iab: {
74
+ enabled: true,
75
+ vendorIds: [755],
76
+ customVendors: [
77
+ {
78
+ id: 'internal-analytics',
79
+ name: 'Our Analytics Platform',
80
+ privacyPolicyUrl: 'https://example.com/privacy',
81
+ purposes: [1, 8],
82
+ },
83
+ ],
84
+ },
85
+ ```
86
+
87
+ ## TC String
88
+
89
+ When IAB TCF is enabled, consent records include a `tcString` field — the encoded TC String that can be passed to vendors. The frontend SDKs handle this automatically when using `mode: 'hosted'`.
@@ -0,0 +1,96 @@
1
+ ---
2
+ title: Observability
3
+ description: Add logging, metrics, and tracing to your self-hosted c15t backend.
4
+ ---
5
+ The c15t backend supports structured logging and opt-in OpenTelemetry integration for production observability.
6
+
7
+ ## OpenTelemetry
8
+
9
+ Telemetry is disabled by default. To enable it, pass your own tracer and meter instances:
10
+
11
+ ```ts title="c15t.ts"
12
+ import { c15tInstance } from '@c15t/backend';
13
+ import { trace, metrics } from '@opentelemetry/api';
14
+
15
+ export const c15t = c15tInstance({
16
+ // ...
17
+ telemetry: {
18
+ enabled: true,
19
+ tracer: trace.getTracer('consent-api'),
20
+ meter: metrics.getMeter('consent-api'),
21
+ defaultAttributes: {
22
+ 'service.name': 'consent-api',
23
+ environment: 'production',
24
+ },
25
+ },
26
+ });
27
+ ```
28
+
29
+ > ℹ️ **Info:**
30
+ > You need to set up your own OpenTelemetry SDK and exporter (e.g. Jaeger, Datadog, Grafana). The c15t backend creates spans and metrics using the instances you provide.
31
+
32
+ ## Metrics
33
+
34
+ When telemetry is enabled, the following metrics are recorded:
35
+
36
+ ### Business Metrics
37
+
38
+ |Metric|Type|Description|
39
+ |--|--|--|
40
+ |`consentCreated`|Counter|Consent record created (by type, jurisdiction)|
41
+ |`consentAccepted`|Counter|Consent accepted (by type)|
42
+ |`consentRejected`|Counter|Consent rejected (by type)|
43
+ |`subjectCreated`|Counter|New subject created|
44
+ |`subjectLinked`|Counter|Subject linked to external ID|
45
+ |`initCount`|Counter|`/init` endpoint called|
46
+
47
+ ### HTTP Metrics
48
+
49
+ |Metric|Type|Description|
50
+ |--|--|--|
51
+ |`httpRequestDuration`|Histogram|Request duration (by method, route, status)|
52
+ |`httpRequestCount`|Counter|Total requests|
53
+ |`httpErrorCount`|Counter|Error responses|
54
+
55
+ ### Database Metrics
56
+
57
+ |Metric|Type|Description|
58
+ |--|--|--|
59
+ |`dbQueryDuration`|Histogram|Query duration (by operation, entity)|
60
+ |`dbQueryCount`|Counter|Total queries|
61
+ |`dbErrorCount`|Counter|Query errors|
62
+
63
+ ### Cache Metrics
64
+
65
+ |Metric|Type|Description|
66
+ |--|--|--|
67
+ |`cacheHit`|Counter|Cache hits (by layer)|
68
+ |`cacheMiss`|Counter|Cache misses (by layer)|
69
+ |`cacheLatency`|Histogram|Cache operation latency|
70
+
71
+ ## Tracing
72
+
73
+ Every request creates a span with:
74
+
75
+ * HTTP method and route
76
+ * Response status code
77
+ * Tenant ID (if multi-tenant)
78
+ * API key authentication status
79
+ * Geo-location resolution
80
+
81
+ Child spans are created for database queries and cache operations.
82
+
83
+ ## Logging
84
+
85
+ Configure the log level:
86
+
87
+ ```ts
88
+ c15tInstance({
89
+ // ...
90
+ logger: {
91
+ level: 'info', // debug, info, warn, error
92
+ },
93
+ });
94
+ ```
95
+
96
+ Logs are structured (JSON-compatible) and include trace context when telemetry is enabled, making it easy to correlate logs with traces.