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.
- package/LICENSE +663 -0
- package/README.md +188 -0
- package/carbon/.claude/hooks/task-completed-gate.sh +19 -0
- package/carbon/.claude/hooks/teammate-idle-gate.sh +29 -0
- package/carbon/.claude/settings.json +29 -0
- package/carbon/.cursor/rules/vibecarbon.mdc +6 -0
- package/carbon/.dockerignore +57 -0
- package/carbon/.env.example +219 -0
- package/carbon/.github/copilot-instructions.md +3 -0
- package/carbon/.github/workflows/deploy.yml +346 -0
- package/carbon/.github/workflows/vibecarbon-build.yml +81 -0
- package/carbon/.windsurfrules +74 -0
- package/carbon/AGENTS.md +422 -0
- package/carbon/CLAUDE.md +3 -0
- package/carbon/DEVELOPMENT.md +187 -0
- package/carbon/Dockerfile +98 -0
- package/carbon/LICENSE +21 -0
- package/carbon/PRODUCTION.md +364 -0
- package/carbon/README.md +193 -0
- package/carbon/TESTING.md +138 -0
- package/carbon/backup/Dockerfile +75 -0
- package/carbon/backup/backup.sh +95 -0
- package/carbon/backup/compose-backup.sh +140 -0
- package/carbon/biome.json +83 -0
- package/carbon/cloud-init/docker-ce-setup.yaml +55 -0
- package/carbon/cloud-init/k3s/master-init.sh +215 -0
- package/carbon/cloud-init/k3s/supabase-init.sh +167 -0
- package/carbon/cloud-init/k3s/worker-init.sh +147 -0
- package/carbon/components.json +24 -0
- package/carbon/content/blog/authentication-guide.mdx +34 -0
- package/carbon/content/blog/getting-started.mdx +41 -0
- package/carbon/content/changelog/v0-1-0.mdx +40 -0
- package/carbon/content/docs/analytics.mdx +90 -0
- package/carbon/content/docs/authentication.mdx +231 -0
- package/carbon/content/docs/background-jobs.mdx +116 -0
- package/carbon/content/docs/cli.mdx +630 -0
- package/carbon/content/docs/database.mdx +236 -0
- package/carbon/content/docs/deployment.mdx +227 -0
- package/carbon/content/docs/development.mdx +238 -0
- package/carbon/content/docs/environments.mdx +84 -0
- package/carbon/content/docs/getting-started.mdx +112 -0
- package/carbon/content/docs/legal/privacy-policy.mdx +89 -0
- package/carbon/content/docs/legal/terms-of-service.mdx +99 -0
- package/carbon/content/docs/optional-services.mdx +160 -0
- package/carbon/db/Dockerfile +23 -0
- package/carbon/docker-compose.dev-init.yml +5 -0
- package/carbon/docker-compose.metabase.override.yml +14 -0
- package/carbon/docker-compose.metabase.prod.yml +28 -0
- package/carbon/docker-compose.metabase.yml +84 -0
- package/carbon/docker-compose.n8n.override.yml +14 -0
- package/carbon/docker-compose.n8n.prod.yml +31 -0
- package/carbon/docker-compose.n8n.yml +97 -0
- package/carbon/docker-compose.observability.override.yml +18 -0
- package/carbon/docker-compose.observability.prod.yml +28 -0
- package/carbon/docker-compose.observability.yml +125 -0
- package/carbon/docker-compose.override.yml +28 -0
- package/carbon/docker-compose.prod.yml +294 -0
- package/carbon/docker-compose.yml +508 -0
- package/carbon/docker-entrypoint.sh +12 -0
- package/carbon/ha/activate-standby.sh +52 -0
- package/carbon/ha/primary-init.sql +36 -0
- package/carbon/ha/standby-init.sh +74 -0
- package/carbon/k8s/LICENSE +99 -0
- package/carbon/k8s/README.md +272 -0
- package/carbon/k8s/base/app/deployment.yaml +130 -0
- package/carbon/k8s/base/app/hpa.yaml +44 -0
- package/carbon/k8s/base/app/kustomization.yaml +10 -0
- package/carbon/k8s/base/app/network-policy.yaml +124 -0
- package/carbon/k8s/base/app/pdb.yaml +14 -0
- package/carbon/k8s/base/app/rbac.yaml +36 -0
- package/carbon/k8s/base/app/service.yaml +16 -0
- package/carbon/k8s/base/backup/cronjob.yaml +120 -0
- package/carbon/k8s/base/backup/kustomization.yaml +7 -0
- package/carbon/k8s/base/backup/network-policy.yaml +31 -0
- package/carbon/k8s/base/backup/rbac.yaml +7 -0
- package/carbon/k8s/base/cluster-autoscaler/deployment.yaml +103 -0
- package/carbon/k8s/base/cluster-autoscaler/kustomization.yaml +17 -0
- package/carbon/k8s/base/cluster-autoscaler/rbac.yaml +109 -0
- package/carbon/k8s/base/config/configmap.yaml +48 -0
- package/carbon/k8s/base/config/kustomization.yaml +8 -0
- package/carbon/k8s/base/hetzner-ccm/kustomization.yaml +9 -0
- package/carbon/k8s/base/hetzner-csi/kustomization.yaml +9 -0
- package/carbon/k8s/base/kustomization.yaml +43 -0
- package/carbon/k8s/base/namespace.yaml +7 -0
- package/carbon/k8s/base/network-policies.yaml +95 -0
- package/carbon/k8s/base/registry/kustomization.yaml +5 -0
- package/carbon/k8s/base/registry/local-registry.yaml +193 -0
- package/carbon/k8s/base/traefik/certificate.yaml +21 -0
- package/carbon/k8s/base/traefik/configmap.yaml +13 -0
- package/carbon/k8s/base/traefik/deployment.yaml +165 -0
- package/carbon/k8s/base/traefik/ingressroute.yaml +141 -0
- package/carbon/k8s/base/traefik/kustomization.yaml +11 -0
- package/carbon/k8s/base/traefik/middleware.yaml +109 -0
- package/carbon/k8s/base/traefik/network-policy.yaml +92 -0
- package/carbon/k8s/base/traefik/service.yaml +38 -0
- package/carbon/k8s/flux/README.md +49 -0
- package/carbon/k8s/flux/clusters/primary/vibecarbon.yaml +128 -0
- package/carbon/k8s/flux/clusters/standby/vibecarbon.yaml +83 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-release.yaml +38 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-repository.yaml +16 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/kustomization.yaml +10 -0
- package/carbon/k8s/gitops/supabase/helm-release.yaml +66 -0
- package/carbon/k8s/gitops/supabase/helm-repository.yaml +18 -0
- package/carbon/k8s/gitops/supabase/kustomization.yaml +33 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-cloudflare.yaml +51 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-hetzner.yaml +59 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-manual.yaml +43 -0
- package/carbon/k8s/infra/cert-manager-resources/kustomization.yaml +15 -0
- package/carbon/k8s/infra/kustomization.yaml +21 -0
- package/carbon/k8s/infra/traefik-crds/kustomization.yaml +10 -0
- package/carbon/k8s/overlays/local/configmap.yaml +23 -0
- package/carbon/k8s/overlays/local/ingressroute-studio.yaml +104 -0
- package/carbon/k8s/overlays/local/kustomization.yaml +215 -0
- package/carbon/k8s/overlays/local/namespace.yaml +8 -0
- package/carbon/k8s/overlays/local/secrets.yaml +32 -0
- package/carbon/k8s/overlays/production/kustomization.yaml +34 -0
- package/carbon/k8s/test-local.sh +318 -0
- package/carbon/k8s/values/supabase.values.yaml +133 -0
- package/carbon/package.json +154 -0
- package/carbon/runtime.Dockerfile +17 -0
- package/carbon/scripts/_dev-jwt.mjs +45 -0
- package/carbon/scripts/check-rls.ts +53 -0
- package/carbon/scripts/dev-init.js +191 -0
- package/carbon/scripts/dev.js +191 -0
- package/carbon/scripts/docker-down.js +63 -0
- package/carbon/scripts/docker-logs.js +59 -0
- package/carbon/scripts/docker-up.js +222 -0
- package/carbon/scripts/generate-dev-configs.sh +131 -0
- package/carbon/scripts/generate-rss.ts +116 -0
- package/carbon/scripts/generate-sitemap.ts +102 -0
- package/carbon/scripts/k8s-apply.js +71 -0
- package/carbon/scripts/k8s-delete.js +75 -0
- package/carbon/scripts/lib/manifest.js +176 -0
- package/carbon/scripts/secret-scan.mjs +278 -0
- package/carbon/scripts/validate-dev-configs.sh +101 -0
- package/carbon/src/client/App.tsx +202 -0
- package/carbon/src/client/assets/hyperformant-dark.svg +29 -0
- package/carbon/src/client/assets/hyperformant-light.svg +29 -0
- package/carbon/src/client/assets/logos/aider.svg +1 -0
- package/carbon/src/client/assets/logos/antigravity.svg +1 -0
- package/carbon/src/client/assets/logos/bolt.svg +1 -0
- package/carbon/src/client/assets/logos/claude-code.svg +1 -0
- package/carbon/src/client/assets/logos/copilot.svg +1 -0
- package/carbon/src/client/assets/logos/cursor.svg +1 -0
- package/carbon/src/client/assets/logos/gemini-cli.svg +1 -0
- package/carbon/src/client/assets/logos/lovable.svg +1 -0
- package/carbon/src/client/assets/logos/openai-codex.svg +1 -0
- package/carbon/src/client/assets/logos/v0.svg +1 -0
- package/carbon/src/client/assets/logos/windsurf.svg +1 -0
- package/carbon/src/client/assets/vibecarbon-icon.svg +19 -0
- package/carbon/src/client/assets/vibecarbon-logo-dark.svg +29 -0
- package/carbon/src/client/assets/vibecarbon-logo-light.svg +29 -0
- package/carbon/src/client/components/AIIntegrationSection.tsx +120 -0
- package/carbon/src/client/components/AppSidebar.tsx +760 -0
- package/carbon/src/client/components/ArchitectureSection.tsx +46 -0
- package/carbon/src/client/components/CTAFooter.tsx +59 -0
- package/carbon/src/client/components/ComparisonSection.tsx +132 -0
- package/carbon/src/client/components/ContentPanel.tsx +66 -0
- package/carbon/src/client/components/ContentSkeleton.tsx +21 -0
- package/carbon/src/client/components/ErrorBoundary.tsx +46 -0
- package/carbon/src/client/components/FAQSection.tsx +76 -0
- package/carbon/src/client/components/FileUpload.tsx +210 -0
- package/carbon/src/client/components/HeaderActions.tsx +17 -0
- package/carbon/src/client/components/Hero.tsx +608 -0
- package/carbon/src/client/components/ImpersonationBanner.tsx +31 -0
- package/carbon/src/client/components/LanguageSwitcher.tsx +67 -0
- package/carbon/src/client/components/Logo.tsx +87 -0
- package/carbon/src/client/components/LogoStrip.tsx +76 -0
- package/carbon/src/client/components/MetricsStrip.tsx +48 -0
- package/carbon/src/client/components/Nav.tsx +195 -0
- package/carbon/src/client/components/NewsletterSignup.tsx +147 -0
- package/carbon/src/client/components/NotificationDrawer.tsx +171 -0
- package/carbon/src/client/components/PageHeader.tsx +24 -0
- package/carbon/src/client/components/PlanGate.tsx +36 -0
- package/carbon/src/client/components/PricingSection.tsx +371 -0
- package/carbon/src/client/components/SEO.tsx +34 -0
- package/carbon/src/client/components/SmoothScroll.tsx +45 -0
- package/carbon/src/client/components/TechStackSection.tsx +165 -0
- package/carbon/src/client/components/WorkflowSection.tsx +604 -0
- package/carbon/src/client/components/admin/DockerLogs.tsx +276 -0
- package/carbon/src/client/components/admin/PerformanceMetrics.tsx +230 -0
- package/carbon/src/client/components/admin/ServiceCard.tsx +45 -0
- package/carbon/src/client/components/admin/ServicesStatus.tsx +296 -0
- package/carbon/src/client/components/architecture/ArchitectureDiagram.tsx +413 -0
- package/carbon/src/client/components/auth/AuthProvider.tsx +466 -0
- package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +35 -0
- package/carbon/src/client/components/effects/FresnelEdge.tsx +49 -0
- package/carbon/src/client/components/effects/GlowTracker.tsx +46 -0
- package/carbon/src/client/components/effects/SparkBurst.tsx +145 -0
- package/carbon/src/client/components/effects/VGlowEffect.tsx +83 -0
- package/carbon/src/client/components/effects/index.ts +2 -0
- package/carbon/src/client/components/layouts/SidebarLayout.tsx +20 -0
- package/carbon/src/client/components/scroll/ScrollSection.tsx +76 -0
- package/carbon/src/client/components/scroll/ScrollytellingProvider.tsx +81 -0
- package/carbon/src/client/components/scroll/index.ts +2 -0
- package/carbon/src/client/components/ui/accordion.tsx +71 -0
- package/carbon/src/client/components/ui/alert-dialog.tsx +162 -0
- package/carbon/src/client/components/ui/alert.tsx +73 -0
- package/carbon/src/client/components/ui/aspect-ratio.tsx +22 -0
- package/carbon/src/client/components/ui/avatar.tsx +91 -0
- package/carbon/src/client/components/ui/badge.tsx +50 -0
- package/carbon/src/client/components/ui/breadcrumb.tsx +100 -0
- package/carbon/src/client/components/ui/button-group.tsx +78 -0
- package/carbon/src/client/components/ui/button.tsx +120 -0
- package/carbon/src/client/components/ui/calendar.tsx +182 -0
- package/carbon/src/client/components/ui/card.tsx +85 -0
- package/carbon/src/client/components/ui/carousel.tsx +227 -0
- package/carbon/src/client/components/ui/chart.tsx +357 -0
- package/carbon/src/client/components/ui/checkbox.tsx +27 -0
- package/carbon/src/client/components/ui/collapsible.tsx +15 -0
- package/carbon/src/client/components/ui/command.tsx +178 -0
- package/carbon/src/client/components/ui/context-menu.tsx +233 -0
- package/carbon/src/client/components/ui/dialog.tsx +132 -0
- package/carbon/src/client/components/ui/drawer.tsx +118 -0
- package/carbon/src/client/components/ui/dropdown-menu.tsx +242 -0
- package/carbon/src/client/components/ui/empty.tsx +94 -0
- package/carbon/src/client/components/ui/field.tsx +226 -0
- package/carbon/src/client/components/ui/hover-card.tsx +44 -0
- package/carbon/src/client/components/ui/input-group.tsx +146 -0
- package/carbon/src/client/components/ui/input-otp.tsx +83 -0
- package/carbon/src/client/components/ui/input.tsx +20 -0
- package/carbon/src/client/components/ui/item.tsx +188 -0
- package/carbon/src/client/components/ui/kbd.tsx +26 -0
- package/carbon/src/client/components/ui/label.tsx +21 -0
- package/carbon/src/client/components/ui/menubar.tsx +250 -0
- package/carbon/src/client/components/ui/navigation-menu.tsx +155 -0
- package/carbon/src/client/components/ui/pagination.tsx +102 -0
- package/carbon/src/client/components/ui/popover.tsx +75 -0
- package/carbon/src/client/components/ui/progress.tsx +66 -0
- package/carbon/src/client/components/ui/radio-group.tsx +36 -0
- package/carbon/src/client/components/ui/resizable.tsx +46 -0
- package/carbon/src/client/components/ui/scroll-area.tsx +48 -0
- package/carbon/src/client/components/ui/select.tsx +186 -0
- package/carbon/src/client/components/ui/separator.tsx +21 -0
- package/carbon/src/client/components/ui/sheet.tsx +124 -0
- package/carbon/src/client/components/ui/sidebar.tsx +702 -0
- package/carbon/src/client/components/ui/skeleton.tsx +13 -0
- package/carbon/src/client/components/ui/slider.tsx +57 -0
- package/carbon/src/client/components/ui/sonner.tsx +45 -0
- package/carbon/src/client/components/ui/spinner.tsx +15 -0
- package/carbon/src/client/components/ui/switch.tsx +30 -0
- package/carbon/src/client/components/ui/table.tsx +89 -0
- package/carbon/src/client/components/ui/tabs.tsx +73 -0
- package/carbon/src/client/components/ui/textarea.tsx +18 -0
- package/carbon/src/client/components/ui/toggle-group.tsx +87 -0
- package/carbon/src/client/components/ui/toggle.tsx +44 -0
- package/carbon/src/client/components/ui/tooltip.tsx +56 -0
- package/carbon/src/client/hooks/api/index.ts +14 -0
- package/carbon/src/client/hooks/api/useAuthSettings.ts +80 -0
- package/carbon/src/client/hooks/api/useSubscription.ts +87 -0
- package/carbon/src/client/hooks/use-mobile.ts +21 -0
- package/carbon/src/client/hooks/useMousePosition.ts +91 -0
- package/carbon/src/client/hooks/useNotifications.tsx +124 -0
- package/carbon/src/client/hooks/useOrganizationMembers.ts +127 -0
- package/carbon/src/client/hooks/useOrganizations.tsx +230 -0
- package/carbon/src/client/hooks/usePackageManager.ts +69 -0
- package/carbon/src/client/hooks/useReducedMotion.ts +20 -0
- package/carbon/src/client/hooks/useRunningServices.ts +114 -0
- package/carbon/src/client/hooks/useScrollProgress.ts +56 -0
- package/carbon/src/client/index.css +467 -0
- package/carbon/src/client/index.html +56 -0
- package/carbon/src/client/lib/admin-services.ts +151 -0
- package/carbon/src/client/lib/api.ts +32 -0
- package/carbon/src/client/lib/blog.ts +35 -0
- package/carbon/src/client/lib/changelog.ts +33 -0
- package/carbon/src/client/lib/docs-search.ts +101 -0
- package/carbon/src/client/lib/docs.ts +37 -0
- package/carbon/src/client/lib/i18n.ts +32 -0
- package/carbon/src/client/lib/supabase.ts +72 -0
- package/carbon/src/client/lib/tailwind-colors.ts +357 -0
- package/carbon/src/client/lib/theme.ts +117 -0
- package/carbon/src/client/lib/utils.ts +22 -0
- package/carbon/src/client/locales/de.json +529 -0
- package/carbon/src/client/locales/en.json +461 -0
- package/carbon/src/client/locales/es.json +529 -0
- package/carbon/src/client/locales/fr.json +529 -0
- package/carbon/src/client/locales/pt.json +529 -0
- package/carbon/src/client/main.tsx +56 -0
- package/carbon/src/client/mdx.d.ts +13 -0
- package/carbon/src/client/pages/ApiDocs.tsx +76 -0
- package/carbon/src/client/pages/AuthCallback.tsx +34 -0
- package/carbon/src/client/pages/Blog.tsx +167 -0
- package/carbon/src/client/pages/Changelog.tsx +171 -0
- package/carbon/src/client/pages/Charts.tsx +388 -0
- package/carbon/src/client/pages/Checkout.tsx +227 -0
- package/carbon/src/client/pages/Contact.tsx +174 -0
- package/carbon/src/client/pages/Dashboard.tsx +368 -0
- package/carbon/src/client/pages/Docs.tsx +372 -0
- package/carbon/src/client/pages/ForgotPassword.tsx +111 -0
- package/carbon/src/client/pages/Home.tsx +187 -0
- package/carbon/src/client/pages/Legal.tsx +100 -0
- package/carbon/src/client/pages/Login.tsx +408 -0
- package/carbon/src/client/pages/MFAVerify.tsx +156 -0
- package/carbon/src/client/pages/NotFound.tsx +21 -0
- package/carbon/src/client/pages/Onboarding.tsx +246 -0
- package/carbon/src/client/pages/ResetPassword.tsx +200 -0
- package/carbon/src/client/pages/UIComponents.tsx +390 -0
- package/carbon/src/client/pages/admin/ContactSubmissions.tsx +220 -0
- package/carbon/src/client/pages/admin/Dashboard.tsx +24 -0
- package/carbon/src/client/pages/admin/Infrastructure.tsx +257 -0
- package/carbon/src/client/pages/admin/Jobs.tsx +225 -0
- package/carbon/src/client/pages/admin/Logs.tsx +18 -0
- package/carbon/src/client/pages/admin/Newsletter.tsx +300 -0
- package/carbon/src/client/pages/admin/Notifications.tsx +603 -0
- package/carbon/src/client/pages/admin/Organizations.tsx +306 -0
- package/carbon/src/client/pages/admin/Settings.tsx +314 -0
- package/carbon/src/client/pages/admin/Theme.tsx +465 -0
- package/carbon/src/client/pages/admin/Users.tsx +365 -0
- package/carbon/src/client/pages/api-docs.css +156 -0
- package/carbon/src/client/pages/organizations/Details.tsx +200 -0
- package/carbon/src/client/pages/organizations/Members.tsx +402 -0
- package/carbon/src/client/pages/settings/Billing.tsx +473 -0
- package/carbon/src/client/pages/settings/Profile.tsx +160 -0
- package/carbon/src/client/pages/settings/Security.tsx +341 -0
- package/carbon/src/client/public/favicon.svg +19 -0
- package/carbon/src/client/public/robots.txt +8 -0
- package/carbon/src/server/billing/index.ts +104 -0
- package/carbon/src/server/billing/provider.ts +81 -0
- package/carbon/src/server/billing/providers/paddle.ts +314 -0
- package/carbon/src/server/billing/providers/polar.ts +325 -0
- package/carbon/src/server/billing/providers/stripe.ts +233 -0
- package/carbon/src/server/emails/templates.ts +116 -0
- package/carbon/src/server/index.ts +554 -0
- package/carbon/src/server/lib/auth.ts +6 -0
- package/carbon/src/server/lib/email.ts +64 -0
- package/carbon/src/server/lib/env.ts +112 -0
- package/carbon/src/server/lib/errors.ts +21 -0
- package/carbon/src/server/lib/logger.ts +17 -0
- package/carbon/src/server/lib/rate-limiter.ts +288 -0
- package/carbon/src/server/lib/request.ts +34 -0
- package/carbon/src/server/lib/stripe.ts +42 -0
- package/carbon/src/server/lib/supabase.ts +65 -0
- package/carbon/src/server/middleware/requirePlan.ts +80 -0
- package/carbon/src/server/routes/_internal/services-status.ts +958 -0
- package/carbon/src/server/routes/_internal/verify-role.ts +185 -0
- package/carbon/src/server/routes/health.ts +48 -0
- package/carbon/src/server/routes/v1/admin/contact.ts +128 -0
- package/carbon/src/server/routes/v1/admin/jobs.ts +171 -0
- package/carbon/src/server/routes/v1/admin/newsletter.ts +237 -0
- package/carbon/src/server/routes/v1/auth.ts +390 -0
- package/carbon/src/server/routes/v1/billing.ts +718 -0
- package/carbon/src/server/routes/v1/contact.ts +93 -0
- package/carbon/src/server/routes/v1/index.ts +1333 -0
- package/carbon/src/server/routes/v1/newsletter.ts +181 -0
- package/carbon/src/server/routes/v1/performance.ts +157 -0
- package/carbon/src/server/routes/v1/stats.ts +170 -0
- package/carbon/src/server/routes/v1/theme.ts +106 -0
- package/carbon/src/server/routes/webhooks/billing.ts +376 -0
- package/carbon/src/server/routes/webhooks/stripe.ts +276 -0
- package/carbon/src/server/types.ts +11 -0
- package/carbon/src/shared/pricing.ts +155 -0
- package/carbon/src/shared/types.ts +338 -0
- package/carbon/supabase/migrations/00001_init.sql +717 -0
- package/carbon/supabase/migrations/00002_theme_settings.sql +13 -0
- package/carbon/supabase/migrations/00003_pg_cron.sql +121 -0
- package/carbon/supabase/migrations/00004_contact_newsletter.sql +81 -0
- package/carbon/supabase/migrations/00005_localization_languages.sql +22 -0
- package/carbon/supabase/seed.sql +16 -0
- package/carbon/tests/_helpers/app.ts +45 -0
- package/carbon/tests/_helpers/env.ts +37 -0
- package/carbon/tests/_helpers/factories.ts +69 -0
- package/carbon/tests/_helpers/jwt.ts +23 -0
- package/carbon/tests/_helpers/setup-integration.ts +20 -0
- package/carbon/tests/_helpers/setup-rtl.ts +12 -0
- package/carbon/tests/component/ErrorBoundary.test.tsx +53 -0
- package/carbon/tests/component/use-auth-settings.test.tsx +119 -0
- package/carbon/tests/integration/server/routes/contact.test.ts +162 -0
- package/carbon/tests/integration/server/routes/health.test.ts +61 -0
- package/carbon/tests/structural/i18n-parity.test.ts +42 -0
- package/carbon/tests/unit/client/utils.test.ts +49 -0
- package/carbon/tests/unit/shared/pricing.test.ts +93 -0
- package/carbon/tsconfig.json +27 -0
- package/carbon/tsconfig.server.json +27 -0
- package/carbon/tsconfig.test.json +9 -0
- package/carbon/vite.config.ts +110 -0
- package/carbon/vitest.config.ts +74 -0
- package/carbon/volumes/db/jwt.sql +57 -0
- package/carbon/volumes/db/metabase-init.sh +29 -0
- package/carbon/volumes/db/n8n-init.sh +25 -0
- package/carbon/volumes/db/realtime.sql +33 -0
- package/carbon/volumes/db/roles.sql +93 -0
- package/carbon/volumes/db/set-passwords.sh +12 -0
- package/carbon/volumes/db/super-admin.dev.sql +113 -0
- package/carbon/volumes/db/super-admin.generated.sql +113 -0
- package/carbon/volumes/db/super-admin.sql +114 -0
- package/carbon/volumes/grafana/dashboards/logs.json +179 -0
- package/carbon/volumes/grafana/dashboards/overview.json +523 -0
- package/carbon/volumes/grafana/dashboards/postgresql.json +337 -0
- package/carbon/volumes/grafana/dashboards.dev/logs.json +179 -0
- package/carbon/volumes/grafana/dashboards.dev/overview.json +156 -0
- package/carbon/volumes/grafana/dashboards.dev/postgresql.json +337 -0
- package/carbon/volumes/grafana/provisioning/dashboards/dashboards.dev.yml +16 -0
- package/carbon/volumes/grafana/provisioning/dashboards/dashboards.yml +16 -0
- package/carbon/volumes/grafana/provisioning/datasources/datasources.yml +43 -0
- package/carbon/volumes/kong/docker-entrypoint.sh +9 -0
- package/carbon/volumes/kong/kong.yml +208 -0
- package/carbon/volumes/loki/loki-config.yml +58 -0
- package/carbon/volumes/n8n/hooks.js +131 -0
- package/carbon/volumes/n8n/scripts/setup.sh +66 -0
- package/carbon/volumes/prometheus/prometheus.yml +43 -0
- package/carbon/volumes/promtail/promtail-config.yml +64 -0
- package/carbon/volumes/traefik/middlewares.dev.yml +83 -0
- package/carbon/volumes/traefik/middlewares.yml +95 -0
- package/carbon/volumes/traefik/vite-dev.yml +20 -0
- package/package.json +95 -0
- package/src/access.js +354 -0
- package/src/activate.js +187 -0
- package/src/add.js +718 -0
- package/src/backup.js +786 -0
- package/src/cli.js +350 -0
- package/src/configure.js +967 -0
- package/src/console.js +155 -0
- package/src/create.js +1499 -0
- package/src/deploy.js +311 -0
- package/src/destroy.js +2033 -0
- package/src/diagnose.js +735 -0
- package/src/down.js +80 -0
- package/src/failover.js +1032 -0
- package/src/lib/backup-s3.js +179 -0
- package/src/lib/build.js +33 -0
- package/src/lib/checksum.js +28 -0
- package/src/lib/ci-setup.js +666 -0
- package/src/lib/cli/help.js +129 -0
- package/src/lib/cli/parse-flags.js +160 -0
- package/src/lib/cli/select-action.js +65 -0
- package/src/lib/cli/select-environment.js +108 -0
- package/src/lib/cli/tty-guard.js +75 -0
- package/src/lib/cloudflare.js +447 -0
- package/src/lib/colors.js +52 -0
- package/src/lib/command.js +361 -0
- package/src/lib/config.js +359 -0
- package/src/lib/cost.js +103 -0
- package/src/lib/deploy/bundle.js +231 -0
- package/src/lib/deploy/compose/acme-verify.js +121 -0
- package/src/lib/deploy/compose/build-args.js +78 -0
- package/src/lib/deploy/compose/ha.js +1874 -0
- package/src/lib/deploy/compose/index.js +1294 -0
- package/src/lib/deploy/compose/reconcile.js +74 -0
- package/src/lib/deploy/github.js +382 -0
- package/src/lib/deploy/image.js +191 -0
- package/src/lib/deploy/index.js +119 -0
- package/src/lib/deploy/k8s/LICENSE +99 -0
- package/src/lib/deploy/k8s/gitops-deploy.js +298 -0
- package/src/lib/deploy/k8s/ha/index.js +1000 -0
- package/src/lib/deploy/k8s/index.js +62 -0
- package/src/lib/deploy/k8s/k3s.js +2515 -0
- package/src/lib/deploy/orchestrator.js +1401 -0
- package/src/lib/deploy/prompts.js +545 -0
- package/src/lib/deploy/remote-build.js +130 -0
- package/src/lib/deploy/state.js +120 -0
- package/src/lib/deploy/utils.js +328 -0
- package/src/lib/deploy-logger.js +93 -0
- package/src/lib/dns-propagation.js +48 -0
- package/src/lib/environment.js +58 -0
- package/src/lib/fetch-retry.js +103 -0
- package/src/lib/github-environments.js +335 -0
- package/src/lib/hetzner-dns.js +377 -0
- package/src/lib/hetzner-guided-setup.js +275 -0
- package/src/lib/host-keys.js +37 -0
- package/src/lib/iac/cloud-init.js +52 -0
- package/src/lib/iac/index.js +325 -0
- package/src/lib/iac/programs/hetzner-compose.js +130 -0
- package/src/lib/iac/programs/hetzner-k8s.js +320 -0
- package/src/lib/kubectl.js +81 -0
- package/src/lib/licensing/index.js +259 -0
- package/src/lib/licensing/tiers.js +181 -0
- package/src/lib/licensing/validator.js +171 -0
- package/src/lib/merge-package-json.js +90 -0
- package/src/lib/operator-ip.js +381 -0
- package/src/lib/package-manager.js +111 -0
- package/src/lib/perf.js +71 -0
- package/src/lib/project-guard.js +39 -0
- package/src/lib/project.js +334 -0
- package/src/lib/providers/base.js +276 -0
- package/src/lib/providers/hetzner-s3.js +656 -0
- package/src/lib/providers/hetzner.js +755 -0
- package/src/lib/providers/index.js +164 -0
- package/src/lib/s3.js +510 -0
- package/src/lib/secret-scan.js +583 -0
- package/src/lib/secrets.js +63 -0
- package/src/lib/server-types.js +195 -0
- package/src/lib/shell.js +91 -0
- package/src/lib/ssh.js +241 -0
- package/src/lib/tracker.js +242 -0
- package/src/lib/upgrade-policy.js +170 -0
- package/src/lib/validators.js +105 -0
- package/src/lib/version.js +5 -0
- package/src/remove.js +292 -0
- package/src/reset.js +97 -0
- package/src/restore.js +871 -0
- package/src/scale.js +1734 -0
- package/src/shell.js +222 -0
- package/src/status.js +981 -0
- package/src/up.js +264 -0
- 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
|