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,422 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents working on this codebase. The **Security Rules** section below is mandatory — violations introduce real vulnerabilities.
4
+
5
+ ## Tech Stack
6
+
7
+ - **Frontend**: React 19, Vite, React Router, TanStack Query, Shadcn UI, Tailwind CSS v4, i18next
8
+ - **Backend**: Hono (lightweight Node.js framework), Pino logger, Zod validation, nodemailer (SMTP email)
9
+ - **Auth/Database**: Self-hosted Supabase (PostgreSQL, Auth, REST API, Realtime, Storage)
10
+ - **Billing**: Stripe (subscriptions, checkout, webhooks, plan gating)
11
+ - **Content**: MDX (blog, changelog, docs) with remark/rehype plugins
12
+ - **Tooling**: Biome (lint/format), TypeScript, esbuild, Vitest
13
+
14
+ ## Commands
15
+
16
+ ```bash
17
+ # Development
18
+ pnpm dev:start # Full cold start: Docker + migrations + dev servers
19
+ pnpm dev # Start API (port 3000) and Vite (port 5173)
20
+ pnpm dev:reset # Remove containers, volumes, and built images
21
+
22
+ # Build & Test
23
+ pnpm build # Build client and server
24
+ pnpm lint # Biome linting
25
+ pnpm typecheck # TypeScript type checking
26
+ pnpm test # Run every tier (unit + component + integration)
27
+ pnpm test:unit # Pure functions, validators, shared lib
28
+ pnpm test:component # React components + hooks (RTL + jsdom)
29
+ pnpm test:integration # Hono routes with mocked Supabase/Stripe/SMTP
30
+ pnpm test:watch # Vitest watch mode
31
+ pnpm test:coverage # Generate coverage/ report (v8)
32
+ pnpm test:prepush # What the pre-push git hook runs
33
+
34
+ # Docker
35
+ pnpm docker:up # Start Supabase services
36
+ pnpm docker:down # Stop services
37
+ pnpm docker:reset # Remove containers, volumes, and built images
38
+ pnpm db:migrate # Run SQL migrations
39
+ ```
40
+
41
+ ## Architecture
42
+
43
+ ### Directory Structure
44
+ ```
45
+ src/
46
+ ├── client/ # React SPA (Vite dev server on :5173)
47
+ │ ├── components/
48
+ │ │ ├── ui/ # 50+ Shadcn UI components
49
+ │ │ └── auth/ # AuthProvider, ProtectedRoute
50
+ │ ├── hooks/ # Custom React hooks
51
+ │ ├── pages/ # Route pages (admin/, settings/, organizations/)
52
+ │ ├── lib/ # supabase.ts, i18n.ts, utils.ts, blog/changelog/docs loaders
53
+ │ └── locales/ # i18n translation files
54
+ ├── server/ # Hono API (Node.js on :3000)
55
+ │ ├── index.ts # App entry, middleware, route mounting
56
+ │ ├── routes/ # health.ts, v1/, webhooks/, _internal/
57
+ │ ├── emails/ # React Email templates
58
+ │ └── lib/ # supabase.ts, env.ts, email.ts, stripe.ts, logger.ts, rate-limiter.ts
59
+ ├── shared/ # Shared TypeScript types (types.ts, pricing.ts)
60
+ content/ # MDX content (blog/, changelog/, docs/)
61
+ supabase/migrations/ # SQL migrations (run with pnpm db:migrate)
62
+ ```
63
+
64
+ ### API Routes
65
+
66
+ | Route | Description |
67
+ |-------|-------------|
68
+ | `GET /api/health` | Liveness probe (no DB check, public) |
69
+ | `GET /api/health/ready` | Readiness probe (checks DB connectivity, public) |
70
+ | `POST /api/v1/auth/login` | Email/password login with lockout protection |
71
+ | `GET /api/v1/auth/settings` | Auth settings (enabled providers, MFA config) |
72
+ | `GET /api/v1/me` | Current user info with memberships |
73
+ | `GET/POST /api/v1/organizations` | List/create organizations |
74
+ | `GET/POST /api/v1/organizations/:orgId/members` | Manage members |
75
+ | `PATCH /api/v1/organizations/:orgId/members/:userId` | Update member role |
76
+ | `GET /api/v1/notifications` | User's active notifications |
77
+ | `POST /api/v1/notifications/:id/dismiss` | Dismiss notification |
78
+ | `POST /api/v1/billing/checkout` | Create checkout session (Stripe/Paddle/Polar) |
79
+ | `POST /api/v1/billing/portal` | Create customer portal session |
80
+ | `POST /api/v1/contact/submit` | Submit contact form (public, rate-limited) |
81
+ | `POST /api/v1/newsletter/subscribe` | Subscribe to newsletter (public, rate-limited) |
82
+ | `GET /api/v1/newsletter/confirm` | Confirm newsletter subscription (via email token) |
83
+ | `GET /api/v1/newsletter/unsubscribe` | Unsubscribe from newsletter |
84
+ | `GET /api/v1/admin/users` | List all users (super admin) |
85
+ | `GET /api/v1/admin/organizations` | List all organizations (super admin) |
86
+ | `POST /api/v1/admin/impersonate/:userId` | Impersonate user (super admin) |
87
+ | `GET/POST /api/v1/admin/notifications` | Manage notifications (super admin) |
88
+ | `GET /api/v1/admin/stats` | Platform-wide stats (super admin) |
89
+ | `GET /api/v1/admin/performance` | Service performance/health checks (super admin) |
90
+ | `GET /api/v1/admin/jobs` | Background jobs and execution history (super admin) |
91
+ | `POST /api/v1/admin/jobs/trigger` | Manually trigger a background job (super admin) |
92
+ | `GET/PATCH/DELETE /api/v1/admin/contact` | Manage contact submissions (super admin) |
93
+ | `GET /api/v1/admin/newsletter` | Manage newsletter subscribers (super admin) |
94
+ | `POST /api/v1/admin/newsletter/send` | Send newsletter to active subscribers (super admin) |
95
+ | `GET /api/v1/admin/newsletter/export` | Export subscribers as CSV (super admin) |
96
+ | `GET /api/_internal/verify-role` | Traefik ForwardAuth |
97
+ | `POST /api/webhooks/billing` | Billing webhook handler (Stripe/Paddle/Polar) |
98
+ | `POST /api/webhooks/stripe` | Stripe webhook handler (backward compat) |
99
+ | `GET /api/docs` | Scalar API reference (dev only) |
100
+
101
+ ### Authentication Flow
102
+
103
+ 1. **Client-side**: Supabase JS handles auth (email/password, OAuth, magic links, MFA)
104
+ 2. **API requests**: Client sends JWT in `Authorization: Bearer <token>` header
105
+ 3. **Server middleware**: Extracts token, calls `supabase.auth.getUser()`, sets `c.get('user')`
106
+ 4. **RLS**: Database queries respect row-level security based on authenticated user
107
+
108
+ ## Supabase Client Usage
109
+
110
+ This is the #1 source of bugs. Three clients exist:
111
+
112
+ **Client-side** (`src/client/lib/supabase.ts`) — uses anon key, RLS enforced:
113
+ ```typescript
114
+ import { supabase } from '@/lib/supabase';
115
+ const { data } = await supabase.from('organizations').select('*'); // RLS enforced
116
+ ```
117
+
118
+ **Server-side** (`src/server/lib/supabase.ts`) — two options:
119
+ ```typescript
120
+ // User-context queries (respects RLS) — use for most routes
121
+ const supabase = c.get('supabase');
122
+
123
+ // Admin operations (bypasses RLS) — use only when necessary
124
+ import { supabaseAdmin } from '@/server/lib/supabase';
125
+
126
+ // Auth operations — NEVER use supabaseAdmin for signInWithPassword
127
+ // (stores session state on singleton, contaminates subsequent queries)
128
+ import { createAuthClient } from '@/server/lib/supabase';
129
+ const authClient = createAuthClient();
130
+ ```
131
+
132
+ ## Key Patterns
133
+
134
+ ### Adding an API Endpoint
135
+
136
+ 1. Create route in `src/server/routes/v1/` — follow existing routes for the `Variables` type, Zod validation, and auth check pattern
137
+ 2. Mount in `src/server/index.ts`: `app.route('/api/v1/items', myRoutes)`
138
+
139
+ ### Adding a Page
140
+
141
+ 1. Create component in `src/client/pages/` — use `SidebarProvider` + `AppSidebar` + `SidebarInset` layout (see existing pages)
142
+ 2. Add route in `src/client/App.tsx` wrapped in `<ProtectedRoute>`
143
+
144
+ ### Adding a Database Table
145
+
146
+ 1. Create migration in `supabase/migrations/` with:
147
+ - Table with `id UUID PRIMARY KEY DEFAULT gen_random_uuid()`, `created_at`, `updated_at`
148
+ - Index on foreign key columns
149
+ - `ALTER TABLE ... ENABLE ROW LEVEL SECURITY`
150
+ - RLS policies using `auth.uid()` for user-scoped access (see `00001_init.sql` for patterns)
151
+ - `update_updated_at()` trigger
152
+ 2. Run `pnpm db:migrate` and add types to `src/shared/types.ts`
153
+
154
+ ### Data Fetching (TanStack Query)
155
+
156
+ **NEVER use raw `fetch` in `useEffect`** — always use `useQuery` for reads and `useMutation` for writes. Raw fetch in useEffect bypasses TanStack Query's deduplication, caching, and retry logic, and React StrictMode will double-fire it in development.
157
+
158
+ ```typescript
159
+ // CORRECT: useQuery with auth guard
160
+ const { data, isLoading } = useQuery({
161
+ queryKey: ['items', orgId],
162
+ queryFn: async () => {
163
+ const headers = await getAuthHeaders();
164
+ const res = await fetch(`/api/v1/items?orgId=${orgId}`, { headers });
165
+ if (!res.ok) throw new Error('Failed to fetch items');
166
+ return res.json();
167
+ },
168
+ enabled: !!user && !!orgId, // Skip fetch when unauthenticated or missing params
169
+ });
170
+
171
+ // WRONG: raw fetch in useEffect
172
+ useEffect(() => {
173
+ fetch('/api/v1/items').then(r => r.json()).then(setItems);
174
+ }, []);
175
+ ```
176
+
177
+ Rules:
178
+ - **Gate authenticated queries** with `enabled: !!user` (or `!!session`) to prevent 401s for logged-out users
179
+ - **Use `getAuthHeaders()`** from `@/lib/api.ts` for auth headers — never duplicate it locally, never send empty `Authorization` headers
180
+ - **Place reusable hooks** in `src/client/hooks/api/` — page-specific queries can live in the page file
181
+ - **Invalidate related queries** after mutations: `queryClient.invalidateQueries({ queryKey: ['items'] })`
182
+ - **Set `staleTime`** to control cache freshness (default is 5min via QueryClient config)
183
+
184
+ ## Database Schema
185
+
186
+ | Table | Purpose |
187
+ |-------|---------|
188
+ | `organizations` | Multi-tenant organizations (name, slug, plan) |
189
+ | `memberships` | User-org relationships (role: OWNER, ADMIN, MEMBER) |
190
+ | `customers` | Billing customer records (user or org, any provider) |
191
+ | `subscriptions` | Billing subscription data |
192
+ | `notifications` | System-wide or org-specific notifications |
193
+ | `notification_dismissals` | Track dismissed notifications per user |
194
+ | `failed_login_attempts` | Brute force protection |
195
+ | `app_settings` | Global app configuration |
196
+ | `cron_job_history` | Background job execution history (pg_cron) |
197
+ | `contact_submissions` | Contact form submissions (name, email, subject, message, status) |
198
+ | `newsletter_subscribers` | Newsletter subscribers (email, status: pending/active/unsubscribed) |
199
+
200
+ RLS helper functions: `get_user_org_ids()`, `get_user_admin_org_ids()`, `is_super_admin()`
201
+
202
+ ## Path Aliases
203
+
204
+ - `@/*` resolves to `./src/client/*`
205
+ - `@shared/*` resolves to `./src/shared/*`
206
+ - `@server/*` resolves to `./src/server/*` (test-only — server code uses relative imports)
207
+ - **Never use `@/shared/*`** — it won't resolve
208
+
209
+ ## Testing
210
+
211
+ Three tiers — pick the narrowest one that exercises the regression you're worried about.
212
+
213
+ | Tier | Env | What it covers | Where it lives |
214
+ |---|---|---|---|
215
+ | `unit` | node | Pure functions, validators, pricing/format/business helpers, structural invariants (e.g. i18n parity) | `tests/unit/`, `tests/structural/` |
216
+ | `component` | jsdom | React components + custom hooks via React Testing Library | `tests/component/` |
217
+ | `integration` | node | Hono route handlers end-to-end via `app.request()`, with Supabase/Stripe/SMTP mocked at the module boundary | `tests/integration/` |
218
+
219
+ Helpers live in `tests/_helpers/`:
220
+
221
+ - `app.ts` — `mountRoute(prefix, routes)` to build a tiny Hono app for one route; `jsonPost(body)` for request boilerplate.
222
+ - `factories.ts` — `makeUser`, `makeOrg`, `makeContactSubmission` for plausible domain fixtures.
223
+ - `jwt.ts` — `mockJwt(payload)` for HS256-signed test tokens.
224
+ - `env.ts` — `mockEnv({...})` returning a restore closure (no globals).
225
+ - `setup-rtl.ts` — vitest setup for the component project (jest-dom matchers + RTL cleanup).
226
+ - `setup-integration.ts` — seeds plausible Supabase env values so server modules load without zod-validation errors.
227
+
228
+ **Mocking conventions:**
229
+ - Supabase: `vi.mock('@server/lib/supabase', () => ({ supabaseAdmin: { from: () => ({ insert: vi.fn() }) } }))`.
230
+ - Stripe: `vi.mock('@server/lib/stripe', () => ({ stripe: { ... } }))`.
231
+ - fetch (client side): `vi.stubGlobal('fetch', vi.fn(...))` + `vi.unstubAllGlobals()` in afterEach.
232
+ - TanStack Query: wrap with a fresh `QueryClient` per render (`retry: false`) — see `tests/component/use-auth-settings.test.tsx`.
233
+ - Rate limiter: `vi.mock('@server/lib/rate-limiter', () => ({ createRateLimiter: () => async (_c, next) => { await next(); } }))` — the real module starts a 60s setInterval at module load.
234
+
235
+ See `TESTING.md` for the full guide. The `test-maintainer` agent reads the tier directories to learn project conventions and will follow them automatically.
236
+
237
+ ## Agent Orchestration
238
+
239
+ This project uses Claude Code's agent teams with a **lead-coordinator** that orchestrates 4 specialist teammates:
240
+
241
+ | Agent | Role | Quality Gate |
242
+ |-------|------|-------------|
243
+ | `lead-coordinator` | Decomposes tasks, delegates, synthesizes results | — |
244
+ | `backend-engineer` | Backend APIs, database, Docker/K8s, server logic | `pnpm lint` + `pnpm typecheck` on idle |
245
+ | `frontend-engineer` | Frontend pages, components, layouts, styling | `pnpm lint` + `pnpm typecheck` on idle |
246
+ | `security-reviewer` | Security audit after backend/infra changes | — |
247
+ | `test-maintainer` | Test writing after any meaningful code change | `pnpm test:unit` on task complete |
248
+
249
+ **Typical workflow**: Use the lead-coordinator for complex multi-step tasks. It decomposes the work, spawns specialists in dependency order (migrations -> API -> security review -> frontend -> tests), and synthesizes results. For simple single-domain tasks, invoke the specialist directly.
250
+
251
+ **Quality gates** are enforced via hooks in `.claude/hooks/`:
252
+ - `teammate-idle-gate.sh` — lint + typecheck before backend/frontend engineers go idle
253
+ - `task-completed-gate.sh` — unit tests must pass before QA marks a task complete
254
+
255
+ **Configuration**: `.claude/settings.json` registers the hooks and the `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` env var required for agent teams.
256
+
257
+ ## Important Notes
258
+
259
+ - **Prefer existing patterns** — follow the established code style in existing routes/components
260
+ - **Auth is handled by Supabase** — don't implement custom auth, use `supabase.auth.*` methods
261
+ - **Add Shadcn components** with `pnpm dlx shadcn@latest add [component-name]`
262
+
263
+ ---
264
+
265
+ # Security Rules (Mandatory)
266
+
267
+ The rules below are mandatory — violations introduce real vulnerabilities. The three most critical:
268
+
269
+ 1. **Always check auth** — every endpoint must verify `c.get('user')` and return 401 if null
270
+ 2. **Always validate input** — use Zod schemas for all API request bodies before processing
271
+ 3. **Use `c.get('supabase')` for user data** — never use `supabaseAdmin` for queries shown to end users; it bypasses RLS
272
+
273
+ ## Database Security (RLS)
274
+
275
+ - **MUST** enable RLS on every new table: `ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;`
276
+ - **MUST** use `(SELECT auth.uid())` in RLS policies, not bare `auth.uid()` — the subquery form prevents the query planner from re-evaluating per row
277
+ - **MUST** use `SECURITY DEFINER` + `SET search_path = ''` on all helper functions used in RLS policies
278
+ - **NEVER** disable RLS on existing tables
279
+ - **NEVER** use `USING (true)` as a policy for user data — every table needs real ownership checks
280
+ - **NEVER** grant direct table access to `anon` or `authenticated` roles beyond what RLS policies allow
281
+
282
+ ### RLS Helper Functions
283
+
284
+ Use these existing functions in policies instead of writing inline queries:
285
+
286
+ | Function | Returns | Use When |
287
+ |----------|---------|----------|
288
+ | `get_user_org_ids()` | `SETOF UUID` | User needs access to any org they belong to |
289
+ | `get_user_admin_org_ids()` | `SETOF UUID` | Only OWNER or ADMIN roles should have access |
290
+ | `get_user_owner_org_ids()` | `SETOF UUID` | Only OWNER role should have access |
291
+ | `get_user_customer_ids()` | `SETOF UUID` | Billing data — includes personal + org customers |
292
+ | `is_super_admin()` | `BOOLEAN` | Platform-wide admin operations |
293
+
294
+ ### RLS Policy Pattern
295
+
296
+ ```sql
297
+ -- Correct: subquery form
298
+ CREATE POLICY "Users can view own data"
299
+ ON my_table FOR SELECT
300
+ USING (user_id = (SELECT auth.uid()));
301
+
302
+ -- Correct: using helper function
303
+ CREATE POLICY "Users can view org data"
304
+ ON my_table FOR SELECT
305
+ USING (organization_id IN (SELECT get_user_org_ids()));
306
+
307
+ -- WRONG: bare function call (performance issue)
308
+ CREATE POLICY "bad_policy"
309
+ ON my_table FOR SELECT
310
+ USING (user_id = auth.uid());
311
+ ```
312
+
313
+ ## API Route Security
314
+
315
+ - **MUST** check authentication on every endpoint: `const user = c.get('user'); if (!user) return c.json({ error: 'Unauthorized' }, 401);`
316
+ - **MUST** validate all input with Zod schemas before processing
317
+ - **MUST** use `c.get('supabase')` for user-facing database queries — this client respects RLS
318
+ - **MUST** validate redirect URLs against an allowlist — never redirect to arbitrary user-supplied URLs
319
+ - **MUST** use `createAuthClient()` for `signInWithPassword()` operations — never use the `supabaseAdmin` singleton for auth flows (it stores session state and contaminates subsequent queries)
320
+ - **MUST** verify webhook signatures before processing webhook payloads (Stripe uses `stripe.webhooks.constructEvent()`)
321
+ - **NEVER** use `supabaseAdmin` for data shown to end users — it bypasses RLS
322
+ - **NEVER** return raw database errors to clients — always return a generic error message
323
+
324
+ ### Supabase Client Selection
325
+
326
+ | Client | Import / Access | RLS | Use For |
327
+ |--------|----------------|-----|---------|
328
+ | `c.get('supabase')` | Hono context (middleware-injected) | Enforced | All user-facing queries |
329
+ | `supabaseAdmin` | `import { supabaseAdmin } from '@/server/lib/supabase'` | Bypassed | Admin operations, webhooks, background jobs |
330
+ | `createAuthClient()` | `import { createAuthClient } from '@/server/lib/supabase'` | N/A | `signInWithPassword()` and other auth flows |
331
+
332
+ ### API Endpoint Pattern
333
+
334
+ ```typescript
335
+ myRoutes.post('/', async (c) => {
336
+ // 1. Auth check
337
+ const user = c.get('user');
338
+ if (!user) return c.json({ error: 'Unauthorized' }, 401);
339
+
340
+ // 2. Input validation
341
+ const body = await c.req.json();
342
+ const result = schema.safeParse(body);
343
+ if (!result.success) {
344
+ return c.json({ error: result.error.issues.map(e => e.message).join(', ') }, 400);
345
+ }
346
+
347
+ // 3. Use RLS-enforced client
348
+ const supabase = c.get('supabase');
349
+ const { data, error } = await supabase.from('items').insert(result.data).select().single();
350
+
351
+ // 4. Generic error response
352
+ if (error) return c.json({ error: 'Failed to create item' }, 500);
353
+ return c.json({ item: data }, 201);
354
+ });
355
+ ```
356
+
357
+ ## Privileged Services
358
+
359
+ - **MUST** protect Studio, n8n, Metabase, and Grafana with Traefik ForwardAuth in production
360
+ - **MUST** use Docker socket proxy in production — never mount `/var/run/docker.sock` directly into Traefik
361
+ - **NEVER** expose internal Supabase services (Auth, PostgREST, Realtime, Storage) directly — they must go through Kong
362
+ - **NEVER** remove ForwardAuth middleware from production compose files or Kubernetes manifests
363
+
364
+ ### Adding a New Admin Service
365
+
366
+ 1. Add Traefik ForwardAuth label in the service's compose config:
367
+ ```yaml
368
+ labels:
369
+ - "traefik.http.routers.myservice.middlewares=super-admin-auth@file"
370
+ ```
371
+ 2. Create a dev override file that disables the middleware for local development:
372
+ ```yaml
373
+ labels:
374
+ - "traefik.http.routers.myservice.middlewares="
375
+ ```
376
+ 3. Use `Host(\`myservice.localhost\`)` for the routing rule
377
+
378
+ ### ForwardAuth Middleware Reference
379
+
380
+ | Middleware | Required Role | Used By |
381
+ |-----------|--------------|---------|
382
+ | `admin-auth` | `super_admin` | Studio, n8n, Metabase, Grafana (Kubernetes) |
383
+ | `super-admin-auth` | `super_admin` | Studio, n8n, Metabase, Grafana (Docker Compose) |
384
+
385
+ ## Environment & Secrets
386
+
387
+ - **NEVER** commit `.env`, `.env.local`, or any file containing secrets
388
+ - **NEVER** hardcode secrets, API keys, or passwords in source code
389
+ - **MUST** use the `VITE_` prefix for any environment variable that needs client-side access
390
+ - **NEVER** expose `SUPABASE_SERVICE_ROLE_KEY` to the client — it bypasses all RLS
391
+ - **MUST** validate all server environment variables in `src/server/lib/env.ts`
392
+
393
+ ## Rate Limiting
394
+
395
+ Three tiers are configured in `src/server/index.ts`:
396
+
397
+ | Route Pattern | Limit | Reason |
398
+ |--------------|-------|--------|
399
+ | `/api/v1/*` | 100/min per IP | Standard API endpoints |
400
+ | `/api/webhooks/*` | 500/min per IP | External services (Stripe) send bursts |
401
+ | `/api/_internal/*` | 1000/min per IP | Infrastructure calls (Traefik ForwardAuth) |
402
+
403
+ When adding new route groups, assign an appropriate rate limit tier.
404
+
405
+ ## Infrastructure (Kubernetes)
406
+
407
+ - **MUST** use deny-all NetworkPolicy as the default — explicitly allowlist each communication path
408
+ - **MUST** set pod security context on all new deployments:
409
+ ```yaml
410
+ securityContext:
411
+ runAsNonRoot: true
412
+ runAsUser: 1000
413
+ runAsGroup: 1000
414
+ readOnlyRootFilesystem: true
415
+ allowPrivilegeEscalation: false
416
+ capabilities:
417
+ drop:
418
+ - ALL
419
+ ```
420
+ - **MUST** use `emptyDir` volumes for any writable paths (e.g., `/tmp`)
421
+ - **NEVER** run application containers as root (init containers are the only exception)
422
+ - **NEVER** add capabilities — drop ALL and don't add any back
@@ -0,0 +1,3 @@
1
+ # CLAUDE.md
2
+
3
+ See [AGENTS.md](./AGENTS.md) for all guidance on this codebase — tech stack, commands, architecture, API routes, Supabase client usage, key patterns, database schema, path aliases, agent orchestration, and mandatory security rules.
@@ -0,0 +1,187 @@
1
+ # Template Development Guide
2
+
3
+ This guide covers developing and testing the `carbon/` template directory locally.
4
+
5
+ ## Template Variable System
6
+
7
+ The CLI replaces placeholders like `{{PROJECT_NAME}}` when users run `npx vibecarbon create`. However, these placeholders cause issues when testing locally because:
8
+
9
+ 1. Services may reject special characters (`{}`) in identifiers
10
+ 2. YAML parsers may fail on unquoted template syntax
11
+ 3. Services expect actual values, not placeholder strings
12
+
13
+ ### Solution: Dev File Pattern
14
+
15
+ Every config file with template placeholders needs a corresponding dev file:
16
+
17
+ ```
18
+ volumes/<service>/config.yml # Template with {{PLACEHOLDERS}}
19
+ volumes/<service>/config.dev.yml # Dev file with hardcoded values
20
+ ```
21
+
22
+ Override files mount dev configs during local development. The build system automatically loads them in the correct order:
23
+
24
+ ```
25
+ docker-compose.yml # Base services
26
+ docker-compose.{service}.yml # Optional service (e.g., observability)
27
+ docker-compose.{service}.override.yml # Service-specific dev overrides
28
+ docker-compose.override.yml # Main dev overrides (loaded last)
29
+ ```
30
+
31
+ **Important:** Services defined in optional compose files must have their dev overrides in a matching `{service}.override.yml` file, not the main override file. This prevents errors when running without that service.
32
+
33
+ ## Adding a New Service
34
+
35
+ ### Step 1: Create template config
36
+
37
+ ```yaml
38
+ # volumes/myservice/config.yml
39
+ name: {{PROJECT_NAME}}
40
+ api_key: {{MYSERVICE_KEY}}
41
+ ```
42
+
43
+ ### Step 2: Generate dev config
44
+
45
+ Run the dev file generator:
46
+
47
+ ```bash
48
+ ./scripts/generate-dev-configs.sh
49
+ ```
50
+
51
+ Or manually create with hardcoded values:
52
+
53
+ ```yaml
54
+ # volumes/myservice/config.dev.yml
55
+ name: vibecarbon
56
+ api_key: dev-key-not-for-production
57
+ ```
58
+
59
+ ### Step 3: Add override mount
60
+
61
+ Add the volume mount to the appropriate override file based on **which service** the volume is mounted to:
62
+
63
+ - If mounting to a service in `docker-compose.yml` (db, kong, etc.) → use `docker-compose.override.yml`
64
+ - If mounting to a service in `docker-compose.{feature}.yml` → use `docker-compose.{feature}.override.yml`
65
+
66
+ Example for a service in the base compose file:
67
+
68
+ ```yaml
69
+ # docker-compose.override.yml
70
+ services:
71
+ db:
72
+ volumes:
73
+ - ./volumes/db/myservice-init.dev.sql:/docker-entrypoint-initdb.d/init-scripts/95-myservice.sql:Z
74
+ ```
75
+
76
+ Example for a service in an optional compose file:
77
+
78
+ ```yaml
79
+ # docker-compose.observability.override.yml
80
+ services:
81
+ grafana:
82
+ volumes:
83
+ - ./volumes/grafana/config.dev.yml:/etc/grafana/config.yml:ro
84
+ ```
85
+
86
+ ### Step 4: Add placeholder to CLI
87
+
88
+ In `src/create.js`, add to the replacements map:
89
+
90
+ ```javascript
91
+ ['{{MYSERVICE_KEY}}', generateSecret()],
92
+ ```
93
+
94
+ ### Step 5: Test both paths
95
+
96
+ ```bash
97
+ # Test local development
98
+ pnpm dev:start:all
99
+
100
+ # Test CLI generation
101
+ cd .. && node src/cli.js create test-app -y \
102
+ --admin-email=test@example.com \
103
+ --admin-password=testpass123
104
+ cd test-app && pnpm dev:start
105
+ ```
106
+
107
+ ## Current Dev File Mappings
108
+
109
+ ### Compose Override Files
110
+
111
+ | Service Compose | Dev Override | Notes |
112
+ |-----------------|--------------|-------|
113
+ | `docker-compose.yml` | `docker-compose.override.yml` | Core services (db, kong, traefik) |
114
+ | `docker-compose.observability.yml` | `docker-compose.observability.override.yml` | Grafana: volumes + no ForwardAuth |
115
+ | `docker-compose.n8n.yml` | `docker-compose.n8n.override.yml` | n8n: no ForwardAuth |
116
+ | `docker-compose.metabase.yml` | `docker-compose.metabase.override.yml` | Metabase: no ForwardAuth |
117
+
118
+ ### Volume Config Files
119
+
120
+ | Template File | Dev File | Notes |
121
+ |--------------|----------|-------|
122
+ | `volumes/db/super-admin.sql` | `volumes/db/super-admin.dev.sql` | Admin credentials |
123
+ | `volumes/grafana/provisioning/dashboards/dashboards.yml` | `...dashboards.dev.yml` | Folder UIDs |
124
+ | `volumes/grafana/dashboards/*.json` | `volumes/grafana/dashboards.dev/*.json` | Dashboard UIDs |
125
+ | `volumes/traefik/middlewares.yml` | `volumes/traefik/middlewares.dev.yml` | ForwardAuth uses host.docker.internal |
126
+
127
+ ## Automation
128
+
129
+ ### Generate all dev configs from templates
130
+
131
+ ```bash
132
+ ./scripts/generate-dev-configs.sh
133
+ ```
134
+
135
+ This script:
136
+ - Finds all template files with `{{PROJECT_NAME}}`
137
+ - Generates corresponding `.dev` versions
138
+ - Replaces common placeholders with dev values
139
+
140
+ ### Validate dev files exist
141
+
142
+ ```bash
143
+ ./scripts/validate-dev-configs.sh
144
+ ```
145
+
146
+ Run this in CI to ensure new template files have dev counterparts.
147
+
148
+ ## Common Issues
149
+
150
+ ### Container not picking up new mounts
151
+
152
+ After modifying `docker-compose.override.yml`, recreate the container:
153
+
154
+ ```bash
155
+ # Wrong - doesn't apply new mounts
156
+ docker compose restart myservice
157
+
158
+ # Correct - recreates with new mounts
159
+ docker compose up -d myservice
160
+ ```
161
+
162
+ ### Service rejecting config
163
+
164
+ Check if the service has restrictions on:
165
+ - Special characters in identifiers/UIDs
166
+ - Required fields that can't be template placeholders
167
+ - Strict YAML parsing that rejects `{{}}`
168
+
169
+ ### Template works but dev fails (or vice versa)
170
+
171
+ Ensure both files have identical structure - only values should differ.
172
+
173
+ ### Admin services (n8n, Metabase, Grafana) in development
174
+
175
+ In production, these services use Traefik ForwardAuth to require `super_admin` role. In development, ForwardAuth is disabled via service-specific override files:
176
+
177
+ - `docker-compose.n8n.override.yml`
178
+ - `docker-compose.metabase.override.yml`
179
+ - `docker-compose.observability.override.yml`
180
+
181
+ These override files set `traefik.http.routers.{service}.middlewares=` (empty) to remove the ForwardAuth requirement. Docker Compose labels are **additive**, so you must explicitly set the middleware label to empty to override it.
182
+
183
+ **If you get 401 errors:**
184
+
185
+ 1. Ensure the override files are included in your compose command
186
+ 2. Force recreate the containers: `docker compose ... up -d --force-recreate n8n metabase grafana`
187
+ 3. Verify the router has no middlewares: `curl http://traefik.localhost/api/http/routers/n8n@docker | jq .middlewares`