vibecarbon 0.1.0

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 (495) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +188 -0
  3. package/carbon/.claude/hooks/task-completed-gate.sh +19 -0
  4. package/carbon/.claude/hooks/teammate-idle-gate.sh +29 -0
  5. package/carbon/.claude/settings.json +29 -0
  6. package/carbon/.cursor/rules/vibecarbon.mdc +6 -0
  7. package/carbon/.dockerignore +57 -0
  8. package/carbon/.env.example +219 -0
  9. package/carbon/.github/copilot-instructions.md +3 -0
  10. package/carbon/.github/workflows/deploy.yml +346 -0
  11. package/carbon/.github/workflows/vibecarbon-build.yml +81 -0
  12. package/carbon/.windsurfrules +74 -0
  13. package/carbon/AGENTS.md +422 -0
  14. package/carbon/CLAUDE.md +3 -0
  15. package/carbon/DEVELOPMENT.md +187 -0
  16. package/carbon/Dockerfile +98 -0
  17. package/carbon/LICENSE +21 -0
  18. package/carbon/PRODUCTION.md +364 -0
  19. package/carbon/README.md +193 -0
  20. package/carbon/TESTING.md +138 -0
  21. package/carbon/backup/Dockerfile +75 -0
  22. package/carbon/backup/backup.sh +95 -0
  23. package/carbon/backup/compose-backup.sh +140 -0
  24. package/carbon/biome.json +83 -0
  25. package/carbon/cloud-init/docker-ce-setup.yaml +55 -0
  26. package/carbon/cloud-init/k3s/master-init.sh +215 -0
  27. package/carbon/cloud-init/k3s/supabase-init.sh +167 -0
  28. package/carbon/cloud-init/k3s/worker-init.sh +147 -0
  29. package/carbon/components.json +24 -0
  30. package/carbon/content/blog/authentication-guide.mdx +34 -0
  31. package/carbon/content/blog/getting-started.mdx +41 -0
  32. package/carbon/content/changelog/v0-1-0.mdx +40 -0
  33. package/carbon/content/docs/analytics.mdx +90 -0
  34. package/carbon/content/docs/authentication.mdx +231 -0
  35. package/carbon/content/docs/background-jobs.mdx +116 -0
  36. package/carbon/content/docs/cli.mdx +630 -0
  37. package/carbon/content/docs/database.mdx +236 -0
  38. package/carbon/content/docs/deployment.mdx +227 -0
  39. package/carbon/content/docs/development.mdx +238 -0
  40. package/carbon/content/docs/environments.mdx +84 -0
  41. package/carbon/content/docs/getting-started.mdx +112 -0
  42. package/carbon/content/docs/legal/privacy-policy.mdx +89 -0
  43. package/carbon/content/docs/legal/terms-of-service.mdx +99 -0
  44. package/carbon/content/docs/optional-services.mdx +160 -0
  45. package/carbon/db/Dockerfile +23 -0
  46. package/carbon/docker-compose.dev-init.yml +5 -0
  47. package/carbon/docker-compose.metabase.override.yml +14 -0
  48. package/carbon/docker-compose.metabase.prod.yml +28 -0
  49. package/carbon/docker-compose.metabase.yml +84 -0
  50. package/carbon/docker-compose.n8n.override.yml +14 -0
  51. package/carbon/docker-compose.n8n.prod.yml +31 -0
  52. package/carbon/docker-compose.n8n.yml +97 -0
  53. package/carbon/docker-compose.observability.override.yml +18 -0
  54. package/carbon/docker-compose.observability.prod.yml +28 -0
  55. package/carbon/docker-compose.observability.yml +125 -0
  56. package/carbon/docker-compose.override.yml +28 -0
  57. package/carbon/docker-compose.prod.yml +294 -0
  58. package/carbon/docker-compose.yml +508 -0
  59. package/carbon/docker-entrypoint.sh +12 -0
  60. package/carbon/ha/activate-standby.sh +52 -0
  61. package/carbon/ha/primary-init.sql +36 -0
  62. package/carbon/ha/standby-init.sh +74 -0
  63. package/carbon/k8s/LICENSE +99 -0
  64. package/carbon/k8s/README.md +272 -0
  65. package/carbon/k8s/base/app/deployment.yaml +130 -0
  66. package/carbon/k8s/base/app/hpa.yaml +44 -0
  67. package/carbon/k8s/base/app/kustomization.yaml +10 -0
  68. package/carbon/k8s/base/app/network-policy.yaml +124 -0
  69. package/carbon/k8s/base/app/pdb.yaml +14 -0
  70. package/carbon/k8s/base/app/rbac.yaml +36 -0
  71. package/carbon/k8s/base/app/service.yaml +16 -0
  72. package/carbon/k8s/base/backup/cronjob.yaml +120 -0
  73. package/carbon/k8s/base/backup/kustomization.yaml +7 -0
  74. package/carbon/k8s/base/backup/network-policy.yaml +31 -0
  75. package/carbon/k8s/base/backup/rbac.yaml +7 -0
  76. package/carbon/k8s/base/cluster-autoscaler/deployment.yaml +103 -0
  77. package/carbon/k8s/base/cluster-autoscaler/kustomization.yaml +17 -0
  78. package/carbon/k8s/base/cluster-autoscaler/rbac.yaml +109 -0
  79. package/carbon/k8s/base/config/configmap.yaml +48 -0
  80. package/carbon/k8s/base/config/kustomization.yaml +8 -0
  81. package/carbon/k8s/base/hetzner-ccm/kustomization.yaml +9 -0
  82. package/carbon/k8s/base/hetzner-csi/kustomization.yaml +9 -0
  83. package/carbon/k8s/base/kustomization.yaml +43 -0
  84. package/carbon/k8s/base/namespace.yaml +7 -0
  85. package/carbon/k8s/base/network-policies.yaml +95 -0
  86. package/carbon/k8s/base/registry/kustomization.yaml +5 -0
  87. package/carbon/k8s/base/registry/local-registry.yaml +193 -0
  88. package/carbon/k8s/base/traefik/certificate.yaml +21 -0
  89. package/carbon/k8s/base/traefik/configmap.yaml +13 -0
  90. package/carbon/k8s/base/traefik/deployment.yaml +165 -0
  91. package/carbon/k8s/base/traefik/ingressroute.yaml +141 -0
  92. package/carbon/k8s/base/traefik/kustomization.yaml +11 -0
  93. package/carbon/k8s/base/traefik/middleware.yaml +109 -0
  94. package/carbon/k8s/base/traefik/network-policy.yaml +92 -0
  95. package/carbon/k8s/base/traefik/service.yaml +38 -0
  96. package/carbon/k8s/flux/README.md +49 -0
  97. package/carbon/k8s/flux/clusters/primary/vibecarbon.yaml +128 -0
  98. package/carbon/k8s/flux/clusters/standby/vibecarbon.yaml +83 -0
  99. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-release.yaml +38 -0
  100. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-repository.yaml +16 -0
  101. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/kustomization.yaml +10 -0
  102. package/carbon/k8s/gitops/supabase/helm-release.yaml +66 -0
  103. package/carbon/k8s/gitops/supabase/helm-repository.yaml +18 -0
  104. package/carbon/k8s/gitops/supabase/kustomization.yaml +33 -0
  105. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-cloudflare.yaml +51 -0
  106. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-hetzner.yaml +59 -0
  107. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-manual.yaml +43 -0
  108. package/carbon/k8s/infra/cert-manager-resources/kustomization.yaml +15 -0
  109. package/carbon/k8s/infra/kustomization.yaml +21 -0
  110. package/carbon/k8s/infra/traefik-crds/kustomization.yaml +10 -0
  111. package/carbon/k8s/overlays/local/configmap.yaml +23 -0
  112. package/carbon/k8s/overlays/local/ingressroute-studio.yaml +104 -0
  113. package/carbon/k8s/overlays/local/kustomization.yaml +215 -0
  114. package/carbon/k8s/overlays/local/namespace.yaml +8 -0
  115. package/carbon/k8s/overlays/local/secrets.yaml +32 -0
  116. package/carbon/k8s/overlays/production/kustomization.yaml +34 -0
  117. package/carbon/k8s/test-local.sh +318 -0
  118. package/carbon/k8s/values/supabase.values.yaml +133 -0
  119. package/carbon/package.json +154 -0
  120. package/carbon/runtime.Dockerfile +17 -0
  121. package/carbon/scripts/_dev-jwt.mjs +45 -0
  122. package/carbon/scripts/check-rls.ts +53 -0
  123. package/carbon/scripts/dev-init.js +191 -0
  124. package/carbon/scripts/dev.js +191 -0
  125. package/carbon/scripts/docker-down.js +63 -0
  126. package/carbon/scripts/docker-logs.js +59 -0
  127. package/carbon/scripts/docker-up.js +222 -0
  128. package/carbon/scripts/generate-dev-configs.sh +131 -0
  129. package/carbon/scripts/generate-rss.ts +116 -0
  130. package/carbon/scripts/generate-sitemap.ts +102 -0
  131. package/carbon/scripts/k8s-apply.js +71 -0
  132. package/carbon/scripts/k8s-delete.js +75 -0
  133. package/carbon/scripts/lib/manifest.js +176 -0
  134. package/carbon/scripts/secret-scan.mjs +278 -0
  135. package/carbon/scripts/validate-dev-configs.sh +101 -0
  136. package/carbon/src/client/App.tsx +202 -0
  137. package/carbon/src/client/assets/hyperformant-dark.svg +29 -0
  138. package/carbon/src/client/assets/hyperformant-light.svg +29 -0
  139. package/carbon/src/client/assets/logos/aider.svg +1 -0
  140. package/carbon/src/client/assets/logos/antigravity.svg +1 -0
  141. package/carbon/src/client/assets/logos/bolt.svg +1 -0
  142. package/carbon/src/client/assets/logos/claude-code.svg +1 -0
  143. package/carbon/src/client/assets/logos/copilot.svg +1 -0
  144. package/carbon/src/client/assets/logos/cursor.svg +1 -0
  145. package/carbon/src/client/assets/logos/gemini-cli.svg +1 -0
  146. package/carbon/src/client/assets/logos/lovable.svg +1 -0
  147. package/carbon/src/client/assets/logos/openai-codex.svg +1 -0
  148. package/carbon/src/client/assets/logos/v0.svg +1 -0
  149. package/carbon/src/client/assets/logos/windsurf.svg +1 -0
  150. package/carbon/src/client/assets/vibecarbon-icon.svg +19 -0
  151. package/carbon/src/client/assets/vibecarbon-logo-dark.svg +29 -0
  152. package/carbon/src/client/assets/vibecarbon-logo-light.svg +29 -0
  153. package/carbon/src/client/components/AIIntegrationSection.tsx +120 -0
  154. package/carbon/src/client/components/AppSidebar.tsx +760 -0
  155. package/carbon/src/client/components/ArchitectureSection.tsx +46 -0
  156. package/carbon/src/client/components/CTAFooter.tsx +59 -0
  157. package/carbon/src/client/components/ComparisonSection.tsx +132 -0
  158. package/carbon/src/client/components/ContentPanel.tsx +66 -0
  159. package/carbon/src/client/components/ContentSkeleton.tsx +21 -0
  160. package/carbon/src/client/components/ErrorBoundary.tsx +46 -0
  161. package/carbon/src/client/components/FAQSection.tsx +76 -0
  162. package/carbon/src/client/components/FileUpload.tsx +210 -0
  163. package/carbon/src/client/components/HeaderActions.tsx +17 -0
  164. package/carbon/src/client/components/Hero.tsx +608 -0
  165. package/carbon/src/client/components/ImpersonationBanner.tsx +31 -0
  166. package/carbon/src/client/components/LanguageSwitcher.tsx +67 -0
  167. package/carbon/src/client/components/Logo.tsx +87 -0
  168. package/carbon/src/client/components/LogoStrip.tsx +76 -0
  169. package/carbon/src/client/components/MetricsStrip.tsx +48 -0
  170. package/carbon/src/client/components/Nav.tsx +195 -0
  171. package/carbon/src/client/components/NewsletterSignup.tsx +147 -0
  172. package/carbon/src/client/components/NotificationDrawer.tsx +171 -0
  173. package/carbon/src/client/components/PageHeader.tsx +24 -0
  174. package/carbon/src/client/components/PlanGate.tsx +36 -0
  175. package/carbon/src/client/components/PricingSection.tsx +371 -0
  176. package/carbon/src/client/components/SEO.tsx +34 -0
  177. package/carbon/src/client/components/SmoothScroll.tsx +45 -0
  178. package/carbon/src/client/components/TechStackSection.tsx +165 -0
  179. package/carbon/src/client/components/WorkflowSection.tsx +604 -0
  180. package/carbon/src/client/components/admin/DockerLogs.tsx +276 -0
  181. package/carbon/src/client/components/admin/PerformanceMetrics.tsx +230 -0
  182. package/carbon/src/client/components/admin/ServiceCard.tsx +45 -0
  183. package/carbon/src/client/components/admin/ServicesStatus.tsx +296 -0
  184. package/carbon/src/client/components/architecture/ArchitectureDiagram.tsx +413 -0
  185. package/carbon/src/client/components/auth/AuthProvider.tsx +466 -0
  186. package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +35 -0
  187. package/carbon/src/client/components/effects/FresnelEdge.tsx +49 -0
  188. package/carbon/src/client/components/effects/GlowTracker.tsx +46 -0
  189. package/carbon/src/client/components/effects/SparkBurst.tsx +145 -0
  190. package/carbon/src/client/components/effects/VGlowEffect.tsx +83 -0
  191. package/carbon/src/client/components/effects/index.ts +2 -0
  192. package/carbon/src/client/components/layouts/SidebarLayout.tsx +20 -0
  193. package/carbon/src/client/components/scroll/ScrollSection.tsx +76 -0
  194. package/carbon/src/client/components/scroll/ScrollytellingProvider.tsx +81 -0
  195. package/carbon/src/client/components/scroll/index.ts +2 -0
  196. package/carbon/src/client/components/ui/accordion.tsx +71 -0
  197. package/carbon/src/client/components/ui/alert-dialog.tsx +162 -0
  198. package/carbon/src/client/components/ui/alert.tsx +73 -0
  199. package/carbon/src/client/components/ui/aspect-ratio.tsx +22 -0
  200. package/carbon/src/client/components/ui/avatar.tsx +91 -0
  201. package/carbon/src/client/components/ui/badge.tsx +50 -0
  202. package/carbon/src/client/components/ui/breadcrumb.tsx +100 -0
  203. package/carbon/src/client/components/ui/button-group.tsx +78 -0
  204. package/carbon/src/client/components/ui/button.tsx +120 -0
  205. package/carbon/src/client/components/ui/calendar.tsx +182 -0
  206. package/carbon/src/client/components/ui/card.tsx +85 -0
  207. package/carbon/src/client/components/ui/carousel.tsx +227 -0
  208. package/carbon/src/client/components/ui/chart.tsx +357 -0
  209. package/carbon/src/client/components/ui/checkbox.tsx +27 -0
  210. package/carbon/src/client/components/ui/collapsible.tsx +15 -0
  211. package/carbon/src/client/components/ui/command.tsx +178 -0
  212. package/carbon/src/client/components/ui/context-menu.tsx +233 -0
  213. package/carbon/src/client/components/ui/dialog.tsx +132 -0
  214. package/carbon/src/client/components/ui/drawer.tsx +118 -0
  215. package/carbon/src/client/components/ui/dropdown-menu.tsx +242 -0
  216. package/carbon/src/client/components/ui/empty.tsx +94 -0
  217. package/carbon/src/client/components/ui/field.tsx +226 -0
  218. package/carbon/src/client/components/ui/hover-card.tsx +44 -0
  219. package/carbon/src/client/components/ui/input-group.tsx +146 -0
  220. package/carbon/src/client/components/ui/input-otp.tsx +83 -0
  221. package/carbon/src/client/components/ui/input.tsx +20 -0
  222. package/carbon/src/client/components/ui/item.tsx +188 -0
  223. package/carbon/src/client/components/ui/kbd.tsx +26 -0
  224. package/carbon/src/client/components/ui/label.tsx +21 -0
  225. package/carbon/src/client/components/ui/menubar.tsx +250 -0
  226. package/carbon/src/client/components/ui/navigation-menu.tsx +155 -0
  227. package/carbon/src/client/components/ui/pagination.tsx +102 -0
  228. package/carbon/src/client/components/ui/popover.tsx +75 -0
  229. package/carbon/src/client/components/ui/progress.tsx +66 -0
  230. package/carbon/src/client/components/ui/radio-group.tsx +36 -0
  231. package/carbon/src/client/components/ui/resizable.tsx +46 -0
  232. package/carbon/src/client/components/ui/scroll-area.tsx +48 -0
  233. package/carbon/src/client/components/ui/select.tsx +186 -0
  234. package/carbon/src/client/components/ui/separator.tsx +21 -0
  235. package/carbon/src/client/components/ui/sheet.tsx +124 -0
  236. package/carbon/src/client/components/ui/sidebar.tsx +702 -0
  237. package/carbon/src/client/components/ui/skeleton.tsx +13 -0
  238. package/carbon/src/client/components/ui/slider.tsx +57 -0
  239. package/carbon/src/client/components/ui/sonner.tsx +45 -0
  240. package/carbon/src/client/components/ui/spinner.tsx +15 -0
  241. package/carbon/src/client/components/ui/switch.tsx +30 -0
  242. package/carbon/src/client/components/ui/table.tsx +89 -0
  243. package/carbon/src/client/components/ui/tabs.tsx +73 -0
  244. package/carbon/src/client/components/ui/textarea.tsx +18 -0
  245. package/carbon/src/client/components/ui/toggle-group.tsx +87 -0
  246. package/carbon/src/client/components/ui/toggle.tsx +44 -0
  247. package/carbon/src/client/components/ui/tooltip.tsx +56 -0
  248. package/carbon/src/client/hooks/api/index.ts +14 -0
  249. package/carbon/src/client/hooks/api/useAuthSettings.ts +80 -0
  250. package/carbon/src/client/hooks/api/useSubscription.ts +87 -0
  251. package/carbon/src/client/hooks/use-mobile.ts +21 -0
  252. package/carbon/src/client/hooks/useMousePosition.ts +91 -0
  253. package/carbon/src/client/hooks/useNotifications.tsx +124 -0
  254. package/carbon/src/client/hooks/useOrganizationMembers.ts +127 -0
  255. package/carbon/src/client/hooks/useOrganizations.tsx +230 -0
  256. package/carbon/src/client/hooks/usePackageManager.ts +69 -0
  257. package/carbon/src/client/hooks/useReducedMotion.ts +20 -0
  258. package/carbon/src/client/hooks/useRunningServices.ts +114 -0
  259. package/carbon/src/client/hooks/useScrollProgress.ts +56 -0
  260. package/carbon/src/client/index.css +467 -0
  261. package/carbon/src/client/index.html +56 -0
  262. package/carbon/src/client/lib/admin-services.ts +151 -0
  263. package/carbon/src/client/lib/api.ts +32 -0
  264. package/carbon/src/client/lib/blog.ts +35 -0
  265. package/carbon/src/client/lib/changelog.ts +33 -0
  266. package/carbon/src/client/lib/docs-search.ts +101 -0
  267. package/carbon/src/client/lib/docs.ts +37 -0
  268. package/carbon/src/client/lib/i18n.ts +32 -0
  269. package/carbon/src/client/lib/supabase.ts +72 -0
  270. package/carbon/src/client/lib/tailwind-colors.ts +357 -0
  271. package/carbon/src/client/lib/theme.ts +117 -0
  272. package/carbon/src/client/lib/utils.ts +22 -0
  273. package/carbon/src/client/locales/de.json +529 -0
  274. package/carbon/src/client/locales/en.json +461 -0
  275. package/carbon/src/client/locales/es.json +529 -0
  276. package/carbon/src/client/locales/fr.json +529 -0
  277. package/carbon/src/client/locales/pt.json +529 -0
  278. package/carbon/src/client/main.tsx +56 -0
  279. package/carbon/src/client/mdx.d.ts +13 -0
  280. package/carbon/src/client/pages/ApiDocs.tsx +76 -0
  281. package/carbon/src/client/pages/AuthCallback.tsx +34 -0
  282. package/carbon/src/client/pages/Blog.tsx +167 -0
  283. package/carbon/src/client/pages/Changelog.tsx +171 -0
  284. package/carbon/src/client/pages/Charts.tsx +388 -0
  285. package/carbon/src/client/pages/Checkout.tsx +227 -0
  286. package/carbon/src/client/pages/Contact.tsx +174 -0
  287. package/carbon/src/client/pages/Dashboard.tsx +368 -0
  288. package/carbon/src/client/pages/Docs.tsx +372 -0
  289. package/carbon/src/client/pages/ForgotPassword.tsx +111 -0
  290. package/carbon/src/client/pages/Home.tsx +187 -0
  291. package/carbon/src/client/pages/Legal.tsx +100 -0
  292. package/carbon/src/client/pages/Login.tsx +408 -0
  293. package/carbon/src/client/pages/MFAVerify.tsx +156 -0
  294. package/carbon/src/client/pages/NotFound.tsx +21 -0
  295. package/carbon/src/client/pages/Onboarding.tsx +246 -0
  296. package/carbon/src/client/pages/ResetPassword.tsx +200 -0
  297. package/carbon/src/client/pages/UIComponents.tsx +390 -0
  298. package/carbon/src/client/pages/admin/ContactSubmissions.tsx +220 -0
  299. package/carbon/src/client/pages/admin/Dashboard.tsx +24 -0
  300. package/carbon/src/client/pages/admin/Infrastructure.tsx +257 -0
  301. package/carbon/src/client/pages/admin/Jobs.tsx +225 -0
  302. package/carbon/src/client/pages/admin/Logs.tsx +18 -0
  303. package/carbon/src/client/pages/admin/Newsletter.tsx +300 -0
  304. package/carbon/src/client/pages/admin/Notifications.tsx +603 -0
  305. package/carbon/src/client/pages/admin/Organizations.tsx +306 -0
  306. package/carbon/src/client/pages/admin/Settings.tsx +314 -0
  307. package/carbon/src/client/pages/admin/Theme.tsx +465 -0
  308. package/carbon/src/client/pages/admin/Users.tsx +365 -0
  309. package/carbon/src/client/pages/api-docs.css +156 -0
  310. package/carbon/src/client/pages/organizations/Details.tsx +200 -0
  311. package/carbon/src/client/pages/organizations/Members.tsx +402 -0
  312. package/carbon/src/client/pages/settings/Billing.tsx +473 -0
  313. package/carbon/src/client/pages/settings/Profile.tsx +160 -0
  314. package/carbon/src/client/pages/settings/Security.tsx +341 -0
  315. package/carbon/src/client/public/favicon.svg +19 -0
  316. package/carbon/src/client/public/robots.txt +8 -0
  317. package/carbon/src/server/billing/index.ts +104 -0
  318. package/carbon/src/server/billing/provider.ts +81 -0
  319. package/carbon/src/server/billing/providers/paddle.ts +314 -0
  320. package/carbon/src/server/billing/providers/polar.ts +325 -0
  321. package/carbon/src/server/billing/providers/stripe.ts +233 -0
  322. package/carbon/src/server/emails/templates.ts +116 -0
  323. package/carbon/src/server/index.ts +554 -0
  324. package/carbon/src/server/lib/auth.ts +6 -0
  325. package/carbon/src/server/lib/email.ts +64 -0
  326. package/carbon/src/server/lib/env.ts +112 -0
  327. package/carbon/src/server/lib/errors.ts +21 -0
  328. package/carbon/src/server/lib/logger.ts +17 -0
  329. package/carbon/src/server/lib/rate-limiter.ts +288 -0
  330. package/carbon/src/server/lib/request.ts +34 -0
  331. package/carbon/src/server/lib/stripe.ts +42 -0
  332. package/carbon/src/server/lib/supabase.ts +65 -0
  333. package/carbon/src/server/middleware/requirePlan.ts +80 -0
  334. package/carbon/src/server/routes/_internal/services-status.ts +958 -0
  335. package/carbon/src/server/routes/_internal/verify-role.ts +185 -0
  336. package/carbon/src/server/routes/health.ts +48 -0
  337. package/carbon/src/server/routes/v1/admin/contact.ts +128 -0
  338. package/carbon/src/server/routes/v1/admin/jobs.ts +171 -0
  339. package/carbon/src/server/routes/v1/admin/newsletter.ts +237 -0
  340. package/carbon/src/server/routes/v1/auth.ts +390 -0
  341. package/carbon/src/server/routes/v1/billing.ts +718 -0
  342. package/carbon/src/server/routes/v1/contact.ts +93 -0
  343. package/carbon/src/server/routes/v1/index.ts +1333 -0
  344. package/carbon/src/server/routes/v1/newsletter.ts +181 -0
  345. package/carbon/src/server/routes/v1/performance.ts +157 -0
  346. package/carbon/src/server/routes/v1/stats.ts +170 -0
  347. package/carbon/src/server/routes/v1/theme.ts +106 -0
  348. package/carbon/src/server/routes/webhooks/billing.ts +376 -0
  349. package/carbon/src/server/routes/webhooks/stripe.ts +276 -0
  350. package/carbon/src/server/types.ts +11 -0
  351. package/carbon/src/shared/pricing.ts +155 -0
  352. package/carbon/src/shared/types.ts +338 -0
  353. package/carbon/supabase/migrations/00001_init.sql +717 -0
  354. package/carbon/supabase/migrations/00002_theme_settings.sql +13 -0
  355. package/carbon/supabase/migrations/00003_pg_cron.sql +121 -0
  356. package/carbon/supabase/migrations/00004_contact_newsletter.sql +81 -0
  357. package/carbon/supabase/migrations/00005_localization_languages.sql +22 -0
  358. package/carbon/supabase/seed.sql +16 -0
  359. package/carbon/tests/_helpers/app.ts +45 -0
  360. package/carbon/tests/_helpers/env.ts +37 -0
  361. package/carbon/tests/_helpers/factories.ts +69 -0
  362. package/carbon/tests/_helpers/jwt.ts +23 -0
  363. package/carbon/tests/_helpers/setup-integration.ts +20 -0
  364. package/carbon/tests/_helpers/setup-rtl.ts +12 -0
  365. package/carbon/tests/component/ErrorBoundary.test.tsx +53 -0
  366. package/carbon/tests/component/use-auth-settings.test.tsx +119 -0
  367. package/carbon/tests/integration/server/routes/contact.test.ts +162 -0
  368. package/carbon/tests/integration/server/routes/health.test.ts +61 -0
  369. package/carbon/tests/structural/i18n-parity.test.ts +42 -0
  370. package/carbon/tests/unit/client/utils.test.ts +49 -0
  371. package/carbon/tests/unit/shared/pricing.test.ts +93 -0
  372. package/carbon/tsconfig.json +27 -0
  373. package/carbon/tsconfig.server.json +27 -0
  374. package/carbon/tsconfig.test.json +9 -0
  375. package/carbon/vite.config.ts +110 -0
  376. package/carbon/vitest.config.ts +74 -0
  377. package/carbon/volumes/db/jwt.sql +57 -0
  378. package/carbon/volumes/db/metabase-init.sh +29 -0
  379. package/carbon/volumes/db/n8n-init.sh +25 -0
  380. package/carbon/volumes/db/realtime.sql +33 -0
  381. package/carbon/volumes/db/roles.sql +93 -0
  382. package/carbon/volumes/db/set-passwords.sh +12 -0
  383. package/carbon/volumes/db/super-admin.dev.sql +113 -0
  384. package/carbon/volumes/db/super-admin.generated.sql +113 -0
  385. package/carbon/volumes/db/super-admin.sql +114 -0
  386. package/carbon/volumes/grafana/dashboards/logs.json +179 -0
  387. package/carbon/volumes/grafana/dashboards/overview.json +523 -0
  388. package/carbon/volumes/grafana/dashboards/postgresql.json +337 -0
  389. package/carbon/volumes/grafana/dashboards.dev/logs.json +179 -0
  390. package/carbon/volumes/grafana/dashboards.dev/overview.json +156 -0
  391. package/carbon/volumes/grafana/dashboards.dev/postgresql.json +337 -0
  392. package/carbon/volumes/grafana/provisioning/dashboards/dashboards.dev.yml +16 -0
  393. package/carbon/volumes/grafana/provisioning/dashboards/dashboards.yml +16 -0
  394. package/carbon/volumes/grafana/provisioning/datasources/datasources.yml +43 -0
  395. package/carbon/volumes/kong/docker-entrypoint.sh +9 -0
  396. package/carbon/volumes/kong/kong.yml +208 -0
  397. package/carbon/volumes/loki/loki-config.yml +58 -0
  398. package/carbon/volumes/n8n/hooks.js +131 -0
  399. package/carbon/volumes/n8n/scripts/setup.sh +66 -0
  400. package/carbon/volumes/prometheus/prometheus.yml +43 -0
  401. package/carbon/volumes/promtail/promtail-config.yml +64 -0
  402. package/carbon/volumes/traefik/middlewares.dev.yml +83 -0
  403. package/carbon/volumes/traefik/middlewares.yml +95 -0
  404. package/carbon/volumes/traefik/vite-dev.yml +20 -0
  405. package/package.json +95 -0
  406. package/src/access.js +354 -0
  407. package/src/activate.js +187 -0
  408. package/src/add.js +718 -0
  409. package/src/backup.js +786 -0
  410. package/src/cli.js +350 -0
  411. package/src/configure.js +967 -0
  412. package/src/console.js +155 -0
  413. package/src/create.js +1499 -0
  414. package/src/deploy.js +311 -0
  415. package/src/destroy.js +2033 -0
  416. package/src/diagnose.js +735 -0
  417. package/src/down.js +80 -0
  418. package/src/failover.js +1032 -0
  419. package/src/lib/backup-s3.js +179 -0
  420. package/src/lib/build.js +33 -0
  421. package/src/lib/checksum.js +28 -0
  422. package/src/lib/ci-setup.js +666 -0
  423. package/src/lib/cli/help.js +129 -0
  424. package/src/lib/cli/parse-flags.js +160 -0
  425. package/src/lib/cli/select-action.js +65 -0
  426. package/src/lib/cli/select-environment.js +108 -0
  427. package/src/lib/cli/tty-guard.js +75 -0
  428. package/src/lib/cloudflare.js +447 -0
  429. package/src/lib/colors.js +52 -0
  430. package/src/lib/command.js +361 -0
  431. package/src/lib/config.js +359 -0
  432. package/src/lib/cost.js +103 -0
  433. package/src/lib/deploy/bundle.js +231 -0
  434. package/src/lib/deploy/compose/acme-verify.js +121 -0
  435. package/src/lib/deploy/compose/build-args.js +78 -0
  436. package/src/lib/deploy/compose/ha.js +1874 -0
  437. package/src/lib/deploy/compose/index.js +1294 -0
  438. package/src/lib/deploy/compose/reconcile.js +74 -0
  439. package/src/lib/deploy/github.js +382 -0
  440. package/src/lib/deploy/image.js +191 -0
  441. package/src/lib/deploy/index.js +119 -0
  442. package/src/lib/deploy/k8s/LICENSE +99 -0
  443. package/src/lib/deploy/k8s/gitops-deploy.js +298 -0
  444. package/src/lib/deploy/k8s/ha/index.js +1000 -0
  445. package/src/lib/deploy/k8s/index.js +62 -0
  446. package/src/lib/deploy/k8s/k3s.js +2515 -0
  447. package/src/lib/deploy/orchestrator.js +1401 -0
  448. package/src/lib/deploy/prompts.js +545 -0
  449. package/src/lib/deploy/remote-build.js +130 -0
  450. package/src/lib/deploy/state.js +120 -0
  451. package/src/lib/deploy/utils.js +328 -0
  452. package/src/lib/deploy-logger.js +93 -0
  453. package/src/lib/dns-propagation.js +48 -0
  454. package/src/lib/environment.js +58 -0
  455. package/src/lib/fetch-retry.js +103 -0
  456. package/src/lib/github-environments.js +335 -0
  457. package/src/lib/hetzner-dns.js +377 -0
  458. package/src/lib/hetzner-guided-setup.js +275 -0
  459. package/src/lib/host-keys.js +37 -0
  460. package/src/lib/iac/cloud-init.js +52 -0
  461. package/src/lib/iac/index.js +325 -0
  462. package/src/lib/iac/programs/hetzner-compose.js +130 -0
  463. package/src/lib/iac/programs/hetzner-k8s.js +320 -0
  464. package/src/lib/kubectl.js +81 -0
  465. package/src/lib/licensing/index.js +259 -0
  466. package/src/lib/licensing/tiers.js +181 -0
  467. package/src/lib/licensing/validator.js +171 -0
  468. package/src/lib/merge-package-json.js +90 -0
  469. package/src/lib/operator-ip.js +381 -0
  470. package/src/lib/package-manager.js +111 -0
  471. package/src/lib/perf.js +71 -0
  472. package/src/lib/project-guard.js +39 -0
  473. package/src/lib/project.js +334 -0
  474. package/src/lib/providers/base.js +276 -0
  475. package/src/lib/providers/hetzner-s3.js +656 -0
  476. package/src/lib/providers/hetzner.js +755 -0
  477. package/src/lib/providers/index.js +164 -0
  478. package/src/lib/s3.js +510 -0
  479. package/src/lib/secret-scan.js +583 -0
  480. package/src/lib/secrets.js +63 -0
  481. package/src/lib/server-types.js +195 -0
  482. package/src/lib/shell.js +91 -0
  483. package/src/lib/ssh.js +241 -0
  484. package/src/lib/tracker.js +242 -0
  485. package/src/lib/upgrade-policy.js +170 -0
  486. package/src/lib/validators.js +105 -0
  487. package/src/lib/version.js +5 -0
  488. package/src/remove.js +292 -0
  489. package/src/reset.js +97 -0
  490. package/src/restore.js +871 -0
  491. package/src/scale.js +1734 -0
  492. package/src/shell.js +222 -0
  493. package/src/status.js +981 -0
  494. package/src/up.js +264 -0
  495. package/src/upgrade.js +721 -0
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: "Analytics"
3
+ description: "Privacy-friendly product analytics with Plausible."
4
+ order: 9
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ Your app includes built-in support for **Plausible Analytics** — a lightweight, privacy-friendly alternative to Google Analytics. Plausible is:
10
+
11
+ - **Cookie-free** — no consent banners needed
12
+ - **GDPR/CCPA compliant** out of the box
13
+ - **Lightweight** — under 1KB script, no impact on page speed
14
+ - **Open source** — self-host or use their cloud service
15
+
16
+ ## Setup
17
+
18
+ ### Option 1: Plausible Cloud
19
+
20
+ 1. Sign up at [plausible.io](https://plausible.io)
21
+ 2. Add your domain in the Plausible dashboard
22
+ 3. Set the environment variables:
23
+
24
+ ```bash
25
+ VITE_PLAUSIBLE_DOMAIN="yourdomain.com"
26
+ VITE_PLAUSIBLE_SCRIPT_URL="https://plausible.io/js/script.js"
27
+ ```
28
+
29
+ ### Option 2: Self-Hosted Plausible
30
+
31
+ 1. Follow the [Plausible self-hosting guide](https://plausible.io/docs/self-hosting)
32
+ 2. Deploy Plausible alongside your app (or on a separate server)
33
+ 3. Set the environment variables:
34
+
35
+ ```bash
36
+ VITE_PLAUSIBLE_DOMAIN="yourdomain.com"
37
+ VITE_PLAUSIBLE_SCRIPT_URL="https://analytics.yourdomain.com/js/script.js"
38
+ ```
39
+
40
+ ## How It Works
41
+
42
+ The analytics script is conditionally injected into the HTML `<head>` at build time. When `VITE_PLAUSIBLE_DOMAIN` is empty (the default), no script is loaded and no data is collected.
43
+
44
+ The script tag uses Plausible's `defer` attribute and `data-domain` configuration — no additional client-side code is needed.
45
+
46
+ ## What Gets Tracked
47
+
48
+ By default, Plausible tracks:
49
+
50
+ - **Page views** — which pages are visited
51
+ - **Referral sources** — where visitors come from
52
+ - **Browser & OS** — what devices are used
53
+ - **Country** — geographic distribution (city-level detail is not collected)
54
+
55
+ Plausible does **not** track:
56
+
57
+ - Individual users or sessions
58
+ - Personal data or IP addresses
59
+ - Cross-site browsing behavior
60
+
61
+ ## Custom Events (Optional)
62
+
63
+ To track custom events (button clicks, signups, etc.), use the Plausible JavaScript API:
64
+
65
+ ```typescript
66
+ // Track a custom event
67
+ window.plausible?.('Signup', { props: { plan: 'pro' } });
68
+
69
+ // Track a goal conversion
70
+ window.plausible?.('Purchase', { revenue: { currency: 'USD', amount: '19.00' } });
71
+ ```
72
+
73
+ Add the TypeScript declaration to avoid type errors:
74
+
75
+ ```typescript
76
+ // src/client/types/plausible.d.ts
77
+ declare global {
78
+ interface Window {
79
+ plausible?: (event: string, options?: { props?: Record<string, string>; revenue?: { currency: string; amount: string } }) => void;
80
+ }
81
+ }
82
+ export {};
83
+ ```
84
+
85
+ ## Dashboard
86
+
87
+ Access your analytics dashboard at:
88
+
89
+ - **Cloud**: `https://plausible.io/yourdomain.com`
90
+ - **Self-hosted**: `https://analytics.yourdomain.com/yourdomain.com`
@@ -0,0 +1,231 @@
1
+ ---
2
+ title: "Authentication"
3
+ description: "Configure sign-in methods, OAuth providers, and multi-factor authentication."
4
+ order: 4
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ Authentication is handled by self-hosted Supabase Auth. It supports:
10
+
11
+ - **Email/password** sign-up and sign-in
12
+ - **OAuth** providers (Google, Microsoft, GitHub, Apple, Discord)
13
+ - **Magic links** — passwordless email authentication
14
+ - **Multi-factor authentication** (TOTP)
15
+
16
+ All auth state is managed through the `useAuth()` hook on the client and JWT middleware on the server.
17
+
18
+ ## useAuth() Hook
19
+
20
+ The `useAuth()` hook (from `AuthProvider`) exposes everything you need for auth in React components:
21
+
22
+ | Property | Type | Description |
23
+ |----------|------|-------------|
24
+ | `user` | `User \| null` | Current Supabase user object |
25
+ | `session` | `Session \| null` | Current session with tokens |
26
+ | `isLoading` | `boolean` | `true` while session is being validated |
27
+ | `isSuperAdmin` | `boolean` | User has `super_admin` role in `app_metadata` |
28
+ | `mfaRequired` | `boolean` | MFA is enforced app-wide (set by admin) |
29
+ | `hasMfaEnabled` | `boolean` | Current user has enrolled a TOTP factor |
30
+ | `signIn` | `object` | Sign-in methods (see below) |
31
+ | `signUp` | `function` | Create a new account |
32
+ | `signOut` | `function` | Sign out and clear session |
33
+ | `mfa` | `object` | MFA operations (enroll, challenge, verify) |
34
+ | `impersonateUser` | `function` | Impersonate a user (super admin only) |
35
+ | `stopImpersonating` | `function` | Return to admin session |
36
+
37
+ ```typescript
38
+ import { useAuth } from '@/components/auth/AuthProvider';
39
+
40
+ function MyComponent() {
41
+ const { user, isLoading, isSuperAdmin } = useAuth();
42
+
43
+ if (isLoading) return <p>Loading...</p>;
44
+ if (!user) return <p>Not signed in</p>;
45
+
46
+ return <p>Hello, {user.email}</p>;
47
+ }
48
+ ```
49
+
50
+ ## Sign-In Methods
51
+
52
+ ### Email & Password
53
+
54
+ The default sign-in method. Calls the server-side login endpoint which handles account lockout checks before authenticating.
55
+
56
+ ```typescript
57
+ const { signIn } = useAuth();
58
+
59
+ try {
60
+ const { requiresMfa } = await signIn.email('user@example.com', 'password');
61
+ if (requiresMfa) {
62
+ // Redirect to MFA verification page
63
+ }
64
+ } catch (error) {
65
+ // Handle error (invalid credentials, account locked, etc.)
66
+ }
67
+ ```
68
+
69
+ The `signIn.email()` method returns `{ requiresMfa: boolean }`. When `true`, the user has MFA enrolled and must verify a TOTP code before accessing the app.
70
+
71
+ ### Magic Links
72
+
73
+ Users receive a sign-in link via email — no password required. Requires SMTP to be configured.
74
+
75
+ ```typescript
76
+ const { signIn } = useAuth();
77
+
78
+ await signIn.magicLink('user@example.com');
79
+ // User receives an email with a sign-in link
80
+ ```
81
+
82
+ ### OAuth Providers
83
+
84
+ Enable social login by configuring providers. Each provider has a dedicated method:
85
+
86
+ ```typescript
87
+ const { signIn } = useAuth();
88
+
89
+ await signIn.google();
90
+ await signIn.github();
91
+ await signIn.microsoft();
92
+ await signIn.apple();
93
+ await signIn.discord();
94
+ ```
95
+
96
+ To enable an OAuth provider:
97
+
98
+ 1. Set the environment variable in `.env.local` (e.g., `GOOGLE_ENABLED=true`)
99
+ 2. Open **Supabase Studio** → **Authentication** → **Providers**
100
+ 3. Configure the **client ID** and **secret** from the provider's developer console
101
+ 4. The provider button automatically appears on the sign-in page
102
+
103
+ ## Sign-Up
104
+
105
+ Create a new account with email, password, and an optional display name:
106
+
107
+ ```typescript
108
+ const { signUp } = useAuth();
109
+
110
+ await signUp('user@example.com', 'password', 'Jane Smith');
111
+ // User is signed in and redirected to onboarding
112
+ ```
113
+
114
+ The `name` parameter is stored in `user_metadata.full_name`.
115
+
116
+ ## Route Protection
117
+
118
+ Two route guard components are defined in `App.tsx`:
119
+
120
+ **`ProtectedRoute`** — Requires any authenticated user. Redirects to `/sign-in` if not logged in. Also redirects to `/mfa-verify` if MFA is required but not yet verified.
121
+
122
+ **`SuperAdminProtectedRoute`** — Requires `super_admin` role. Redirects to the dashboard if the user is not a super admin.
123
+
124
+ ```tsx
125
+ // In App.tsx routes
126
+ <Route
127
+ path="/dashboard"
128
+ element={
129
+ <ProtectedRoute>
130
+ <Dashboard />
131
+ </ProtectedRoute>
132
+ }
133
+ />
134
+
135
+ <Route
136
+ path="/admin/*"
137
+ element={
138
+ <SuperAdminProtectedRoute>
139
+ <AdminPanel />
140
+ </SuperAdminProtectedRoute>
141
+ }
142
+ />
143
+ ```
144
+
145
+ Public pages (like the landing page, blog, docs) don't need a wrapper.
146
+
147
+ ## Multi-Factor Authentication
148
+
149
+ ### Enrollment
150
+
151
+ Users enroll in MFA from **Settings → Security**. The flow:
152
+
153
+ 1. Call `mfa.enroll()` to generate a TOTP secret and QR code
154
+ 2. User scans the QR code with an authenticator app
155
+ 3. User enters a TOTP code to verify enrollment
156
+
157
+ ```typescript
158
+ const { mfa } = useAuth();
159
+
160
+ // Step 1: Enroll
161
+ const data = await mfa.enroll('My Authenticator');
162
+ // data.totp.qr_code — data URI for the QR code
163
+ // data.totp.uri — otpauth:// URI for manual entry
164
+
165
+ // Step 2: Verify enrollment with a code from the authenticator app
166
+ await mfa.challengeAndVerify(data.id, '123456');
167
+ ```
168
+
169
+ ### Sign-In with MFA
170
+
171
+ When a user with MFA signs in via email/password:
172
+
173
+ 1. `signIn.email()` returns `{ requiresMfa: true }`
174
+ 2. The app redirects to `/mfa-verify`
175
+ 3. User enters their TOTP code
176
+ 4. The code is verified against the pending challenge
177
+
178
+ ```typescript
179
+ const { signIn, pendingMfaChallenge, mfa } = useAuth();
180
+
181
+ // During sign-in
182
+ const { requiresMfa } = await signIn.email(email, password);
183
+ if (requiresMfa) {
184
+ // Navigate to MFA verification page
185
+ }
186
+
187
+ // On the MFA verification page
188
+ if (pendingMfaChallenge) {
189
+ await mfa.verify(
190
+ pendingMfaChallenge.factorId,
191
+ pendingMfaChallenge.challengeId,
192
+ totpCode
193
+ );
194
+ }
195
+ ```
196
+
197
+ ### Admin Enforcement
198
+
199
+ Super admins can require MFA for all users from the admin dashboard. This sets the `mfa_enabled` key in the `app_settings` table. When enforced, `ProtectedRoute` redirects users without MFA to the enrollment page.
200
+
201
+ ## Account Lockout
202
+
203
+ Brute force protection is built in. After **5 failed login attempts** from the same email and IP address within a **15-minute window**, the account is locked.
204
+
205
+ - Locked accounts receive an HTTP `429 Too Many Requests` response with `remaining_minutes`
206
+ - Failed attempts are tracked in the `failed_login_attempts` table
207
+ - Successful login clears all failed attempts for that email/IP pair
208
+ - Super admins can unlock accounts via the `admin_unlock_account(email)` database function
209
+
210
+ ## Server-Side Auth
211
+
212
+ API requests include the user's JWT in the `Authorization` header. The server middleware validates it and makes the user available in route handlers:
213
+
214
+ ```typescript
215
+ const user = c.get('user');
216
+ if (!user) return c.json({ error: 'Unauthorized' }, 401);
217
+ ```
218
+
219
+ The middleware chain runs on every request:
220
+
221
+ 1. **Extract token** from `Authorization: Bearer <token>` header
222
+ 2. **Validate** with `supabase.auth.getUser()` (server-side verification)
223
+ 3. **Set context** — `c.set('user', user)` and `c.set('supabase', client)` where `supabase` is a user-scoped client that respects RLS
224
+
225
+ For operations that need to bypass RLS (admin tasks, webhooks), use the service role client:
226
+
227
+ ```typescript
228
+ import { supabaseAdmin } from '@/server/lib/supabase';
229
+
230
+ const { data } = await supabaseAdmin.from('organizations').select('*');
231
+ ```
@@ -0,0 +1,116 @@
1
+ ---
2
+ title: "Background Jobs"
3
+ description: "Schedule and manage recurring tasks with pg_cron."
4
+ order: 7
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ Background jobs are powered by **pg_cron**, a PostgreSQL extension that runs scheduled tasks directly in the database. This means zero additional infrastructure — no Redis, no separate worker process, no external job queue.
10
+
11
+ ## Built-in Jobs
12
+
13
+ Your app comes with three pre-configured jobs:
14
+
15
+ | Job | Schedule | Description |
16
+ |-----|----------|-------------|
17
+ | `cleanup-login-attempts` | Every 6 hours | Removes failed login attempts older than 24 hours |
18
+ | `cleanup-expired-notifications` | Daily at 3 AM | Deletes notifications past their expiry date |
19
+ | `cleanup-job-history` | Weekly (Sunday 4 AM) | Prunes job execution history older than 30 days |
20
+
21
+ ## Admin Panel
22
+
23
+ Super admins can view and manage jobs from **Admin > Jobs**:
24
+
25
+ - View all scheduled jobs and their last execution status
26
+ - See execution history with timestamps and results
27
+ - Manually trigger any job with the "Run Now" button
28
+
29
+ ## Adding Custom Jobs
30
+
31
+ To add a new scheduled job, create a new SQL migration:
32
+
33
+ ```sql
34
+ -- supabase/migrations/XXXXXX_my_custom_job.sql
35
+
36
+ SELECT cron.schedule(
37
+ 'my-job-name', -- unique job name
38
+ '*/30 * * * *', -- cron expression (every 30 minutes)
39
+ $$
40
+ DO $$
41
+ DECLARE
42
+ affected INTEGER;
43
+ BEGIN
44
+ -- Your SQL logic here
45
+ DELETE FROM some_table WHERE expired_at < now();
46
+ GET DIAGNOSTICS affected = ROW_COUNT;
47
+
48
+ -- Log the result
49
+ PERFORM public.log_cron_job('my-job-name', 'succeeded',
50
+ 'Processed ' || affected || ' rows');
51
+ EXCEPTION WHEN OTHERS THEN
52
+ PERFORM public.log_cron_job('my-job-name', 'failed', NULL, SQLERRM);
53
+ END $$;
54
+ $$
55
+ );
56
+ ```
57
+
58
+ Run `pnpm db:migrate` to apply the migration.
59
+
60
+ ## Cron Expression Reference
61
+
62
+ ```
63
+ ┌───────────── minute (0-59)
64
+ │ ┌───────────── hour (0-23)
65
+ │ │ ┌───────────── day of month (1-31)
66
+ │ │ │ ┌───────────── month (1-12)
67
+ │ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
68
+ │ │ │ │ │
69
+ * * * * *
70
+ ```
71
+
72
+ Common patterns:
73
+
74
+ | Expression | Meaning |
75
+ |-----------|---------|
76
+ | `*/5 * * * *` | Every 5 minutes |
77
+ | `0 */6 * * *` | Every 6 hours |
78
+ | `0 3 * * *` | Daily at 3:00 AM |
79
+ | `0 0 * * 0` | Weekly on Sunday at midnight |
80
+ | `0 0 1 * *` | Monthly on the 1st at midnight |
81
+
82
+ ## Managing Jobs
83
+
84
+ ### Remove a Job
85
+
86
+ ```sql
87
+ SELECT cron.unschedule('job-name');
88
+ ```
89
+
90
+ ### List All Jobs
91
+
92
+ ```sql
93
+ SELECT * FROM cron.job;
94
+ ```
95
+
96
+ ### View Execution Log
97
+
98
+ ```sql
99
+ SELECT * FROM cron.job_run_details
100
+ ORDER BY start_time DESC
101
+ LIMIT 20;
102
+ ```
103
+
104
+ ## Job History
105
+
106
+ All job executions are logged in the `cron_job_history` table. Use `public.log_cron_job()` in your custom jobs to record results:
107
+
108
+ ```sql
109
+ -- Log success
110
+ PERFORM public.log_cron_job('my-job', 'succeeded', 'Processed 42 rows');
111
+
112
+ -- Log failure
113
+ PERFORM public.log_cron_job('my-job', 'failed', NULL, 'Connection timeout');
114
+ ```
115
+
116
+ History is automatically cleaned up after 30 days by the `cleanup-job-history` job.