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,215 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Note: This script runs AS PART of cloud-init (user_data), so we cannot wait for
|
|
5
|
+
# cloud-init to complete (that would cause a deadlock). The script starts immediately.
|
|
6
|
+
|
|
7
|
+
# Wait for any dpkg locks held by unattended-upgrades (runs on first boot).
|
|
8
|
+
# Without this, apt-get fails intermittently with "Could not get lock /var/lib/dpkg/lock".
|
|
9
|
+
for i in $(seq 1 30); do
|
|
10
|
+
if ! fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; then
|
|
11
|
+
break
|
|
12
|
+
fi
|
|
13
|
+
echo "Waiting for dpkg lock to be released... ($i/30)"
|
|
14
|
+
sleep 10
|
|
15
|
+
done
|
|
16
|
+
|
|
17
|
+
# Update apt lists + install the packages we need. `apt-get upgrade` was
|
|
18
|
+
# removed from the critical path — unattended-upgrades handles security
|
|
19
|
+
# patches async. Saves 30-60s per deploy.
|
|
20
|
+
apt-get update -qq
|
|
21
|
+
apt-get install -y -qq curl jq docker.io docker-buildx
|
|
22
|
+
|
|
23
|
+
# Validate k3s version is set
|
|
24
|
+
K3S_TARGET_VERSION="${k3s_version}"
|
|
25
|
+
if [ -z "$K3S_TARGET_VERSION" ]; then
|
|
26
|
+
echo "ERROR: k3s_version not set"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
echo "Installing k3s version: $K3S_TARGET_VERSION"
|
|
30
|
+
|
|
31
|
+
# IDENTICAL block in master-init.sh / worker-init.sh / supabase-init.sh — keep in sync.
|
|
32
|
+
# (Three-way duplication is acceptable here because the cloud-init scripts are
|
|
33
|
+
# rendered independently per role; extracting a shared snippet would require
|
|
34
|
+
# restructuring the renderScript() pipeline. Out of scope for Phase 1.)
|
|
35
|
+
# Configure containerd to mirror pulls for 10.0.1.1:5000 to the local registry
|
|
36
|
+
# we run on master. Cluster-autoscaler-spawned workers pull the app image from
|
|
37
|
+
# here on-demand (sideload only reaches workers that exist at sideload time).
|
|
38
|
+
# k3s only reads /etc/rancher/k3s/registries.yaml on agent start, so this MUST
|
|
39
|
+
# land before the k3s install step below.
|
|
40
|
+
#
|
|
41
|
+
# Plain http://10.0.1.1:5000 — the registry runs HTTP on the private network.
|
|
42
|
+
# Do NOT add `insecure_skip_verify: true` here: that flag only affects HTTPS
|
|
43
|
+
# endpoints, and recent k3s versions place it incorrectly when present
|
|
44
|
+
# (k3s-io/k3s#13215), which can break containerd config generation.
|
|
45
|
+
mkdir -p /etc/rancher/k3s
|
|
46
|
+
cat > /etc/rancher/k3s/registries.yaml << 'REGEOF'
|
|
47
|
+
mirrors:
|
|
48
|
+
"10.0.1.1:5000":
|
|
49
|
+
endpoint:
|
|
50
|
+
- "http://10.0.1.1:5000"
|
|
51
|
+
REGEOF
|
|
52
|
+
|
|
53
|
+
# Configure Floating IP on the OS so the kernel accepts packets destined for it.
|
|
54
|
+
# Hetzner routes the Floating IP to this server at the network level, but without
|
|
55
|
+
# a local address binding the kernel drops the packets.
|
|
56
|
+
# Use `ip addr add` instead of netplan — a separate netplan file that redefines
|
|
57
|
+
# ethernets.eth0 can override Hetzner's cloud-init config and drop the primary IP.
|
|
58
|
+
ip addr add ${floating_ip}/32 dev eth0 2>/dev/null || true
|
|
59
|
+
|
|
60
|
+
# Persist across reboots via a long-running systemd service (netplan-safe).
|
|
61
|
+
# Must be Type=simple (not oneshot) so it survives DHCP renewals — when networkd
|
|
62
|
+
# re-applies the cloud-init netplan config it can flush manually-added addresses.
|
|
63
|
+
# The loop re-adds the floating IP within 30 seconds if that happens.
|
|
64
|
+
cat > /etc/systemd/system/floating-ip.service << FIPEOF
|
|
65
|
+
[Unit]
|
|
66
|
+
Description=Maintain Hetzner Floating IP
|
|
67
|
+
After=network-online.target
|
|
68
|
+
Wants=network-online.target
|
|
69
|
+
|
|
70
|
+
[Service]
|
|
71
|
+
Type=simple
|
|
72
|
+
ExecStart=/bin/bash -c 'while true; do if ! ip addr show eth0 | grep -q "${floating_ip}"; then ip addr add ${floating_ip}/32 dev eth0 && echo "Floating IP re-added"; fi; sleep 30; done'
|
|
73
|
+
Restart=always
|
|
74
|
+
RestartSec=5
|
|
75
|
+
|
|
76
|
+
[Install]
|
|
77
|
+
WantedBy=multi-user.target
|
|
78
|
+
FIPEOF
|
|
79
|
+
systemctl daemon-reload
|
|
80
|
+
systemctl enable floating-ip.service
|
|
81
|
+
|
|
82
|
+
# Get node IPs for k3s configuration
|
|
83
|
+
PUBLIC_IP=$(curl -s --max-time 10 http://169.254.169.254/hetzner/v1/metadata/public-ipv4)
|
|
84
|
+
|
|
85
|
+
# Wait for the private network interface (10.0.x.x) to be assigned.
|
|
86
|
+
# Hetzner attaches the private network after the server boots, so the interface
|
|
87
|
+
# may not exist yet when cloud-init runs. grep exits 1 if no match, which kills
|
|
88
|
+
# the script under set -euo pipefail — so we retry with || true.
|
|
89
|
+
PRIVATE_IFACE=""
|
|
90
|
+
RETRY_COUNT=0
|
|
91
|
+
MAX_RETRIES=120
|
|
92
|
+
DHCP_TRIGGERED=false
|
|
93
|
+
echo "Waiting for private network interface..."
|
|
94
|
+
while [ -z "$PRIVATE_IFACE" ] && [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
95
|
+
PRIVATE_IFACE=$(ip -4 addr show | grep 'inet 10\.0\.' | awk '{print $NF}' || true)
|
|
96
|
+
if [ -z "$PRIVATE_IFACE" ]; then
|
|
97
|
+
# See worker-init.sh for the dhcpcd-race RCA. Same recovery applies here.
|
|
98
|
+
if [ "$DHCP_TRIGGERED" = "false" ] && [ $RETRY_COUNT -ge 6 ] && command -v dhcpcd >/dev/null 2>&1; then
|
|
99
|
+
CANDIDATE=$(ip -o link show 2>/dev/null | awk -F': ' '/^[0-9]+: en[a-z]+[0-9]+s[0-9]+/ {print $2}' | grep -v '^eth' | head -1 || true)
|
|
100
|
+
if [ -n "$CANDIDATE" ]; then
|
|
101
|
+
DHCP_TRIGGERED=true
|
|
102
|
+
echo "Private NIC $CANDIDATE has no lease after 30s — triggering dhcpcd"
|
|
103
|
+
ip link set "$CANDIDATE" up 2>&1 || true
|
|
104
|
+
dhcpcd -1 "$CANDIDATE" 2>&1 | sed 's/^/ dhcpcd: /' || true
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
if [ $((RETRY_COUNT % 12)) -eq 0 ]; then
|
|
108
|
+
echo "Private interface not ready yet... ($RETRY_COUNT/$MAX_RETRIES)"
|
|
109
|
+
fi
|
|
110
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
111
|
+
sleep 5
|
|
112
|
+
fi
|
|
113
|
+
done
|
|
114
|
+
if [ -z "$PRIVATE_IFACE" ]; then
|
|
115
|
+
echo "FATAL: Private network interface not found after $MAX_RETRIES retries"
|
|
116
|
+
echo "Hetzner private network may not be attached to this server."
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
echo "Private interface: $PRIVATE_IFACE"
|
|
120
|
+
PRIVATE_IP=$(ip -4 addr show "$PRIVATE_IFACE" 2>/dev/null | grep -oP 'inet \K[\d.]+' || echo "")
|
|
121
|
+
|
|
122
|
+
# Install k3s master with pre-shared token.
|
|
123
|
+
# --node-ip must be the PUBLIC IP so Hetzner CCM can match it against the Hetzner API
|
|
124
|
+
# and remove the node.cloudprovider.kubernetes.io/uninitialized taint.
|
|
125
|
+
# --flannel-iface routes pod-to-pod traffic over the private network.
|
|
126
|
+
#
|
|
127
|
+
# The k3s install script itself fetches https://github.com/k3s-io/k3s/releases/... and
|
|
128
|
+
# GitHub routinely returns 504 for brief windows. The outer curl has --retry 3, but
|
|
129
|
+
# GitHub failures happen inside the install script where we can't hand --retry through.
|
|
130
|
+
# Wrap the whole pipeline in a bash retry loop — up to 5 attempts with 20s backoff —
|
|
131
|
+
# so a transient CDN hiccup doesn't kill a fresh provision.
|
|
132
|
+
K3S_INSTALL_OK=false
|
|
133
|
+
for attempt in 1 2 3 4 5; do
|
|
134
|
+
if curl -sfL --max-time 120 --retry 3 --retry-delay 10 https://get.k3s.io | INSTALL_K3S_VERSION="${k3s_version}" sh -s - server \
|
|
135
|
+
--cluster-init \
|
|
136
|
+
--token "${k3s_token}" \
|
|
137
|
+
--tls-san "$PUBLIC_IP" \
|
|
138
|
+
--tls-san "${floating_ip}" \
|
|
139
|
+
--node-ip "$PUBLIC_IP" \
|
|
140
|
+
--node-external-ip "$PUBLIC_IP" \
|
|
141
|
+
--advertise-address "$PUBLIC_IP" \
|
|
142
|
+
--flannel-iface "$PRIVATE_IFACE" \
|
|
143
|
+
--disable traefik \
|
|
144
|
+
--disable servicelb \
|
|
145
|
+
--disable-cloud-controller \
|
|
146
|
+
--flannel-backend=wireguard-native \
|
|
147
|
+
--kubelet-arg="cloud-provider=external" \
|
|
148
|
+
--write-kubeconfig-mode=644; then
|
|
149
|
+
K3S_INSTALL_OK=true
|
|
150
|
+
break
|
|
151
|
+
fi
|
|
152
|
+
echo "k3s install attempt $attempt failed (likely transient GitHub/CDN issue), retrying in 20s..."
|
|
153
|
+
sleep 20
|
|
154
|
+
done
|
|
155
|
+
if [ "$K3S_INSTALL_OK" != "true" ]; then
|
|
156
|
+
echo "k3s install failed after 5 attempts — check GitHub CDN status" >&2
|
|
157
|
+
exit 1
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Wait for k3s to be ready (max 3 minutes — if it hasn't started by then, something is wrong)
|
|
161
|
+
K3S_READY=false
|
|
162
|
+
for i in $(seq 1 36); do
|
|
163
|
+
if kubectl get nodes 2>/dev/null | grep -q "Ready"; then
|
|
164
|
+
K3S_READY=true
|
|
165
|
+
break
|
|
166
|
+
fi
|
|
167
|
+
echo "Waiting for k3s to be ready... ($i/36)"
|
|
168
|
+
sleep 5
|
|
169
|
+
done
|
|
170
|
+
if [ "$K3S_READY" != "true" ]; then
|
|
171
|
+
echo "FATAL: k3s did not become ready within 3 minutes"
|
|
172
|
+
echo "k3s service status:"
|
|
173
|
+
systemctl status k3s --no-pager 2>&1 || true
|
|
174
|
+
journalctl -u k3s --no-pager -n 20 2>&1 || true
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# Marker for vibecarbon's deployK3s waiter — set AFTER hcloud Secret + CCM/CSI
|
|
179
|
+
# install below complete (see end of script). Keeps the early-readiness signal
|
|
180
|
+
# distinct from "fully provisioned and ready for app install."
|
|
181
|
+
|
|
182
|
+
# Create secret for Hetzner Cloud Controller Manager
|
|
183
|
+
kubectl -n kube-system create secret generic hcloud \
|
|
184
|
+
--from-literal=token="${hcloud_token}" \
|
|
185
|
+
--from-literal=network="${network_id}" \
|
|
186
|
+
--dry-run=client -o yaml | kubectl apply -f -
|
|
187
|
+
|
|
188
|
+
# Create secret for Hetzner CSI Driver
|
|
189
|
+
kubectl -n kube-system create secret generic hcloud-csi \
|
|
190
|
+
--from-literal=token="${hcloud_token}" \
|
|
191
|
+
--dry-run=client -o yaml | kubectl apply -f -
|
|
192
|
+
|
|
193
|
+
# Install Hetzner Cloud Controller Manager
|
|
194
|
+
# Use ccm.yaml (not ccm-networks.yaml) since k3s manages pod networking via flannel
|
|
195
|
+
# Retry up to 3 times — kubectl apply can fail if API server is briefly unavailable
|
|
196
|
+
for i in 1 2 3; do
|
|
197
|
+
kubectl apply -f https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/download/v1.20.0/ccm.yaml && break
|
|
198
|
+
echo "CCM install attempt $i failed, retrying in 10s..."
|
|
199
|
+
sleep 10
|
|
200
|
+
done
|
|
201
|
+
|
|
202
|
+
# Install Hetzner CSI Driver
|
|
203
|
+
for i in 1 2 3; do
|
|
204
|
+
kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/v2.9.0/deploy/kubernetes/hcloud-csi.yml && break
|
|
205
|
+
echo "CSI install attempt $i failed, retrying in 10s..."
|
|
206
|
+
sleep 10
|
|
207
|
+
done
|
|
208
|
+
|
|
209
|
+
echo "k3s master installation complete!"
|
|
210
|
+
|
|
211
|
+
# Final marker: vibecarbon's deployK3s polls for /tmp/k3s-ready before fetching
|
|
212
|
+
# kubeconfig + applying app manifests. Written here so the marker only fires
|
|
213
|
+
# after k3s + hcloud secrets + CCM + CSI are all in place — i.e., the cluster
|
|
214
|
+
# is ready for real workloads, not just "k3s daemon is up."
|
|
215
|
+
touch /tmp/k3s-ready
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Note: This script runs AS PART of cloud-init (user_data), so we cannot wait for
|
|
5
|
+
# cloud-init to complete (that would cause a deadlock). The script starts immediately.
|
|
6
|
+
|
|
7
|
+
# Wait for any dpkg locks held by unattended-upgrades (runs on first boot).
|
|
8
|
+
for i in $(seq 1 30); do
|
|
9
|
+
if ! fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; then
|
|
10
|
+
break
|
|
11
|
+
fi
|
|
12
|
+
echo "Waiting for dpkg lock to be released... ($i/30)"
|
|
13
|
+
sleep 10
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
# Update apt lists + install just what we need. `apt-get upgrade` was removed
|
|
17
|
+
# from this path — we don't need every security update applied before the node
|
|
18
|
+
# joins the cluster (unattended-upgrades handles that async in the background),
|
|
19
|
+
# and it used to add 30-60s to every deploy.
|
|
20
|
+
apt-get update -qq
|
|
21
|
+
apt-get install -y -qq curl jq
|
|
22
|
+
|
|
23
|
+
# Validate k3s version is set
|
|
24
|
+
K3S_TARGET_VERSION="${k3s_version}"
|
|
25
|
+
if [ -z "$K3S_TARGET_VERSION" ]; then
|
|
26
|
+
echo "ERROR: k3s_version not set"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
echo "Installing k3s version: $K3S_TARGET_VERSION"
|
|
30
|
+
|
|
31
|
+
# IDENTICAL block in master-init.sh / worker-init.sh / supabase-init.sh — keep in sync.
|
|
32
|
+
# (Three-way duplication is acceptable here because the cloud-init scripts are
|
|
33
|
+
# rendered independently per role; extracting a shared snippet would require
|
|
34
|
+
# restructuring the renderScript() pipeline. Out of scope for Phase 1.)
|
|
35
|
+
# Configure containerd to mirror pulls for 10.0.1.1:5000 to the local registry
|
|
36
|
+
# running on master. Cluster-autoscaler-spawned workers pull the app image from
|
|
37
|
+
# here on-demand (sideload only reaches workers that exist at sideload time).
|
|
38
|
+
# k3s only reads /etc/rancher/k3s/registries.yaml on agent start, so this MUST
|
|
39
|
+
# land before the k3s install step below.
|
|
40
|
+
#
|
|
41
|
+
# Plain http://10.0.1.1:5000 — the registry runs HTTP on the private network.
|
|
42
|
+
# Do NOT add `insecure_skip_verify: true` here: that flag only affects HTTPS
|
|
43
|
+
# endpoints, and recent k3s versions place it incorrectly when present
|
|
44
|
+
# (k3s-io/k3s#13215), which can break containerd config generation.
|
|
45
|
+
mkdir -p /etc/rancher/k3s
|
|
46
|
+
cat > /etc/rancher/k3s/registries.yaml << 'REGEOF'
|
|
47
|
+
mirrors:
|
|
48
|
+
"10.0.1.1:5000":
|
|
49
|
+
endpoint:
|
|
50
|
+
- "http://10.0.1.1:5000"
|
|
51
|
+
REGEOF
|
|
52
|
+
|
|
53
|
+
# Get node IPs for k3s configuration
|
|
54
|
+
PUBLIC_IP=$(curl -s --max-time 10 http://169.254.169.254/hetzner/v1/metadata/public-ipv4)
|
|
55
|
+
|
|
56
|
+
# Wait for the private network interface (10.0.x.x) to be assigned.
|
|
57
|
+
# Hetzner attaches the private network after the server boots, so the interface
|
|
58
|
+
# may not exist yet when cloud-init runs. grep exits 1 if no match, which kills
|
|
59
|
+
# the script under set -euo pipefail — so we retry with || true.
|
|
60
|
+
#
|
|
61
|
+
# This must happen BEFORE the master-readyz probe below, because that probe
|
|
62
|
+
# uses the master's private IP (10.0.1.1) — unreachable until our private NIC
|
|
63
|
+
# is up. See worker-init.sh for the full RCA.
|
|
64
|
+
PRIVATE_IFACE=""
|
|
65
|
+
RETRY_COUNT=0
|
|
66
|
+
MAX_RETRIES=120
|
|
67
|
+
DHCP_TRIGGERED=false
|
|
68
|
+
echo "Waiting for private network interface..."
|
|
69
|
+
while [ -z "$PRIVATE_IFACE" ] && [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
70
|
+
PRIVATE_IFACE=$(ip -4 addr show | grep 'inet 10\.0\.' | awk '{print $NF}' || true)
|
|
71
|
+
if [ -z "$PRIVATE_IFACE" ]; then
|
|
72
|
+
if [ "$DHCP_TRIGGERED" = "false" ] && [ $RETRY_COUNT -ge 6 ] && command -v dhcpcd >/dev/null 2>&1; then
|
|
73
|
+
CANDIDATE=$(ip -o link show 2>/dev/null | awk -F': ' '/^[0-9]+: en[a-z]+[0-9]+s[0-9]+/ {print $2}' | grep -v '^eth' | head -1 || true)
|
|
74
|
+
if [ -n "$CANDIDATE" ]; then
|
|
75
|
+
DHCP_TRIGGERED=true
|
|
76
|
+
echo "Private NIC $CANDIDATE has no lease after 30s — triggering dhcpcd"
|
|
77
|
+
ip link set "$CANDIDATE" up 2>&1 || true
|
|
78
|
+
dhcpcd -1 "$CANDIDATE" 2>&1 | sed 's/^/ dhcpcd: /' || true
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
if [ $((RETRY_COUNT % 12)) -eq 0 ]; then
|
|
82
|
+
echo "Private interface not ready yet... ($RETRY_COUNT/$MAX_RETRIES)"
|
|
83
|
+
fi
|
|
84
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
85
|
+
sleep 5
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
if [ -z "$PRIVATE_IFACE" ]; then
|
|
89
|
+
echo "FATAL: Private network interface not found after $MAX_RETRIES retries"
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
echo "Private interface: $PRIVATE_IFACE"
|
|
93
|
+
PRIVATE_IP=$(ip -4 addr show "$PRIVATE_IFACE" 2>/dev/null | grep -oP 'inet \K[\d.]+' || echo "")
|
|
94
|
+
|
|
95
|
+
# Wait for master k3s API to be ready before joining (uses private IP — must
|
|
96
|
+
# come AFTER private-NIC bring-up above).
|
|
97
|
+
MAX_RETRIES=120
|
|
98
|
+
RETRY_COUNT=0
|
|
99
|
+
|
|
100
|
+
echo "Waiting for master k3s API to be ready..."
|
|
101
|
+
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
102
|
+
if curl -sk --max-time 5 "https://${master_ip}:6443/readyz" >/dev/null 2>&1; then
|
|
103
|
+
echo "Master k3s API is ready!"
|
|
104
|
+
break
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if [ $((RETRY_COUNT % 12)) -eq 0 ]; then
|
|
108
|
+
echo "Master not ready yet... ($RETRY_COUNT/$MAX_RETRIES)"
|
|
109
|
+
fi
|
|
110
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
111
|
+
sleep 5
|
|
112
|
+
done
|
|
113
|
+
|
|
114
|
+
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
|
|
115
|
+
echo "Warning: Master may not be fully ready, attempting to join anyway..."
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Install k3s agent as dedicated Supabase node.
|
|
119
|
+
# --node-ip must be the PUBLIC IP so Hetzner CCM can match it (see master-init.sh).
|
|
120
|
+
# Bash retry loop around the whole pipeline — the k3s install script fetches
|
|
121
|
+
# from github.com which returns transient 504s under load. 5 attempts, 20s backoff.
|
|
122
|
+
K3S_INSTALL_OK=false
|
|
123
|
+
for attempt in 1 2 3 4 5; do
|
|
124
|
+
if curl -sfL --max-time 120 --retry 3 --retry-delay 10 https://get.k3s.io | INSTALL_K3S_VERSION="${k3s_version}" K3S_URL="https://${master_ip}:6443" K3S_TOKEN="${k3s_token}" sh -s - agent \
|
|
125
|
+
--node-ip "$PUBLIC_IP" \
|
|
126
|
+
--node-external-ip "$PUBLIC_IP" \
|
|
127
|
+
--flannel-iface "$PRIVATE_IFACE" \
|
|
128
|
+
--kubelet-arg="cloud-provider=external" \
|
|
129
|
+
--node-label="dedicated=supabase" \
|
|
130
|
+
--node-label="node-pool=supabase-pool" \
|
|
131
|
+
--node-taint="dedicated=supabase:NoSchedule"; then
|
|
132
|
+
K3S_INSTALL_OK=true
|
|
133
|
+
break
|
|
134
|
+
fi
|
|
135
|
+
echo "k3s supabase install attempt $attempt failed (likely transient GitHub/CDN issue), retrying in 20s..."
|
|
136
|
+
sleep 20
|
|
137
|
+
done
|
|
138
|
+
if [ "$K3S_INSTALL_OK" != "true" ]; then
|
|
139
|
+
echo "k3s supabase install failed after 5 attempts — check GitHub CDN status" >&2
|
|
140
|
+
exit 1
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
echo "k3s supabase node installation complete!"
|
|
144
|
+
|
|
145
|
+
# Pre-warm containerd image cache in the background so the Supabase HelmRelease
|
|
146
|
+
# doesn't pay the full ~400MB postgres pull on its critical path. We only
|
|
147
|
+
# pre-pull stable-name images that supabase-community pins conservatively;
|
|
148
|
+
# version drift just means a cache miss (not wrong data).
|
|
149
|
+
#
|
|
150
|
+
# Runs async so the node can be marked Ready immediately — the main deploy
|
|
151
|
+
# doesn't block on this. If the pull is still in-flight when the chart
|
|
152
|
+
# schedules a pod, containerd deduplicates the concurrent pulls anyway.
|
|
153
|
+
nohup bash -c '
|
|
154
|
+
for i in $(seq 1 60); do
|
|
155
|
+
[ -S /run/k3s/containerd/containerd.sock ] && break
|
|
156
|
+
sleep 5
|
|
157
|
+
done
|
|
158
|
+
# Heaviest base images in the Supabase stack (~400MB combined). Pinned
|
|
159
|
+
# tags match supabase-community chart defaults at time of authoring.
|
|
160
|
+
for img in \
|
|
161
|
+
docker.io/supabase/postgres:15.1.1.78 \
|
|
162
|
+
docker.io/library/kong:2.8.1 \
|
|
163
|
+
; do
|
|
164
|
+
/usr/local/bin/k3s crictl pull "$img" 2>&1 || echo "Pre-pull skipped: $img"
|
|
165
|
+
done
|
|
166
|
+
' > /var/log/supabase-image-prewarm.log 2>&1 &
|
|
167
|
+
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Note: This script runs AS PART of cloud-init (user_data), so we cannot wait for
|
|
5
|
+
# cloud-init to complete (that would cause a deadlock). The script starts immediately.
|
|
6
|
+
|
|
7
|
+
# Wait for any dpkg locks held by unattended-upgrades (runs on first boot).
|
|
8
|
+
for i in $(seq 1 30); do
|
|
9
|
+
if ! fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; then
|
|
10
|
+
break
|
|
11
|
+
fi
|
|
12
|
+
echo "Waiting for dpkg lock to be released... ($i/30)"
|
|
13
|
+
sleep 10
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
# Update apt lists + install the packages we need. `apt-get upgrade` was
|
|
17
|
+
# removed from the critical path — unattended-upgrades handles security
|
|
18
|
+
# patches async. Saves 30-60s per deploy.
|
|
19
|
+
apt-get update -qq
|
|
20
|
+
apt-get install -y -qq curl jq
|
|
21
|
+
|
|
22
|
+
# Validate k3s version is set
|
|
23
|
+
K3S_TARGET_VERSION="${k3s_version}"
|
|
24
|
+
if [ -z "$K3S_TARGET_VERSION" ]; then
|
|
25
|
+
echo "ERROR: k3s_version not set"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
echo "Installing k3s version: $K3S_TARGET_VERSION"
|
|
29
|
+
|
|
30
|
+
# IDENTICAL block in master-init.sh / worker-init.sh / supabase-init.sh — keep in sync.
|
|
31
|
+
# (Three-way duplication is acceptable here because the cloud-init scripts are
|
|
32
|
+
# rendered independently per role; extracting a shared snippet would require
|
|
33
|
+
# restructuring the renderScript() pipeline. Out of scope for Phase 1.)
|
|
34
|
+
# Configure containerd to mirror pulls for 10.0.1.1:5000 to the local registry
|
|
35
|
+
# running on master. Cluster-autoscaler-spawned workers pull the app image from
|
|
36
|
+
# here on-demand (sideload only reaches workers that exist at sideload time).
|
|
37
|
+
# k3s only reads /etc/rancher/k3s/registries.yaml on agent start, so this MUST
|
|
38
|
+
# land before the k3s install step below.
|
|
39
|
+
#
|
|
40
|
+
# Plain http://10.0.1.1:5000 — the registry runs HTTP on the private network.
|
|
41
|
+
# Do NOT add `insecure_skip_verify: true` here: that flag only affects HTTPS
|
|
42
|
+
# endpoints, and recent k3s versions place it incorrectly when present
|
|
43
|
+
# (k3s-io/k3s#13215), which can break containerd config generation.
|
|
44
|
+
mkdir -p /etc/rancher/k3s
|
|
45
|
+
cat > /etc/rancher/k3s/registries.yaml << 'REGEOF'
|
|
46
|
+
mirrors:
|
|
47
|
+
"10.0.1.1:5000":
|
|
48
|
+
endpoint:
|
|
49
|
+
- "http://10.0.1.1:5000"
|
|
50
|
+
REGEOF
|
|
51
|
+
|
|
52
|
+
# Get node IPs for k3s configuration
|
|
53
|
+
PUBLIC_IP=$(curl -s --max-time 10 http://169.254.169.254/hetzner/v1/metadata/public-ipv4)
|
|
54
|
+
|
|
55
|
+
# Wait for the private network interface (10.0.x.x) to be assigned.
|
|
56
|
+
# Hetzner attaches the private network after the server boots, so the interface
|
|
57
|
+
# may not exist yet when cloud-init runs. grep exits 1 if no match, which kills
|
|
58
|
+
# the script under set -euo pipefail — so we retry with || true.
|
|
59
|
+
#
|
|
60
|
+
# This must happen BEFORE the master-readyz probe below, because that probe
|
|
61
|
+
# uses the master's private IP (10.0.1.1) — unreachable until our private NIC
|
|
62
|
+
# is up. Workers stuck in master-readyz with no private NIC was the failure
|
|
63
|
+
# mode in k8s-ha 2026-05-01 deploy run (yir52p, primary-worker-1): hung 600s
|
|
64
|
+
# in "Master not ready yet..." while enp7s0 sat in state DOWN with no IP.
|
|
65
|
+
PRIVATE_IFACE=""
|
|
66
|
+
RETRY_COUNT=0
|
|
67
|
+
MAX_RETRIES=120
|
|
68
|
+
DHCP_TRIGGERED=false
|
|
69
|
+
echo "Waiting for private network interface..."
|
|
70
|
+
while [ -z "$PRIVATE_IFACE" ] && [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
71
|
+
PRIVATE_IFACE=$(ip -4 addr show | grep 'inet 10\.0\.' | awk '{print $NF}' || true)
|
|
72
|
+
if [ -z "$PRIVATE_IFACE" ]; then
|
|
73
|
+
# After 30s with no 10.0.x.x address, actively trigger dhcpcd on any
|
|
74
|
+
# unmanaged enp* NIC. Hetzner ships dhcpcd-base + auto-starts dhcpcd at
|
|
75
|
+
# boot, but it races with the post-boot Hetzner private-network
|
|
76
|
+
# attachment. When dhcpcd loses the race the interface stays DOWN and no
|
|
77
|
+
# IP is ever requested — running `dhcpcd -1 <iface>` once obtains the
|
|
78
|
+
# lease idempotently. Skip if already triggered or dhcpcd missing.
|
|
79
|
+
if [ "$DHCP_TRIGGERED" = "false" ] && [ $RETRY_COUNT -ge 6 ] && command -v dhcpcd >/dev/null 2>&1; then
|
|
80
|
+
CANDIDATE=$(ip -o link show 2>/dev/null | awk -F': ' '/^[0-9]+: en[a-z]+[0-9]+s[0-9]+/ {print $2}' | grep -v '^eth' | head -1 || true)
|
|
81
|
+
if [ -n "$CANDIDATE" ]; then
|
|
82
|
+
DHCP_TRIGGERED=true
|
|
83
|
+
echo "Private NIC $CANDIDATE has no lease after 30s — triggering dhcpcd"
|
|
84
|
+
ip link set "$CANDIDATE" up 2>&1 || true
|
|
85
|
+
dhcpcd -1 "$CANDIDATE" 2>&1 | sed 's/^/ dhcpcd: /' || true
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
if [ $((RETRY_COUNT % 12)) -eq 0 ]; then
|
|
89
|
+
echo "Private interface not ready yet... ($RETRY_COUNT/$MAX_RETRIES)"
|
|
90
|
+
fi
|
|
91
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
92
|
+
sleep 5
|
|
93
|
+
fi
|
|
94
|
+
done
|
|
95
|
+
if [ -z "$PRIVATE_IFACE" ]; then
|
|
96
|
+
echo "FATAL: Private network interface not found after $MAX_RETRIES retries"
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
99
|
+
echo "Private interface: $PRIVATE_IFACE"
|
|
100
|
+
PRIVATE_IP=$(ip -4 addr show "$PRIVATE_IFACE" 2>/dev/null | grep -oP 'inet \K[\d.]+' || echo "")
|
|
101
|
+
|
|
102
|
+
# Wait for master k3s API to be ready before joining (uses private IP — must
|
|
103
|
+
# come AFTER private-NIC bring-up above).
|
|
104
|
+
MAX_RETRIES=120
|
|
105
|
+
RETRY_COUNT=0
|
|
106
|
+
|
|
107
|
+
echo "Waiting for master k3s API to be ready..."
|
|
108
|
+
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
|
109
|
+
if curl -sk --max-time 5 "https://${master_ip}:6443/readyz" >/dev/null 2>&1; then
|
|
110
|
+
echo "Master k3s API is ready!"
|
|
111
|
+
break
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if [ $((RETRY_COUNT % 12)) -eq 0 ]; then
|
|
115
|
+
echo "Master not ready yet... ($RETRY_COUNT/$MAX_RETRIES)"
|
|
116
|
+
fi
|
|
117
|
+
RETRY_COUNT=$((RETRY_COUNT + 1))
|
|
118
|
+
sleep 5
|
|
119
|
+
done
|
|
120
|
+
|
|
121
|
+
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
|
|
122
|
+
echo "Warning: Master may not be fully ready, attempting to join anyway..."
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# Install k3s agent with pre-shared token.
|
|
126
|
+
# --node-ip must be the PUBLIC IP so Hetzner CCM can match it (see master-init.sh).
|
|
127
|
+
# Bash retry loop around the whole pipeline — the k3s install script fetches
|
|
128
|
+
# from github.com which returns transient 504s under load. 5 attempts, 20s backoff.
|
|
129
|
+
K3S_INSTALL_OK=false
|
|
130
|
+
for attempt in 1 2 3 4 5; do
|
|
131
|
+
if curl -sfL --max-time 120 --retry 3 --retry-delay 10 https://get.k3s.io | INSTALL_K3S_VERSION="${k3s_version}" K3S_URL="https://${master_ip}:6443" K3S_TOKEN="${k3s_token}" sh -s - agent \
|
|
132
|
+
--node-ip "$PUBLIC_IP" \
|
|
133
|
+
--node-external-ip "$PUBLIC_IP" \
|
|
134
|
+
--flannel-iface "$PRIVATE_IFACE" \
|
|
135
|
+
--kubelet-arg="cloud-provider=external"; then
|
|
136
|
+
K3S_INSTALL_OK=true
|
|
137
|
+
break
|
|
138
|
+
fi
|
|
139
|
+
echo "k3s worker install attempt $attempt failed (likely transient GitHub/CDN issue), retrying in 20s..."
|
|
140
|
+
sleep 20
|
|
141
|
+
done
|
|
142
|
+
if [ "$K3S_INSTALL_OK" != "true" ]; then
|
|
143
|
+
echo "k3s worker install failed after 5 attempts — check GitHub CDN status" >&2
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
echo "k3s worker installation complete!"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "base-maia",
|
|
4
|
+
"rsc": false,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/client/index.css",
|
|
9
|
+
"baseColor": "gray",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "lucide",
|
|
14
|
+
"aliases": {
|
|
15
|
+
"components": "@/components",
|
|
16
|
+
"utils": "@/lib/utils",
|
|
17
|
+
"ui": "@/components/ui",
|
|
18
|
+
"lib": "@/lib",
|
|
19
|
+
"hooks": "@/hooks"
|
|
20
|
+
},
|
|
21
|
+
"menuColor": "default",
|
|
22
|
+
"menuAccent": "bold",
|
|
23
|
+
"registries": {}
|
|
24
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Authentication Guide
|
|
3
|
+
description: A complete guide to authentication features including OAuth, magic links, and MFA.
|
|
4
|
+
date: "2025-01-20"
|
|
5
|
+
author: Team
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Authentication Guide
|
|
9
|
+
|
|
10
|
+
Your application comes with a full authentication system powered by Supabase Auth.
|
|
11
|
+
|
|
12
|
+
## Supported Methods
|
|
13
|
+
|
|
14
|
+
- **Email & Password** — Traditional sign-up and sign-in
|
|
15
|
+
- **OAuth Providers** — Google, Microsoft, GitHub, Apple, Discord
|
|
16
|
+
- **Magic Links** — Passwordless sign-in via email
|
|
17
|
+
- **Multi-Factor Authentication** — TOTP-based MFA for added security
|
|
18
|
+
|
|
19
|
+
## Enabling OAuth Providers
|
|
20
|
+
|
|
21
|
+
OAuth providers are configured in your Supabase dashboard and toggled via environment variables:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
GITHUB_ENABLED=true
|
|
25
|
+
APPLE_ENABLED=true
|
|
26
|
+
DISCORD_ENABLED=true
|
|
27
|
+
MAGIC_LINK_ENABLED=true
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The sign-in page automatically shows buttons for enabled providers.
|
|
31
|
+
|
|
32
|
+
## Row-Level Security
|
|
33
|
+
|
|
34
|
+
All database access is secured with Row-Level Security (RLS) policies. Users can only access data they're authorized to see, enforced at the database level.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting Started
|
|
3
|
+
description: Learn how to set up your project and start building your SaaS application.
|
|
4
|
+
date: "2025-01-15"
|
|
5
|
+
author: Team
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Getting Started
|
|
9
|
+
|
|
10
|
+
Welcome to your new SaaS application! This guide will help you get up and running quickly.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
Before you begin, make sure you have:
|
|
15
|
+
|
|
16
|
+
- **Node.js 22+** installed
|
|
17
|
+
- **Docker** running for local development
|
|
18
|
+
- **pnpm** as your package manager
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
1. Start the development environment:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm dev:start
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This command will:
|
|
29
|
+
- Start Docker containers (Supabase, PostgreSQL, etc.)
|
|
30
|
+
- Run database migrations
|
|
31
|
+
- Launch the API server and Vite dev server
|
|
32
|
+
|
|
33
|
+
2. Open your browser to `http://app.localhost`
|
|
34
|
+
|
|
35
|
+
3. Sign in with your admin credentials
|
|
36
|
+
|
|
37
|
+
## Next Steps
|
|
38
|
+
|
|
39
|
+
- Configure your [Stripe billing](/settings/billing) to start accepting payments
|
|
40
|
+
- Set up [OAuth providers](/settings/security) for social login
|
|
41
|
+
- Invite your [team members](/dashboard) to collaborate
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Initial Release"
|
|
3
|
+
description: "The first release of {{PROJECT_NAME}} with core features including authentication, billing, and infrastructure."
|
|
4
|
+
date: "2025-01-15"
|
|
5
|
+
version: "0.1.0"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## What's New
|
|
9
|
+
|
|
10
|
+
### Authentication
|
|
11
|
+
- Email/password sign-up and sign-in
|
|
12
|
+
- OAuth providers (Google, Microsoft, GitHub, Apple, Discord)
|
|
13
|
+
- Magic link authentication
|
|
14
|
+
- Multi-factor authentication (TOTP)
|
|
15
|
+
- Account lockout protection
|
|
16
|
+
|
|
17
|
+
### Billing
|
|
18
|
+
- Stripe integration with subscription management
|
|
19
|
+
- Free, Startup, and Pro plans
|
|
20
|
+
- Customer portal for self-service billing
|
|
21
|
+
- Webhook-driven subscription lifecycle
|
|
22
|
+
|
|
23
|
+
### Infrastructure
|
|
24
|
+
- Self-hosted Supabase (PostgreSQL, Auth, REST API, Realtime, Storage)
|
|
25
|
+
- Docker Compose for local development
|
|
26
|
+
- Kubernetes manifests with Kustomize overlays
|
|
27
|
+
- High-availability deployment with PostgreSQL replication
|
|
28
|
+
- Traefik reverse proxy with automatic SSL
|
|
29
|
+
|
|
30
|
+
### Admin Panel
|
|
31
|
+
- Super admin dashboard with service health monitoring
|
|
32
|
+
- User and organization management
|
|
33
|
+
- System-wide notification management
|
|
34
|
+
- User impersonation for support
|
|
35
|
+
|
|
36
|
+
### Developer Experience
|
|
37
|
+
- Full TypeScript across client and server
|
|
38
|
+
- Biome for linting and formatting
|
|
39
|
+
- Vitest test suite
|
|
40
|
+
- AI editor rules (Claude Code, Cursor, Windsurf, Copilot)
|