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,75 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Delete Kubernetes manifests using Kustomize
4
+ *
5
+ * Usage:
6
+ * node scripts/k8s-delete.js # Delete local overlay
7
+ * node scripts/k8s-delete.js production-nbg1 # Delete specific overlay
8
+ * node scripts/k8s-delete.js --dry-run # Show what would be deleted
9
+ */
10
+
11
+ import { execSync } from 'node:child_process';
12
+ import { existsSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { loadManifest, getEnabledFeatures, parseArgs } from './lib/manifest.js';
15
+
16
+ const args = parseArgs(process.argv.slice(2));
17
+
18
+ if (args.help) {
19
+ console.log(`
20
+ Usage: node scripts/k8s-delete.js [overlay] [options]
21
+
22
+ Arguments:
23
+ overlay Kustomize overlay to delete (default: local)
24
+ Available: local, production-nbg1, production-hel1
25
+
26
+ Options:
27
+ --dry-run Show what would be deleted without executing
28
+ --verbose Verbose output
29
+ -h, --help Show this help message
30
+
31
+ Examples:
32
+ node scripts/k8s-delete.js # Delete local overlay
33
+ node scripts/k8s-delete.js production-nbg1 # Delete production overlay
34
+ pnpm k8s:delete # Via package.json script
35
+ `);
36
+ process.exit(0);
37
+ }
38
+
39
+ const manifest = loadManifest();
40
+ const overlay = args.positional[0] || 'local';
41
+ const overlayPath = join(process.cwd(), 'k8s', 'overlays', overlay);
42
+
43
+ if (!existsSync(overlayPath)) {
44
+ console.error(`Error: Overlay '${overlay}' not found at ${overlayPath}`);
45
+ console.error('Available overlays: local, production-nbg1, production-hel1');
46
+ process.exit(1);
47
+ }
48
+
49
+ const features = getEnabledFeatures(manifest);
50
+ if (args.verbose && features.length > 0) {
51
+ console.log('Enabled features:', features.join(', '));
52
+ }
53
+
54
+ // Build the kubectl delete command
55
+ const dryRunFlag = args.dryRun ? ' --dry-run=client' : '';
56
+ const cmd = `kubectl delete -k ${overlayPath}${dryRunFlag}`;
57
+
58
+ if (args.dryRun) {
59
+ console.log('Would run:', cmd);
60
+ console.log('\nDry run output:');
61
+ }
62
+
63
+ if (args.verbose) {
64
+ console.log('Running:', cmd);
65
+ }
66
+
67
+ try {
68
+ execSync(cmd, { stdio: 'inherit' });
69
+ } catch (error) {
70
+ // Exit code 1 from kubectl delete is common (resources not found), don't fail hard
71
+ if (args.verbose) {
72
+ console.log('kubectl delete exited with code:', error.status);
73
+ }
74
+ process.exit(error.status || 1);
75
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Shared manifest utilities for Vibecarbon scripts
3
+ * Reads .vibecarbon.json to determine which services are enabled
4
+ */
5
+
6
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+
9
+ /**
10
+ * Load the vibecarbon manifest from the current directory
11
+ * @returns {Object} The manifest object with version and services
12
+ */
13
+ export function loadManifest() {
14
+ const path = join(process.cwd(), '.vibecarbon.json');
15
+ if (!existsSync(path)) {
16
+ return { version: '1', services: {} };
17
+ }
18
+ return JSON.parse(readFileSync(path, 'utf-8'));
19
+ }
20
+
21
+ /**
22
+ * Get list of enabled service names from manifest
23
+ * Supports both "services" (new) and "features" (legacy) keys
24
+ * @param {Object} manifest - The vibecarbon manifest
25
+ * @returns {string[]} Array of service names
26
+ */
27
+ export function getEnabledServices(manifest) {
28
+ // Support both "services" (new) and "features" (legacy)
29
+ const services = manifest.services || manifest.features || {};
30
+ return Object.keys(services);
31
+ }
32
+
33
+ /**
34
+ * Discover all optional service compose files in the project directory
35
+ * Excludes core files (prod, override, ha-*) and service-specific overrides
36
+ * @returns {string[]} Array of optional service compose file names
37
+ */
38
+ export function discoverOptionalServices() {
39
+ const cwd = process.cwd();
40
+ const corePatterns = [
41
+ 'docker-compose.yml',
42
+ 'docker-compose.prod.yml',
43
+ 'docker-compose.override.yml',
44
+ /^docker-compose\.ha-.*\.yml$/,
45
+ /^docker-compose\..*\.override\.yml$/, // Service-specific overrides (handled separately)
46
+ /^docker-compose\..*\.prod\.yml$/, // Service-specific prod configs (use --prod flag)
47
+ ];
48
+
49
+ const files = readdirSync(cwd).filter((file) => {
50
+ if (!file.startsWith('docker-compose.') || !file.endsWith('.yml')) {
51
+ return false;
52
+ }
53
+ // Exclude core patterns
54
+ for (const pattern of corePatterns) {
55
+ if (typeof pattern === 'string' && file === pattern) return false;
56
+ if (pattern instanceof RegExp && pattern.test(file)) return false;
57
+ }
58
+ return true;
59
+ });
60
+
61
+ return files;
62
+ }
63
+
64
+ /**
65
+ * Get list of Docker Compose files to use based on enabled services
66
+ * @param {Object} manifest - The vibecarbon manifest
67
+ * @param {Object} options - Options for file selection
68
+ * @param {boolean} options.prod - Include production overlay
69
+ * @param {boolean} options.all - Include all optional services (ignores manifest)
70
+ * @returns {string[]} Array of compose file paths
71
+ */
72
+ export function getComposeFiles(manifest, options = {}) {
73
+ const files = ['docker-compose.yml'];
74
+ const cwd = process.cwd();
75
+
76
+ if (options.prod) {
77
+ files.push('docker-compose.prod.yml');
78
+ }
79
+
80
+ // Collect service files (either from --all discovery or manifest)
81
+ let serviceFiles = [];
82
+ if (options.all) {
83
+ serviceFiles = discoverOptionalServices();
84
+ } else {
85
+ for (const service of getEnabledServices(manifest)) {
86
+ const serviceFile = `docker-compose.${service}.yml`;
87
+ if (existsSync(join(cwd, serviceFile))) {
88
+ serviceFiles.push(serviceFile);
89
+ }
90
+ }
91
+ }
92
+
93
+ // Add each service file, followed by its environment-specific override
94
+ for (const serviceFile of serviceFiles) {
95
+ files.push(serviceFile);
96
+
97
+ if (options.prod) {
98
+ // In prod mode, check for service-specific prod file
99
+ // e.g., docker-compose.observability.yml -> docker-compose.observability.prod.yml
100
+ const prodFile = serviceFile.replace('.yml', '.prod.yml');
101
+ if (existsSync(join(cwd, prodFile))) {
102
+ files.push(prodFile);
103
+ }
104
+ } else {
105
+ // In dev mode, check for service-specific override file
106
+ // e.g., docker-compose.observability.yml -> docker-compose.observability.override.yml
107
+ const overrideFile = serviceFile.replace('.yml', '.override.yml');
108
+ if (existsSync(join(cwd, overrideFile))) {
109
+ files.push(overrideFile);
110
+ }
111
+ }
112
+ }
113
+
114
+ // In dev mode, include the main override file LAST so it can override
115
+ // service-specific compose files with dev passwords/config
116
+ if (!options.prod && existsSync(join(cwd, 'docker-compose.override.yml'))) {
117
+ files.push('docker-compose.override.yml');
118
+ }
119
+
120
+ // dev-init.js generates this file to mount the admin SQL with real credentials
121
+ // (only exists when developing the CLI itself, not in derived projects)
122
+ if (!options.prod && existsSync(join(cwd, 'docker-compose.dev-init.yml'))) {
123
+ files.push('docker-compose.dev-init.yml');
124
+ }
125
+
126
+ return files;
127
+ }
128
+
129
+ /**
130
+ * Check if a specific service is enabled
131
+ * @param {Object} manifest - The vibecarbon manifest
132
+ * @param {string} serviceName - Name of the service to check
133
+ * @returns {boolean} True if service is enabled
134
+ */
135
+ export function isServiceEnabled(manifest, serviceName) {
136
+ const services = manifest.services || manifest.features || {};
137
+ return Boolean(services[serviceName]);
138
+ }
139
+
140
+ /**
141
+ * Build docker compose command with appropriate files
142
+ * @param {string} action - The compose action (up, down, logs, etc.)
143
+ * @param {Object} manifest - The vibecarbon manifest
144
+ * @param {Object} options - Options for command building
145
+ * @param {boolean} options.prod - Use production config
146
+ * @param {boolean} options.all - Include all optional services
147
+ * @param {string[]} options.extraArgs - Additional arguments
148
+ * @returns {string} The full docker compose command
149
+ */
150
+ export function buildComposeCommand(action, manifest, options = {}) {
151
+ const files = getComposeFiles(manifest, { prod: options.prod, all: options.all });
152
+ const fileArgs = files.map((f) => `-f ${f}`).join(' ');
153
+ const extraArgs = options.extraArgs ? options.extraArgs.join(' ') : '';
154
+
155
+ return `docker compose ${fileArgs} ${action} ${extraArgs}`.trim();
156
+ }
157
+
158
+ /**
159
+ * Parse CLI arguments for common flags
160
+ * @param {string[]} args - CLI arguments
161
+ * @returns {Object} Parsed arguments
162
+ */
163
+ export function parseArgs(args) {
164
+ return {
165
+ prod: args.includes('--prod'),
166
+ all: args.includes('--all'),
167
+ dryRun: args.includes('--dry-run'),
168
+ verbose: args.includes('--verbose') || args.includes('-v'),
169
+ help: args.includes('--help') || args.includes('-h'),
170
+ positional: args.filter((arg) => !arg.startsWith('-')),
171
+ };
172
+ }
173
+
174
+ // Legacy aliases for backwards compatibility
175
+ export const getEnabledFeatures = getEnabledServices;
176
+ export const isFeatureEnabled = isServiceEnabled;
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env node
2
+ // Self-contained secret scanner for the vibecarbon-managed pre-commit
3
+ // hook. Mirrors the rules in src/lib/secret-scan.js (vibecarbon CLI)
4
+ // so the same allowlist + behaviour applies offline / without the CLI
5
+ // installed. Keep the rule set in sync — the test suite over there is
6
+ // the source of truth.
7
+ //
8
+ // Usage:
9
+ // node scripts/secret-scan.mjs <file> [<file>...]
10
+ // node scripts/secret-scan.mjs --stdin # reads file paths from stdin, one per line
11
+
12
+ import { existsSync, readFileSync, statSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+
15
+ const SECRET_RULES = [
16
+ { id: 'aws-access-key', description: 'AWS access key ID', pattern: /\bAKIA[0-9A-Z]{16}\b/g },
17
+ {
18
+ id: 'aws-secret-key',
19
+ description: 'AWS secret access key',
20
+ pattern:
21
+ /(?:aws[_-]?secret[_-]?access[_-]?key|secretAccessKey)\s*[:=]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/gi,
22
+ },
23
+ {
24
+ id: 'github-pat',
25
+ description: 'GitHub personal access token',
26
+ pattern: /\bgh[posru]_[A-Za-z0-9]{36,}\b/g,
27
+ },
28
+ {
29
+ id: 'stripe-live-key',
30
+ description: 'Stripe live secret key',
31
+ pattern: /\bsk_live_[A-Za-z0-9]{24,}\b/g,
32
+ },
33
+ {
34
+ id: 'stripe-test-key',
35
+ description: 'Stripe test secret key',
36
+ pattern: /\bsk_test_[A-Za-z0-9]{24,}\b/g,
37
+ },
38
+ { id: 'slack-token', description: 'Slack token', pattern: /\bxox[abprs]-[0-9A-Za-z-]{10,}\b/g },
39
+ { id: 'openai-key', description: 'OpenAI API key', pattern: /\bsk-[A-Za-z0-9]{20,}\b/g },
40
+ {
41
+ id: 'anthropic-key',
42
+ description: 'Anthropic API key',
43
+ pattern: /\bsk-ant-[A-Za-z0-9_-]{40,}\b/g,
44
+ },
45
+ {
46
+ id: 'google-service-account-json',
47
+ description: 'Google Cloud service account JSON',
48
+ pattern: /"type"\s*:\s*"service_account"/g,
49
+ context: /"private_key"\s*:\s*"-----BEGIN[ A-Z]+PRIVATE KEY-----/,
50
+ },
51
+ { id: 'google-api-key', description: 'Google API key', pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
52
+ {
53
+ id: 'private-key-block',
54
+ description: 'PEM-encoded private key',
55
+ pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/g,
56
+ },
57
+ {
58
+ id: 'hetzner-token',
59
+ description: 'Hetzner Cloud API token',
60
+ pattern: /['"]([A-Za-z0-9]{64})['"]/g,
61
+ context: /(?:hetzner|hcloud|HCLOUD_TOKEN|HETZNER_API_TOKEN)/i,
62
+ },
63
+ {
64
+ id: 'cloudflare-api-token',
65
+ description: 'Cloudflare scoped API token',
66
+ pattern: /\b[A-Za-z0-9_-]{40}\b/g,
67
+ context: /(?:cloudflare|cf[_-]?api[_-]?token|CLOUDFLARE_API_TOKEN)/i,
68
+ predicate: (m) => !/^[a-f0-9]+$/i.test(m),
69
+ },
70
+ {
71
+ id: 'supabase-service-role-jwt',
72
+ description: 'Supabase service-role JWT',
73
+ pattern: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
74
+ predicate: (m) => {
75
+ const parts = m.split('.');
76
+ if (parts.length !== 3) return false;
77
+ const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
78
+ const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
79
+ try {
80
+ const payload = JSON.parse(Buffer.from(padded, 'base64').toString('utf-8'));
81
+ return payload?.role === 'service_role' || payload?.role === 'admin';
82
+ } catch {
83
+ return false;
84
+ }
85
+ },
86
+ },
87
+ {
88
+ id: 'generic-secret-assignment',
89
+ description: 'High-entropy value assigned to a secret-named variable',
90
+ pattern:
91
+ /(?:password|passwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|credentials?)\s*[:=]\s*['"]([^'"\s]{20,})['"]/gi,
92
+ predicate: (_m, captured) => {
93
+ const value = captured?.[1];
94
+ if (!value) return false;
95
+ if (/^(?:REPLACE|TODO|CHANGE|YOUR|EXAMPLE|PLACEHOLDER|FIXME)/i.test(value)) return false;
96
+ if (/^(?:local|dev|test|sample|fake|demo)[-_]/i.test(value)) return false;
97
+ const freq = new Map();
98
+ for (const c of value) freq.set(c, (freq.get(c) ?? 0) + 1);
99
+ let h = 0;
100
+ for (const n of freq.values()) {
101
+ const p = n / value.length;
102
+ h -= p * Math.log2(p);
103
+ }
104
+ return h >= 3.5;
105
+ },
106
+ },
107
+ ];
108
+
109
+ const SKIP_EXTS = new Set([
110
+ '.png',
111
+ '.jpg',
112
+ '.jpeg',
113
+ '.gif',
114
+ '.webp',
115
+ '.ico',
116
+ '.pdf',
117
+ '.zip',
118
+ '.gz',
119
+ '.tar',
120
+ '.tgz',
121
+ '.7z',
122
+ '.rar',
123
+ '.mp3',
124
+ '.mp4',
125
+ '.mov',
126
+ '.avi',
127
+ '.webm',
128
+ '.woff',
129
+ '.woff2',
130
+ '.ttf',
131
+ '.otf',
132
+ '.eot',
133
+ '.so',
134
+ '.dll',
135
+ '.dylib',
136
+ '.bin',
137
+ '.wasm',
138
+ ]);
139
+ const MAX_FILE_BYTES = 1_048_576;
140
+
141
+ function loadAllowlist() {
142
+ try {
143
+ return readFileSync(join(process.cwd(), '.vibecarbonignore'), 'utf-8')
144
+ .split('\n')
145
+ .map((l) => l.trim())
146
+ .filter((l) => l && !l.startsWith('#'));
147
+ } catch {
148
+ return [];
149
+ }
150
+ }
151
+
152
+ function matchesAllowlist(value, allowlist) {
153
+ for (const entry of allowlist) {
154
+ if (entry.startsWith('regex:')) {
155
+ try {
156
+ if (new RegExp(entry.slice(6)).test(value)) return true;
157
+ } catch {}
158
+ } else if (value.includes(entry)) {
159
+ return true;
160
+ }
161
+ }
162
+ return false;
163
+ }
164
+
165
+ function locate(content, offset) {
166
+ let line = 1;
167
+ let lineStart = 0;
168
+ for (let i = 0; i < offset; i++) {
169
+ if (content.charCodeAt(i) === 10) {
170
+ line++;
171
+ lineStart = i + 1;
172
+ }
173
+ }
174
+ return { line, column: offset - lineStart + 1 };
175
+ }
176
+
177
+ function scanContent(content, allowlist) {
178
+ if (!content) return [];
179
+ const findings = [];
180
+ for (const rule of SECRET_RULES) {
181
+ rule.pattern.lastIndex = 0;
182
+ let m = rule.pattern.exec(content);
183
+ while (m !== null) {
184
+ const [whole] = m;
185
+ if (rule.context) {
186
+ const start = Math.max(0, m.index - 128);
187
+ const end = Math.min(content.length, m.index + whole.length + 128);
188
+ if (!rule.context.test(content.slice(start, end))) {
189
+ m = rule.pattern.exec(content);
190
+ continue;
191
+ }
192
+ }
193
+ if (rule.predicate && !rule.predicate(whole, m)) {
194
+ m = rule.pattern.exec(content);
195
+ continue;
196
+ }
197
+ if (matchesAllowlist(whole, allowlist)) {
198
+ m = rule.pattern.exec(content);
199
+ continue;
200
+ }
201
+ const { line, column } = locate(content, m.index);
202
+ findings.push({
203
+ ruleId: rule.id,
204
+ description: rule.description,
205
+ match: whole.length > 64 ? `${whole.slice(0, 32)}…${whole.slice(-16)}` : whole,
206
+ line,
207
+ column,
208
+ });
209
+ m = rule.pattern.exec(content);
210
+ }
211
+ }
212
+ return findings;
213
+ }
214
+
215
+ async function readStdinList() {
216
+ const chunks = [];
217
+ for await (const chunk of process.stdin) chunks.push(chunk);
218
+ return Buffer.concat(chunks)
219
+ .toString('utf-8')
220
+ .split('\n')
221
+ .map((l) => l.trim())
222
+ .filter(Boolean);
223
+ }
224
+
225
+ async function main() {
226
+ const argv = process.argv.slice(2);
227
+ let files = [];
228
+ if (argv.includes('--stdin')) {
229
+ files = await readStdinList();
230
+ } else {
231
+ files = argv;
232
+ }
233
+ if (files.length === 0) process.exit(0);
234
+
235
+ const allowlist = loadAllowlist();
236
+ const failures = [];
237
+ for (const file of files) {
238
+ if (!existsSync(file)) continue;
239
+ const ext = file.includes('.') ? file.slice(file.lastIndexOf('.')).toLowerCase() : '';
240
+ if (SKIP_EXTS.has(ext)) continue;
241
+ let stat;
242
+ try {
243
+ stat = statSync(file);
244
+ } catch {
245
+ continue;
246
+ }
247
+ if (!stat.isFile() || stat.size > MAX_FILE_BYTES) continue;
248
+ let content;
249
+ try {
250
+ content = readFileSync(file, 'utf-8');
251
+ } catch {
252
+ continue;
253
+ }
254
+ const findings = scanContent(content, allowlist);
255
+ for (const f of findings) failures.push({ file, ...f });
256
+ }
257
+
258
+ if (failures.length === 0) process.exit(0);
259
+ process.stderr.write(
260
+ `\n[X] Refusing commit: ${failures.length} potential secret${failures.length === 1 ? '' : 's'} detected:\n\n`,
261
+ );
262
+ for (const f of failures) {
263
+ process.stderr.write(` ${f.file}:${f.line}:${f.column} [${f.ruleId}] ${f.description}\n`);
264
+ process.stderr.write(` -> ${f.match}\n`);
265
+ }
266
+ process.stderr.write(
267
+ '\nIf a finding is a false positive, add it to .vibecarbonignore at the repo root:\n',
268
+ );
269
+ process.stderr.write(' - one entry per line\n');
270
+ process.stderr.write(' - literal substring match, OR `regex:<pattern>` for a regex\n');
271
+ process.stderr.write(' - `#` comments are ignored\n\n');
272
+ process.exit(1);
273
+ }
274
+
275
+ main().catch((err) => {
276
+ process.stderr.write(`secret-scan: ${err.message}\n`);
277
+ process.exit(2);
278
+ });
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Validate that all template files have corresponding dev files
4
+ # Run in CI to catch missing dev configs
5
+ #
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ CARBON_DIR="$(dirname "$SCRIPT_DIR")"
10
+
11
+ cd "$CARBON_DIR"
12
+
13
+ errors=0
14
+
15
+ # Known template/dev file pairs that must exist
16
+ declare -a REQUIRED_PAIRS=(
17
+ # Grafana dashboards
18
+ "volumes/grafana/provisioning/dashboards/dashboards.yml:volumes/grafana/provisioning/dashboards/dashboards.dev.yml"
19
+ # Database initialization (only super-admin needs dev variant — roles/services use runtime env vars)
20
+ "volumes/db/super-admin.sql:volumes/db/super-admin.dev.sql"
21
+ )
22
+
23
+ # Directories where each template file needs a dev counterpart
24
+ declare -a REQUIRED_DIR_PAIRS=(
25
+ "volumes/grafana/dashboards:volumes/grafana/dashboards.dev"
26
+ )
27
+
28
+ echo "Validating dev config files..."
29
+ echo ""
30
+
31
+ # Check individual file pairs
32
+ echo "Required file pairs:"
33
+ for pair in "${REQUIRED_PAIRS[@]}"; do
34
+ template="${pair%%:*}"
35
+ dev_file="${pair##*:}"
36
+
37
+ if [[ ! -f "$template" ]]; then
38
+ echo " SKIP: $template (template not found)"
39
+ continue
40
+ fi
41
+
42
+ if [[ -f "$dev_file" ]]; then
43
+ echo " OK: $dev_file"
44
+ else
45
+ echo " MISSING: $dev_file"
46
+ ((errors++))
47
+ fi
48
+ done
49
+
50
+ echo ""
51
+
52
+ # Check directory pairs
53
+ echo "Required directory pairs:"
54
+ for dir_pair in "${REQUIRED_DIR_PAIRS[@]}"; do
55
+ template_dir="${dir_pair%%:*}"
56
+ dev_dir="${dir_pair##*:}"
57
+
58
+ if [[ ! -d "$template_dir" ]]; then
59
+ echo " SKIP: $template_dir (template dir not found)"
60
+ continue
61
+ fi
62
+
63
+ for template_file in "$template_dir"/*; do
64
+ if [[ -f "$template_file" ]]; then
65
+ filename=$(basename "$template_file")
66
+ dev_file="$dev_dir/$filename"
67
+
68
+ if [[ -f "$dev_file" ]]; then
69
+ echo " OK: $dev_file"
70
+ else
71
+ echo " MISSING: $dev_file"
72
+ ((errors++))
73
+ fi
74
+ fi
75
+ done
76
+ done
77
+
78
+ echo ""
79
+
80
+ # Check for template placeholders in mounted files
81
+ echo "Checking for unsubstituted placeholders in dev files..."
82
+ dev_files_with_placeholders=$(find volumes -name "*.dev.*" -o -name "*.dev" -type f 2>/dev/null | xargs grep -l '{{[A-Z_]*}}' 2>/dev/null || true)
83
+
84
+ if [[ -n "$dev_files_with_placeholders" ]]; then
85
+ echo " WARNING: Dev files still contain template placeholders:"
86
+ echo "$dev_files_with_placeholders" | while read -r f; do
87
+ echo " $f"
88
+ done
89
+ ((errors++))
90
+ fi
91
+
92
+ echo ""
93
+
94
+ if [[ $errors -gt 0 ]]; then
95
+ echo "FAILED: $errors issue(s) found"
96
+ echo "Run ./scripts/generate-dev-configs.sh to fix"
97
+ exit 1
98
+ else
99
+ echo "PASSED: All dev configs present"
100
+ exit 0
101
+ fi