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,346 @@
1
+ name: Deploy
2
+
3
+ # Phase 4 of the GitOps refactor (2026-04-20). Deploy is now driven by
4
+ # GitHub Actions rather than the vibecarbon CLI applying manifests from a
5
+ # local workstation. This keeps:
6
+ #
7
+ # • Secrets in GitHub Environments (never committed, env-scoped).
8
+ # • Source of truth in the repo under k8s/.
9
+ # • Reconciliation via Flux (the cluster's own GitRepository +
10
+ # Kustomization watches the repo's main branch every minute).
11
+ #
12
+ # The CLI still creates the cluster (Pulumi) and bootstraps the first
13
+ # workflow run — thereafter, `git push` is the deploy verb.
14
+ #
15
+ # Security note: all external-input interpolation flows through the
16
+ # `env:` block rather than inline `${{ }}` in `run:` blocks, to avoid the
17
+ # script-injection class of workflow CVEs. Secrets are written to files
18
+ # via base64 when possible (kubeconfig) to avoid shell quoting hazards.
19
+
20
+ on:
21
+ # No `push` trigger: this workflow requires a per-environment KUBECONFIG_B64
22
+ # secret which only exists after `vibecarbon deploy --k8s` has seeded the
23
+ # matching GitHub Environment. A `push` trigger would fail fast (kubectl
24
+ # connection refused) for every deploy, whether k8s or compose-only. The
25
+ # vibecarbon CLI invokes this workflow explicitly via the gh CLI
26
+ # (triggerDeployWorkflow in gitops-deploy.js) with the right environment,
27
+ # so manual dispatch is the only entry point.
28
+ workflow_dispatch:
29
+ inputs:
30
+ environment:
31
+ description: 'Environment to deploy (dev / staging / prod)'
32
+ type: environment
33
+ required: true
34
+
35
+ concurrency:
36
+ # Serialize per-environment so two pushes don't race against Flux's
37
+ # reconcile loop.
38
+ group: deploy-${{ inputs.environment || 'prod' }}
39
+ cancel-in-progress: false
40
+
41
+ permissions:
42
+ contents: read
43
+ id-token: write # for future OIDC to cloud providers; not used today
44
+
45
+ jobs:
46
+ apply-secrets:
47
+ # Reads env-specific secrets from GitHub Environments and applies them
48
+ # to the cluster as the `vibecarbon-secrets` Secret. Flux does NOT
49
+ # manage this Secret (no manifest in k8s/) so there's no drift.
50
+ name: Apply env secrets → cluster
51
+ runs-on: ubuntu-latest
52
+ environment: ${{ inputs.environment || 'prod' }}
53
+ env:
54
+ KUBECONFIG: ${{ github.workspace }}/kubeconfig
55
+ steps:
56
+ - uses: actions/checkout@v4
57
+
58
+ - name: Install kubectl
59
+ uses: azure/setup-kubectl@v4
60
+ with:
61
+ # Pin to a specific minor; bump deliberately.
62
+ version: 'v1.32.0'
63
+
64
+ - name: Write kubeconfig from env secret
65
+ env:
66
+ KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }}
67
+ run: |
68
+ printf '%s' "$KUBECONFIG_B64" | base64 -d > "$KUBECONFIG"
69
+ chmod 600 "$KUBECONFIG"
70
+ kubectl version --client=true
71
+
72
+ - name: Ensure vibecarbon namespace exists
73
+ run: kubectl create namespace vibecarbon --dry-run=client -o yaml | kubectl apply -f -
74
+
75
+ # Apply the vibecarbon-secrets Secret. Server-side apply with a
76
+ # distinct field manager ensures Flux doesn't try to prune these
77
+ # fields later, and that rotations simply overwrite. Secret values
78
+ # are written via stdin heredoc using $ENV_VARS rather than
79
+ # `${{ secrets.X }}` interpolation (defense-in-depth).
80
+ - name: Apply vibecarbon-secrets Secret
81
+ env:
82
+ DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
83
+ JWT_SECRET: ${{ secrets.JWT_SECRET }}
84
+ SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
85
+ SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
86
+ HETZNER_API_TOKEN: ${{ secrets.HETZNER_API_TOKEN }}
87
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
88
+ SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
89
+ SITE_URL: ${{ vars.SITE_URL }}
90
+ run: |
91
+ # `kubectl create secret --from-literal=KEY=VALUE` handles
92
+ # arbitrary VALUEs correctly (passes through Unix argv, no
93
+ # shell interpretation). `--dry-run -o yaml | kubectl apply`
94
+ # is the idempotent create-or-update pattern.
95
+ kubectl create secret generic vibecarbon-secrets \
96
+ --namespace=vibecarbon \
97
+ --from-literal=DB_PASSWORD="$DB_PASSWORD" \
98
+ --from-literal=JWT_SECRET="$JWT_SECRET" \
99
+ --from-literal=SUPABASE_ANON_KEY="$SUPABASE_ANON_KEY" \
100
+ --from-literal=SUPABASE_SERVICE_ROLE_KEY="$SUPABASE_SERVICE_ROLE_KEY" \
101
+ --from-literal=HETZNER_API_TOKEN="$HETZNER_API_TOKEN" \
102
+ --from-literal=CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" \
103
+ --from-literal=SMTP_PASSWORD="$SMTP_PASSWORD" \
104
+ --from-literal=SITE_URL="$SITE_URL" \
105
+ --dry-run=client -o yaml \
106
+ | kubectl apply --server-side --force-conflicts \
107
+ --field-manager=github-actions-deploy -f -
108
+
109
+ - name: Apply hetzner-api-token Secret (cert-manager-webhook-hetzner)
110
+ if: vars.DNS_PROVIDER == 'hetzner'
111
+ env:
112
+ HETZNER_API_TOKEN: ${{ secrets.HETZNER_API_TOKEN }}
113
+ run: |
114
+ kubectl create namespace cert-manager --dry-run=client -o yaml | kubectl apply -f -
115
+ kubectl create secret generic hetzner-api-token \
116
+ --namespace=cert-manager \
117
+ --from-literal=api-token="$HETZNER_API_TOKEN" \
118
+ --dry-run=client -o yaml \
119
+ | kubectl apply --server-side --force-conflicts \
120
+ --field-manager=github-actions-deploy -f -
121
+
122
+ - name: Apply kube-system/hcloud Secret (CCM + CSI)
123
+ env:
124
+ HETZNER_API_TOKEN: ${{ secrets.HETZNER_API_TOKEN }}
125
+ HCLOUD_NETWORK_ID: ${{ vars.HCLOUD_NETWORK_ID }}
126
+ run: |
127
+ kubectl create secret generic hcloud \
128
+ --namespace=kube-system \
129
+ --from-literal=token="$HETZNER_API_TOKEN" \
130
+ --from-literal=network="$HCLOUD_NETWORK_ID" \
131
+ --dry-run=client -o yaml \
132
+ | kubectl apply --server-side --force-conflicts \
133
+ --field-manager=github-actions-deploy -f -
134
+
135
+ bootstrap-flux:
136
+ # Phase 4.3b.C of the GitOps refactor: idempotently install Flux +
137
+ # the flux-system Secret (Git auth) + the root GitRepository and
138
+ # Kustomizations that watch the customer's repo. Runs every deploy
139
+ # (safe — kubectl apply --server-side is a no-op when state matches).
140
+ #
141
+ # After this job succeeds, Flux is in charge of reconciling
142
+ # k8s/gitops/{supabase,cert-manager-webhook-hetzner}/ and k8s/base/
143
+ # from the main branch. The apply-secrets job is what keeps the
144
+ # per-env vibecarbon-secrets / hetzner-api-token / hcloud Secrets
145
+ # in sync with GitHub Environment secrets.
146
+ name: Bootstrap Flux + root Kustomization
147
+ needs: apply-secrets
148
+ runs-on: ubuntu-latest
149
+ environment: ${{ inputs.environment || 'prod' }}
150
+ env:
151
+ KUBECONFIG: ${{ github.workspace }}/kubeconfig
152
+ steps:
153
+ - uses: actions/checkout@v4
154
+
155
+ - name: Install kubectl
156
+ uses: azure/setup-kubectl@v4
157
+ with:
158
+ version: 'v1.32.0'
159
+
160
+ - name: Write kubeconfig from env secret
161
+ env:
162
+ KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }}
163
+ run: |
164
+ printf '%s' "$KUBECONFIG_B64" | base64 -d > "$KUBECONFIG"
165
+ chmod 600 "$KUBECONFIG"
166
+
167
+ - name: Install Flux if not present
168
+ # FLUX_INSTALL_URL matches src/lib/deploy/k8s/index.js so the
169
+ # imperative path and GitOps path land on the same Flux version.
170
+ # Bumping here requires a matching bump there + a re-test.
171
+ run: |
172
+ if ! kubectl -n flux-system get deployment source-controller > /dev/null 2>&1; then
173
+ kubectl apply -f https://github.com/fluxcd/flux2/releases/download/v2.8.5/install.yaml
174
+ kubectl -n flux-system wait --for=condition=available \
175
+ deployment/source-controller deployment/kustomize-controller \
176
+ deployment/helm-controller deployment/notification-controller \
177
+ --timeout=5m
178
+ fi
179
+
180
+ # cert-manager + Traefik CRDs must be installed BEFORE the root
181
+ # Kustomization reconciles. k8s/base/traefik/ contains IngressRoute
182
+ # (traefik.io/v1alpha1) and Certificate (cert-manager.io/v1)
183
+ # resources; Flux's vibecarbon-base Kustomization will sit with
184
+ # "Ready=False: resource mapping not found" forever without the
185
+ # CRDs installed out-of-band.
186
+ #
187
+ # Under the pre-GitOps flow these were installed imperatively by
188
+ # applyKubernetesManifests before Flux was bootstrapped (see
189
+ # src/lib/deploy/k8s/index.js and the CERT_MANAGER_INSTALL_URL
190
+ # constant). The GitOps refactor moved Flux bootstrap here but
191
+ # left this step out — e2e run 2026-04-22 18:29 UTC showed
192
+ # vibecarbon-base timing out after 10m on exactly this.
193
+ #
194
+ # cert-manager v1.20.2 matches k8s/infra/kustomization.yaml +
195
+ # CERT_MANAGER_VERSION in src/lib/deploy/k8s/k3s.js; bump in lockstep.
196
+ - name: Install cert-manager + Traefik CRDs
197
+ run: |
198
+ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.yaml
199
+ kubectl apply -k k8s/infra/traefik-crds/
200
+ kubectl -n cert-manager wait --for=condition=available \
201
+ deployment/cert-manager \
202
+ deployment/cert-manager-webhook \
203
+ deployment/cert-manager-cainjector \
204
+ --timeout=5m
205
+
206
+ - name: Create flux-system Secret (Git auth for private repo)
207
+ # Flux uses `username`/`password` keys on a namespace Secret to
208
+ # pull the GitRepository. Using the workflow's GITHUB_TOKEN keeps
209
+ # everything within the repo's own perms — no separate PAT to
210
+ # rotate. `x-access-token` is GitHub's convention for short-lived
211
+ # Actions tokens.
212
+ env:
213
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
214
+ run: |
215
+ kubectl create namespace flux-system --dry-run=client -o yaml | kubectl apply -f -
216
+ kubectl create secret generic flux-system \
217
+ --namespace=flux-system \
218
+ --from-literal=username=x-access-token \
219
+ --from-literal=password="$GITHUB_TOKEN" \
220
+ --dry-run=client -o yaml \
221
+ | kubectl apply --server-side --force-conflicts \
222
+ --field-manager=github-actions-deploy -f -
223
+
224
+ - name: Apply root GitRepository + Kustomizations
225
+ # Template at carbon/k8s/flux/clusters/primary/vibecarbon.yaml
226
+ # (copied to k8s/flux/clusters/primary/vibecarbon.yaml in the
227
+ # customer repo by `vibecarbon create`). `{{GITHUB_OWNER}}` +
228
+ # `{{PROJECT_NAME}}` in that template are substituted at create
229
+ # time, so by the time Actions runs this file has the real URL.
230
+ run: |
231
+ kubectl apply --server-side --force-conflicts \
232
+ --field-manager=github-actions-deploy \
233
+ -f k8s/flux/clusters/primary/vibecarbon.yaml
234
+
235
+ trigger-reconcile:
236
+ # After Secrets are fresh + Flux is bootstrapped, nudge Flux to
237
+ # reconcile now rather than wait up to 10 min for its interval.
238
+ name: Trigger Flux reconcile
239
+ needs: [apply-secrets, bootstrap-flux]
240
+ runs-on: ubuntu-latest
241
+ environment: ${{ inputs.environment || 'prod' }}
242
+ env:
243
+ KUBECONFIG: ${{ github.workspace }}/kubeconfig
244
+ steps:
245
+ - name: Install kubectl
246
+ uses: azure/setup-kubectl@v4
247
+ with:
248
+ version: 'v1.32.0'
249
+
250
+ - name: Write kubeconfig from env secret
251
+ env:
252
+ KUBECONFIG_B64: ${{ secrets.KUBECONFIG_B64 }}
253
+ run: |
254
+ printf '%s' "$KUBECONFIG_B64" | base64 -d > "$KUBECONFIG"
255
+ chmod 600 "$KUBECONFIG"
256
+
257
+ - name: Annotate GitRepository + root Kustomizations
258
+ run: |
259
+ now="$(date -u +%FT%TZ)"
260
+ kubectl -n flux-system annotate --overwrite gitrepository/vibecarbon \
261
+ reconcile.fluxcd.io/requestedAt="$now" || true
262
+ for ks in vibecarbon-base vibecarbon-supabase vibecarbon-cert-manager-webhook-hetzner; do
263
+ kubectl -n flux-system annotate --overwrite kustomization/"$ks" \
264
+ reconcile.fluxcd.io/requestedAt="$now" || true
265
+ done
266
+
267
+ # State dump is inlined into the SAME shell as the wait commands so
268
+ # its output shows up in `gh run view <id> --log-failed`. A separate
269
+ # `if: failure()` job-step would run but wouldn't be in the failed-
270
+ # step logs (which is what waitForLatestWorkflowRun fetches), so
271
+ # debugging from the operator's machine would still be opaque.
272
+ - name: Wait for reconciliation (20 min budget)
273
+ run: |
274
+ set +e
275
+ kubectl -n flux-system wait --for=condition=Ready kustomization/vibecarbon-base --timeout=10m
276
+ BASE_RC=$?
277
+ kubectl -n flux-system wait --for=condition=Ready kustomization/vibecarbon-supabase --timeout=20m
278
+ SUPABASE_RC=$?
279
+ if [ $BASE_RC -ne 0 ] || [ $SUPABASE_RC -ne 0 ]; then
280
+ echo "=== RECONCILE FAILED — dumping Flux state ==="
281
+ echo "base=$BASE_RC supabase=$SUPABASE_RC"
282
+ echo
283
+ echo "=== Flux GitRepository ==="
284
+ kubectl -n flux-system describe gitrepository/vibecarbon || true
285
+ for ks in vibecarbon-base vibecarbon-supabase vibecarbon-cert-manager-webhook-hetzner; do
286
+ echo
287
+ echo "=== Kustomization: $ks ==="
288
+ kubectl -n flux-system describe kustomization/"$ks" || true
289
+ done
290
+ echo
291
+ echo "=== Flux events (last 80) ==="
292
+ kubectl -n flux-system get events --sort-by=.lastTimestamp 2>/dev/null | tail -80
293
+ echo
294
+ echo "=== vibecarbon namespace events (last 80) ==="
295
+ kubectl -n vibecarbon get events --sort-by=.lastTimestamp 2>/dev/null | tail -80
296
+ echo
297
+ echo "=== Non-ready pods (all namespaces) ==="
298
+ kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded 2>/dev/null
299
+ echo
300
+ echo "=== vibecarbon pods ==="
301
+ kubectl -n vibecarbon get pods -o wide 2>/dev/null
302
+ echo
303
+ echo "=== nodes (internal/external IPs) ==="
304
+ kubectl get nodes -o wide 2>/dev/null
305
+ echo
306
+ echo "=== kube-system pods (CoreDNS + CCM + CSI) ==="
307
+ kubectl -n kube-system get pods -o wide 2>/dev/null
308
+ echo
309
+ echo "=== CoreDNS ConfigMap (upstream resolvers) ==="
310
+ kubectl -n kube-system get configmap coredns -o yaml 2>/dev/null | sed -n '/data:/,/^[^ ]/p' | head -40
311
+ echo
312
+ echo "=== CoreDNS logs (last 30 lines per pod) ==="
313
+ for pod in $(kubectl -n kube-system get pods -l k8s-app=kube-dns -o name 2>/dev/null); do
314
+ echo "--- $pod ---"
315
+ kubectl -n kube-system logs "$pod" --tail=30 2>/dev/null
316
+ done
317
+ echo
318
+ echo "=== Hetzner CCM logs (last 40 lines) ==="
319
+ for pod in $(kubectl -n kube-system get pods -l app.kubernetes.io/name=hcloud-cloud-controller-manager -o name 2>/dev/null); do
320
+ echo "--- $pod ---"
321
+ kubectl -n kube-system logs "$pod" --tail=40 2>/dev/null
322
+ done
323
+ echo
324
+ echo "=== source-controller logs (last 40 lines) ==="
325
+ SC_POD=$(kubectl -n flux-system get pods -l app=source-controller -o name 2>/dev/null | head -1)
326
+ if [ -n "$SC_POD" ]; then
327
+ kubectl -n flux-system logs "$SC_POD" --tail=40 2>/dev/null || true
328
+ kubectl -n flux-system logs "$SC_POD" --previous --tail=40 2>/dev/null || true
329
+ fi
330
+ echo
331
+ echo "=== pod DNS probe (busybox in kube-system) ==="
332
+ # kube-system has relaxed PodSecurity (allows system pods).
333
+ # A plain pod can curl/nslookup without host namespaces —
334
+ # this is what Flux's source-controller is actually doing.
335
+ kubectl -n kube-system run dns-probe-$RANDOM --image=busybox:1.36 \
336
+ --restart=Never --rm -i --timeout=60s \
337
+ --overrides='{"spec":{"containers":[{"name":"probe","image":"busybox:1.36","command":["sh","-c","echo \"--- /etc/resolv.conf ---\"; cat /etc/resolv.conf; echo; echo \"--- nslookup github.com (via coredns) ---\"; nslookup github.com; echo; echo \"--- nslookup github.com @1.1.1.1 ---\"; nslookup github.com 1.1.1.1; echo; echo \"--- wget github.com ---\"; wget -qO- --timeout=5 https://github.com 2>&1 | head -5 || echo wget-failed"]}]}}' \
338
+ 2>&1 | head -50 || echo "(pod dns-probe failed)"
339
+ echo
340
+ echo "=== kube-proxy / node routing (kube-proxy pod logs) ==="
341
+ for pod in $(kubectl -n kube-system get pods -l k8s-app=kube-proxy -o name 2>/dev/null | head -1); do
342
+ echo "--- $pod ---"
343
+ kubectl -n kube-system logs "$pod" --tail=20 2>/dev/null
344
+ done
345
+ exit 1
346
+ fi
@@ -0,0 +1,81 @@
1
+ name: Build & Push
2
+
3
+ # Builds the app image on every push to main and every tagged release, then
4
+ # publishes to GitHub Container Registry. `vibecarbon deploy` pulls from this
5
+ # registry — no deploy logic lives in CI. This keeps CI fast and focused, and
6
+ # lets users deploy from their laptop (or a CI runner) against any cluster.
7
+
8
+ on:
9
+ push:
10
+ branches: [main]
11
+ tags: ['v*']
12
+ workflow_dispatch:
13
+
14
+ concurrency:
15
+ group: build-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ env:
19
+ REGISTRY: ghcr.io
20
+ IMAGE_NAME: ${{ github.repository }}
21
+
22
+ jobs:
23
+ test:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: pnpm/action-setup@v4
28
+ - uses: actions/setup-node@v4
29
+ with:
30
+ node-version: "22"
31
+ cache: "pnpm"
32
+ - run: pnpm install --frozen-lockfile
33
+ - run: pnpm run lint
34
+ - run: pnpm run typecheck
35
+ - run: pnpm test || true
36
+
37
+ build:
38
+ needs: [test]
39
+ runs-on: ubuntu-latest
40
+ permissions:
41
+ contents: read
42
+ packages: write
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+
46
+ # Native amd64 build on the GitHub runner — no QEMU. Hetzner cpx* + cx*
47
+ # server types are all amd64 (ARM cax* isn't supported by the deploy
48
+ # preflight in src/lib/deploy/k8s/k3s.js). Users who need arm64 for
49
+ # other cloud providers can add `platforms: linux/amd64,linux/arm64`
50
+ # back and re-enable docker/setup-qemu-action@v3 — cutting it here
51
+ # saves 2-4 min per CI build on the hot path.
52
+ - uses: docker/setup-buildx-action@v3
53
+
54
+ - uses: docker/login-action@v3
55
+ with:
56
+ registry: ${{ env.REGISTRY }}
57
+ username: ${{ github.actor }}
58
+ password: ${{ secrets.GITHUB_TOKEN }}
59
+
60
+ - id: meta
61
+ uses: docker/metadata-action@v5
62
+ with:
63
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
64
+ tags: |
65
+ type=ref,event=branch
66
+ type=ref,event=tag
67
+ type=sha,format=short,prefix=
68
+ type=raw,value=latest,enable={{is_default_branch}}
69
+
70
+ - uses: docker/build-push-action@v6
71
+ with:
72
+ context: .
73
+ platforms: linux/amd64
74
+ push: true
75
+ tags: ${{ steps.meta.outputs.tags }}
76
+ labels: ${{ steps.meta.outputs.labels }}
77
+ # GHA cache keyed on Dockerfile + lockfile. `mode=max` caches every
78
+ # layer including intermediate ones, so re-builds with an unchanged
79
+ # install step reuse the pnpm-store layer (biggest single layer).
80
+ cache-from: type=gha
81
+ cache-to: type=gha,mode=max
@@ -0,0 +1,74 @@
1
+ # {{PROJECT_NAME}} Development Rules
2
+
3
+ > **Read and follow all rules in AGENTS.md at the project root.** It contains mandatory security constraints for database, API, and infrastructure code.
4
+
5
+ ## Security Quick Reference
6
+
7
+ - MUST check auth on every endpoint: `if (!c.get('user')) return c.json({ error: 'Unauthorized' }, 401)`
8
+ - MUST validate all input with Zod schemas before processing
9
+ - MUST use `c.get('supabase')` for user-facing queries (respects RLS) — never `supabaseAdmin`
10
+ - MUST enable RLS on every new table with `(SELECT auth.uid())` in policies
11
+ - MUST use `createAuthClient()` for `signInWithPassword()` — never the `supabaseAdmin` singleton
12
+ - NEVER return raw database errors to clients
13
+ - NEVER expose `SUPABASE_SERVICE_ROLE_KEY` to the client
14
+
15
+ ## Stack
16
+ - Frontend: React 19, Vite, React Router, TanStack Query, Shadcn UI, Tailwind CSS v4
17
+ - Backend: Hono (Node.js), Pino logger, Zod validation
18
+ - Auth/Database: Self-hosted Supabase (PostgreSQL, Auth, REST API, Realtime, Storage)
19
+ - Tooling: Biome (lint/format), TypeScript, esbuild, Vitest
20
+
21
+ ## Commands
22
+ - `pnpm dev:start` - Full cold start (Docker + migrations + dev servers)
23
+ - `pnpm dev` - Start API and Vite dev servers
24
+ - `pnpm build` - Build client and server
25
+ - `pnpm lint` - Biome linting
26
+ - `pnpm typecheck` - TypeScript checking
27
+ - `pnpm test` - Run vitest tests
28
+ - `pnpm db:migrate` - Run SQL migrations
29
+
30
+ ## Path Aliases
31
+ - `@/*` → `src/client/*`
32
+ - `@shared/*` → `src/shared/*`
33
+ - Never use `@/shared/*` - it won't resolve
34
+
35
+ ## Key Patterns
36
+
37
+ ### Supabase
38
+ - Client-side (`src/client/lib/supabase.ts`): anon key, RLS enforced
39
+ - Server-side (`src/server/lib/supabase.ts`): service role key, bypasses RLS
40
+ - Auth is handled by Supabase - don't implement custom auth
41
+ - Use `(SELECT auth.uid())` in RLS policies (not `auth.uid()` directly)
42
+
43
+ ### API Routes
44
+ - Create in `src/server/routes/v1/`, mount in `src/server/index.ts`
45
+ - Validate input with Zod schemas
46
+ - Get user via `c.get('user')`, get Supabase client via `c.get('supabase')`
47
+
48
+ ### React Pages
49
+ - Protected pages: wrap with `<ProtectedRoute>` in `App.tsx`
50
+ - Admin pages: wrap with `<SuperAdminProtectedRoute>`
51
+ - App pages use `<SidebarProvider>` + `<AppSidebar />` + `<SidebarInset>` layout
52
+
53
+ ### UI
54
+ - Shadcn UI components in `src/client/components/ui/`
55
+ - Use `cn()` from `@/lib/utils` for conditional classes
56
+ - Use `toast` from `sonner` for notifications
57
+ - Button `asChild` with Link children: add `className="inline-flex items-center gap-2"` to the Link, `shrink-0` on icons
58
+
59
+ ### Data Fetching
60
+ - **NEVER use raw `fetch` in `useEffect`** — always use `useQuery` for reads, `useMutation` for writes. Raw fetch bypasses deduplication/caching and React StrictMode will double-fire it.
61
+ - **Gate authenticated queries** with `enabled: !!user` to prevent 401s for logged-out users
62
+ - **Use `getAuthHeaders()`** from `@/lib/api.ts` — never duplicate it locally, never send empty `Authorization` headers
63
+ - Shared hooks in `src/client/hooks/api/`
64
+ - Invalidate related queries after mutations: `queryClient.invalidateQueries({ queryKey: ['items'] })`
65
+
66
+ ### Database
67
+ - Migrations in `supabase/migrations/` with incrementing prefix
68
+ - Always enable RLS on new tables
69
+ - Helper functions: `get_user_org_ids()`, `get_user_admin_org_ids()`, `is_super_admin()`
70
+
71
+ ### Environment Variables
72
+ - Server env vars validated in `src/server/lib/env.ts`
73
+ - Client env vars must start with `VITE_`
74
+ - Feature flags use `isXConfigured()` pattern