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,318 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Local Kubernetes Testing with k3d
|
|
3
|
+
# Tests the k8s manifests locally before deploying to production
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# Colors
|
|
8
|
+
RED='\033[0;31m'
|
|
9
|
+
GREEN='\033[0;32m'
|
|
10
|
+
YELLOW='\033[1;33m'
|
|
11
|
+
BLUE='\033[0;34m'
|
|
12
|
+
NC='\033[0m'
|
|
13
|
+
|
|
14
|
+
CLUSTER_NAME="vibecarbon-local"
|
|
15
|
+
NAMESPACE="vibecarbon"
|
|
16
|
+
|
|
17
|
+
# Get script directory
|
|
18
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
19
|
+
|
|
20
|
+
print_step() { echo -e "\n${GREEN}▶ $1${NC}"; }
|
|
21
|
+
print_info() { echo -e "${BLUE}ℹ $1${NC}"; }
|
|
22
|
+
print_warning() { echo -e "${YELLOW}⚠ $1${NC}"; }
|
|
23
|
+
print_error() { echo -e "${RED}✗ $1${NC}"; }
|
|
24
|
+
print_success() { echo -e "${GREEN}✓ $1${NC}"; }
|
|
25
|
+
|
|
26
|
+
check_dependencies() {
|
|
27
|
+
print_step "Checking dependencies..."
|
|
28
|
+
|
|
29
|
+
local missing=()
|
|
30
|
+
|
|
31
|
+
command -v docker &> /dev/null || missing+=("docker")
|
|
32
|
+
command -v kubectl &> /dev/null || missing+=("kubectl")
|
|
33
|
+
command -v k3d &> /dev/null || missing+=("k3d")
|
|
34
|
+
|
|
35
|
+
if [ ${#missing[@]} -ne 0 ]; then
|
|
36
|
+
print_error "Missing dependencies: ${missing[*]}"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Install instructions:"
|
|
39
|
+
echo ""
|
|
40
|
+
if [[ " ${missing[*]} " =~ " kubectl " ]]; then
|
|
41
|
+
echo " kubectl:"
|
|
42
|
+
echo " curl -LO \"https://dl.k8s.io/release/\$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\""
|
|
43
|
+
echo " sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl"
|
|
44
|
+
echo ""
|
|
45
|
+
fi
|
|
46
|
+
if [[ " ${missing[*]} " =~ " k3d " ]]; then
|
|
47
|
+
echo " k3d:"
|
|
48
|
+
echo " curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash"
|
|
49
|
+
echo ""
|
|
50
|
+
fi
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
print_success "All dependencies satisfied"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
create_cluster() {
|
|
58
|
+
print_step "Creating k3d cluster: ${CLUSTER_NAME}"
|
|
59
|
+
|
|
60
|
+
# Check if cluster exists
|
|
61
|
+
if k3d cluster list | grep -q "${CLUSTER_NAME}"; then
|
|
62
|
+
print_warning "Cluster ${CLUSTER_NAME} already exists"
|
|
63
|
+
read -p "Delete and recreate? (y/N) " -n 1 -r
|
|
64
|
+
echo
|
|
65
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
66
|
+
k3d cluster delete ${CLUSTER_NAME}
|
|
67
|
+
else
|
|
68
|
+
print_info "Using existing cluster"
|
|
69
|
+
return
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Create cluster with port mappings
|
|
74
|
+
# - 8080:80 for HTTP traffic via Traefik
|
|
75
|
+
# - 8443:443 for HTTPS traffic
|
|
76
|
+
# Note: k3d comes with built-in Traefik, but we deploy our own
|
|
77
|
+
k3d cluster create ${CLUSTER_NAME} \
|
|
78
|
+
--port "8080:80@loadbalancer" \
|
|
79
|
+
--port "8443:443@loadbalancer" \
|
|
80
|
+
--agents 1 \
|
|
81
|
+
--k3s-arg "--disable=traefik@server:0" \
|
|
82
|
+
--wait
|
|
83
|
+
|
|
84
|
+
print_success "Cluster created"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
install_traefik_crds() {
|
|
88
|
+
print_step "Installing Traefik CRDs..."
|
|
89
|
+
|
|
90
|
+
# Install Traefik CRDs from the official Traefik helm chart
|
|
91
|
+
# These are needed for IngressRoute and Middleware resources
|
|
92
|
+
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.2/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml 2>/dev/null || {
|
|
93
|
+
print_warning "Could not fetch Traefik v3 CRDs, trying v2..."
|
|
94
|
+
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.11/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
print_success "Traefik CRDs installed"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
deploy_manifests() {
|
|
101
|
+
print_step "Deploying Kubernetes manifests..."
|
|
102
|
+
|
|
103
|
+
# Install Traefik CRDs first (since we disabled k3d's built-in Traefik)
|
|
104
|
+
install_traefik_crds
|
|
105
|
+
|
|
106
|
+
# Use the local overlay which:
|
|
107
|
+
# - Sets correct namespace (vibecarbon)
|
|
108
|
+
# - Uses local-path storage class instead of hcloud-volumes
|
|
109
|
+
# - Uses nginx placeholder for app image
|
|
110
|
+
# - Includes secrets and configmap
|
|
111
|
+
# - Reduces replicas for local testing
|
|
112
|
+
|
|
113
|
+
print_info "Applying local overlay..."
|
|
114
|
+
|
|
115
|
+
# Apply the local overlay with kustomize
|
|
116
|
+
# Use kustomize build with load restrictor to allow referencing base manifests
|
|
117
|
+
kubectl kustomize "${SCRIPT_DIR}/overlays/local" --load-restrictor=LoadRestrictionsNone | kubectl apply -f -
|
|
118
|
+
|
|
119
|
+
print_success "Manifests deployed"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
wait_for_pods() {
|
|
123
|
+
print_step "Waiting for pods to be ready..."
|
|
124
|
+
|
|
125
|
+
local timeout=300
|
|
126
|
+
local start_time=$(date +%s)
|
|
127
|
+
|
|
128
|
+
# First, wait for any pods to appear
|
|
129
|
+
print_info "Waiting for pods to be created..."
|
|
130
|
+
local pod_count=0
|
|
131
|
+
while [ $pod_count -eq 0 ]; do
|
|
132
|
+
local current_time=$(date +%s)
|
|
133
|
+
local elapsed=$((current_time - start_time))
|
|
134
|
+
|
|
135
|
+
if [ $elapsed -gt 60 ]; then
|
|
136
|
+
print_warning "No pods created after 60s, checking all namespaces..."
|
|
137
|
+
kubectl get pods -A
|
|
138
|
+
break
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
pod_count=$(kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | wc -l)
|
|
142
|
+
if [ $pod_count -eq 0 ]; then
|
|
143
|
+
sleep 2
|
|
144
|
+
fi
|
|
145
|
+
done
|
|
146
|
+
|
|
147
|
+
# Now wait for pods to be ready
|
|
148
|
+
while true; do
|
|
149
|
+
local current_time=$(date +%s)
|
|
150
|
+
local elapsed=$((current_time - start_time))
|
|
151
|
+
|
|
152
|
+
if [ $elapsed -gt $timeout ]; then
|
|
153
|
+
print_error "Timeout waiting for pods"
|
|
154
|
+
echo ""
|
|
155
|
+
echo "Pod Status:"
|
|
156
|
+
kubectl get pods -n ${NAMESPACE} -o wide
|
|
157
|
+
echo ""
|
|
158
|
+
echo "Events:"
|
|
159
|
+
kubectl get events -n ${NAMESPACE} --sort-by='.lastTimestamp' | tail -20
|
|
160
|
+
exit 1
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
local total=$(kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | wc -l)
|
|
164
|
+
local running=$(kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | grep "Running" | wc -l)
|
|
165
|
+
local pending=$(kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | grep -E "Pending|ContainerCreating|Init" | wc -l)
|
|
166
|
+
local failed=$(kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | grep -E "Error|CrashLoopBackOff|ImagePullBackOff|CreateContainerConfigError" | wc -l)
|
|
167
|
+
|
|
168
|
+
if [ "$total" -gt 0 ] && [ "$running" -eq "$total" ]; then
|
|
169
|
+
print_success "All ${total} pods ready"
|
|
170
|
+
break
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
if [ "$failed" -gt 0 ]; then
|
|
174
|
+
print_warning "${failed} pods have errors (will continue waiting...)"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
print_info "Pods: ${running}/${total} running, ${pending} pending (${elapsed}s)"
|
|
178
|
+
sleep 5
|
|
179
|
+
done
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
show_status() {
|
|
183
|
+
print_step "Deployment Status"
|
|
184
|
+
|
|
185
|
+
echo ""
|
|
186
|
+
echo "Pods:"
|
|
187
|
+
kubectl get pods -n ${NAMESPACE} -o wide 2>/dev/null || echo "No pods found"
|
|
188
|
+
|
|
189
|
+
echo ""
|
|
190
|
+
echo "Services:"
|
|
191
|
+
kubectl get svc -n ${NAMESPACE} 2>/dev/null || echo "No services found"
|
|
192
|
+
|
|
193
|
+
echo ""
|
|
194
|
+
echo "PVCs:"
|
|
195
|
+
kubectl get pvc -n ${NAMESPACE} 2>/dev/null || echo "No PVCs found"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
show_access_info() {
|
|
199
|
+
print_step "Access Information"
|
|
200
|
+
|
|
201
|
+
echo ""
|
|
202
|
+
echo -e "${BLUE}Local URLs (no auth required):${NC}"
|
|
203
|
+
echo " App: http://localhost:8080"
|
|
204
|
+
echo " Studio: http://studio.localhost:8080"
|
|
205
|
+
echo " n8n: http://n8n.localhost:8080 (admin / local-n8n-password)"
|
|
206
|
+
echo " Traefik: http://traefik.localhost:8080"
|
|
207
|
+
echo ""
|
|
208
|
+
echo -e "${BLUE}Admin URLs (requires admin role - production-like):${NC}"
|
|
209
|
+
echo " Studio: http://localhost:8080/admin/studio"
|
|
210
|
+
echo " n8n: http://localhost:8080/admin/n8n"
|
|
211
|
+
echo ""
|
|
212
|
+
echo -e "${BLUE}Supabase API (via Kong):${NC}"
|
|
213
|
+
echo " Auth: http://localhost:8080/auth/v1/"
|
|
214
|
+
echo " REST: http://localhost:8080/rest/v1/"
|
|
215
|
+
echo " Realtime: http://localhost:8080/realtime/v1/"
|
|
216
|
+
echo " Storage: http://localhost:8080/storage/v1/"
|
|
217
|
+
echo ""
|
|
218
|
+
echo -e "${BLUE}App API:${NC}"
|
|
219
|
+
echo " Health: http://localhost:8080/api/health"
|
|
220
|
+
echo " Docs: http://localhost:8080/api/docs"
|
|
221
|
+
echo " v1 API: http://localhost:8080/api/v1/..."
|
|
222
|
+
echo ""
|
|
223
|
+
echo -e "${BLUE}Port Forwarding (direct service access):${NC}"
|
|
224
|
+
echo " kubectl port-forward svc/postgres 5432:5432 -n ${NAMESPACE}"
|
|
225
|
+
echo " kubectl port-forward svc/kong 8000:8000 -n ${NAMESPACE}"
|
|
226
|
+
echo " kubectl port-forward svc/studio 3001:3000 -n ${NAMESPACE}"
|
|
227
|
+
echo ""
|
|
228
|
+
echo -e "${BLUE}View logs:${NC}"
|
|
229
|
+
echo " kubectl logs -f statefulset/postgres -n ${NAMESPACE}"
|
|
230
|
+
echo " kubectl logs -f deployment/kong -n ${NAMESPACE}"
|
|
231
|
+
echo " kubectl logs -f deployment/auth -n ${NAMESPACE}"
|
|
232
|
+
echo " kubectl logs -f deployment/app -n ${NAMESPACE}"
|
|
233
|
+
echo ""
|
|
234
|
+
echo -e "${BLUE}Delete cluster when done:${NC}"
|
|
235
|
+
echo " k3d cluster delete ${CLUSTER_NAME}"
|
|
236
|
+
echo " # or: pnpm k8s:local:down"
|
|
237
|
+
echo ""
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
debug_pods() {
|
|
241
|
+
print_step "Debugging pod issues..."
|
|
242
|
+
|
|
243
|
+
echo ""
|
|
244
|
+
echo "Failed/Pending pods:"
|
|
245
|
+
kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | grep -v "Running" || echo "All pods running"
|
|
246
|
+
|
|
247
|
+
echo ""
|
|
248
|
+
echo "Recent events:"
|
|
249
|
+
kubectl get events -n ${NAMESPACE} --sort-by='.lastTimestamp' 2>/dev/null | tail -15
|
|
250
|
+
|
|
251
|
+
local problem_pod=$(kubectl get pods -n ${NAMESPACE} --no-headers 2>/dev/null | grep -v "Running" | head -1 | awk '{print $1}')
|
|
252
|
+
if [ -n "$problem_pod" ]; then
|
|
253
|
+
echo ""
|
|
254
|
+
echo "Details for problematic pod: $problem_pod"
|
|
255
|
+
kubectl describe pod "$problem_pod" -n ${NAMESPACE} 2>/dev/null | tail -30
|
|
256
|
+
fi
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
cleanup() {
|
|
260
|
+
print_step "Cleaning up local cluster..."
|
|
261
|
+
k3d cluster delete ${CLUSTER_NAME}
|
|
262
|
+
|
|
263
|
+
# Also delete k3d docker volumes to ensure fresh state
|
|
264
|
+
docker volume rm k3d-${CLUSTER_NAME}-images 2>/dev/null || true
|
|
265
|
+
|
|
266
|
+
print_success "Cluster deleted"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# Parse arguments
|
|
270
|
+
case "${1:-}" in
|
|
271
|
+
"delete"|"cleanup"|"down")
|
|
272
|
+
cleanup
|
|
273
|
+
exit 0
|
|
274
|
+
;;
|
|
275
|
+
"status")
|
|
276
|
+
kubectl config use-context k3d-${CLUSTER_NAME} 2>/dev/null || true
|
|
277
|
+
show_status
|
|
278
|
+
exit 0
|
|
279
|
+
;;
|
|
280
|
+
"logs")
|
|
281
|
+
kubectl config use-context k3d-${CLUSTER_NAME} 2>/dev/null || true
|
|
282
|
+
kubectl logs -f "${2:-deployment/app}" -n ${NAMESPACE}
|
|
283
|
+
exit 0
|
|
284
|
+
;;
|
|
285
|
+
"debug")
|
|
286
|
+
kubectl config use-context k3d-${CLUSTER_NAME} 2>/dev/null || true
|
|
287
|
+
debug_pods
|
|
288
|
+
exit 0
|
|
289
|
+
;;
|
|
290
|
+
"help"|"-h"|"--help")
|
|
291
|
+
echo "Usage: $0 [command]"
|
|
292
|
+
echo ""
|
|
293
|
+
echo "Commands:"
|
|
294
|
+
echo " (none) Create cluster and deploy"
|
|
295
|
+
echo " status Show pod/service status"
|
|
296
|
+
echo " logs Follow logs (default: deployment/app)"
|
|
297
|
+
echo " debug Debug failing pods"
|
|
298
|
+
echo " delete Delete the local cluster"
|
|
299
|
+
echo " help Show this help"
|
|
300
|
+
exit 0
|
|
301
|
+
;;
|
|
302
|
+
esac
|
|
303
|
+
|
|
304
|
+
# Main flow
|
|
305
|
+
echo -e "${BLUE}"
|
|
306
|
+
echo "╔════════════════════════════════════════════════════════════╗"
|
|
307
|
+
echo "║ Vibecarbon Local Kubernetes Testing ║"
|
|
308
|
+
echo "╚════════════════════════════════════════════════════════════╝"
|
|
309
|
+
echo -e "${NC}"
|
|
310
|
+
|
|
311
|
+
check_dependencies
|
|
312
|
+
create_cluster
|
|
313
|
+
deploy_manifests
|
|
314
|
+
wait_for_pods
|
|
315
|
+
show_status
|
|
316
|
+
show_access_info
|
|
317
|
+
|
|
318
|
+
print_success "Local k8s environment ready!"
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Supabase Helm chart values (supabase-community/supabase-kubernetes).
|
|
2
|
+
#
|
|
3
|
+
# Chart schema reference:
|
|
4
|
+
# helm show values supabase-community/supabase
|
|
5
|
+
#
|
|
6
|
+
# Top-level keys recognised by this chart: secret, deployment, image,
|
|
7
|
+
# environment, persistence, service, autoscaling, ingress, migration,
|
|
8
|
+
# bigQuery. Component toggles live under `deployment.<comp>.enabled` —
|
|
9
|
+
# top-level keys like `functions:` or `analytics:` are silently ignored.
|
|
10
|
+
#
|
|
11
|
+
# Placeholders like {{DOMAIN}} are substituted at deploy time in
|
|
12
|
+
# installSupabase — do not hand-edit after deploy.
|
|
13
|
+
#
|
|
14
|
+
# We intentionally do NOT set fullnameOverride/nameOverride: the chart's
|
|
15
|
+
# generated resources ignore the top-level override, so attempting to
|
|
16
|
+
# rename creates a mismatch (ConfigMap URLs stop resolving). Deploy code
|
|
17
|
+
# and the production overlay reference the chart-generated names directly
|
|
18
|
+
# (`supabase-supabase-<component>`).
|
|
19
|
+
|
|
20
|
+
# Shared secret wiring — every component reads from the vibecarbon-secrets
|
|
21
|
+
# Secret created by applyVibecarbonSecrets before helm install runs. The
|
|
22
|
+
# chart's `secretRef` + `secretRefKey` map the chart's internal field
|
|
23
|
+
# names to keys in our secret; plain `secretName: vibecarbon-secrets`
|
|
24
|
+
# does NOT work — the chart silently falls back to generating its own
|
|
25
|
+
# supabase-jwt/supabase-dashboard secrets with random values (hit on
|
|
26
|
+
# 2026-04-19 run #3 — Kong rejected our app's SERVICE_ROLE_KEY because
|
|
27
|
+
# it was signed with our JWT_SECRET while the chart-generated
|
|
28
|
+
# supabase-jwt.secret was a different string).
|
|
29
|
+
secret:
|
|
30
|
+
jwt:
|
|
31
|
+
secretRef: vibecarbon-secrets
|
|
32
|
+
secretRefKey:
|
|
33
|
+
anonKey: ANON_KEY
|
|
34
|
+
serviceKey: SERVICE_ROLE_KEY
|
|
35
|
+
secret: JWT_SECRET
|
|
36
|
+
dashboard:
|
|
37
|
+
# Inline values — dashboard.openAiApiKey is chart-required and we don't
|
|
38
|
+
# ship one in vibecarbon-secrets. Using secretRef here would force the
|
|
39
|
+
# chart to read EVERY dashboard field from the secret (including
|
|
40
|
+
# openAiApiKey), and kubelet would fail the studio StatefulSet with
|
|
41
|
+
# "couldn't find key openAiApiKey in Secret vibecarbon/vibecarbon-secrets".
|
|
42
|
+
# Inline values are rendered into the chart's own supabase-dashboard
|
|
43
|
+
# secret at install time — still secure, just not deduped into ours.
|
|
44
|
+
username: "{{ADMIN_EMAIL}}"
|
|
45
|
+
password: "{{ADMIN_PASSWORD}}"
|
|
46
|
+
openAiApiKey: ""
|
|
47
|
+
db:
|
|
48
|
+
secretRef: vibecarbon-secrets
|
|
49
|
+
# When secretRef is set, the chart uses secret keys for every db.* value
|
|
50
|
+
# (inline values are ignored). Maps the chart's lowercase field names to
|
|
51
|
+
# the uppercase keys vibecarbon-secrets ships (DB_NAME defaulted to
|
|
52
|
+
# "postgres" by applyVibecarbonSecrets).
|
|
53
|
+
secretRefKey:
|
|
54
|
+
password: DB_PASSWORD
|
|
55
|
+
database: DB_NAME
|
|
56
|
+
|
|
57
|
+
# Component enable toggles — chart's path is deployment.<comp>.enabled.
|
|
58
|
+
# Top-level `functions:`/`analytics:`/`vector:` are silently ignored
|
|
59
|
+
# (was the bug behind the 2026-04-26 e2e helm-wait timeout).
|
|
60
|
+
deployment:
|
|
61
|
+
functions:
|
|
62
|
+
# Chart's edge-runtime expects a function source at /home/deno/functions.
|
|
63
|
+
# The template doesn't ship a default `main/index.ts`, so the chart's
|
|
64
|
+
# default deployment fetches `https://deno.land/x/jose@v4.14.4/index.ts`
|
|
65
|
+
# at boot — and the network policy + missing function source produce
|
|
66
|
+
# a CrashLoopBackOff that holds up `helm --wait`. Re-enable once a
|
|
67
|
+
# project actually defines a function.
|
|
68
|
+
enabled: false
|
|
69
|
+
analytics:
|
|
70
|
+
# Logflare — disable until we need it. Adds ClickHouse + BigQuery deps.
|
|
71
|
+
enabled: false
|
|
72
|
+
vector:
|
|
73
|
+
# Log-shipper — disable; conflicts with the optional observability stack.
|
|
74
|
+
enabled: false
|
|
75
|
+
db:
|
|
76
|
+
# Pin postgres to the dedicated supabase node (taint applied by Pulumi).
|
|
77
|
+
nodeSelector:
|
|
78
|
+
dedicated: supabase
|
|
79
|
+
tolerations:
|
|
80
|
+
- key: dedicated
|
|
81
|
+
operator: Equal
|
|
82
|
+
value: supabase
|
|
83
|
+
effect: NoSchedule
|
|
84
|
+
|
|
85
|
+
# Per-component environment — chart's path is environment.<comp>.
|
|
86
|
+
environment:
|
|
87
|
+
auth:
|
|
88
|
+
API_EXTERNAL_URL: https://{{DOMAIN}}
|
|
89
|
+
GOTRUE_SITE_URL: https://{{DOMAIN}}
|
|
90
|
+
GOTRUE_EXTERNAL_EMAIL_ENABLED: "true"
|
|
91
|
+
# Auto-confirm signups: the chart doesn't ship an SMTP provider, so
|
|
92
|
+
# leaving AUTOCONFIRM=false makes every signup return 500 "Error
|
|
93
|
+
# sending confirmation email" (observed 2026-04-26 batch run #3 —
|
|
94
|
+
# k8s verify-deploy failed at the auth_signup check). Real
|
|
95
|
+
# production deployments override this to false once they wire up
|
|
96
|
+
# SMTP (configure SMTP_* env in the auth section). Default-true is
|
|
97
|
+
# the right call for an out-of-the-box deploy that doesn't have a
|
|
98
|
+
# mail provider.
|
|
99
|
+
GOTRUE_MAILER_AUTOCONFIRM: "true"
|
|
100
|
+
realtime:
|
|
101
|
+
DB_NAME: postgres
|
|
102
|
+
storage:
|
|
103
|
+
STORAGE_BACKEND: s3
|
|
104
|
+
imgproxy:
|
|
105
|
+
IMGPROXY_USE_S3: "true"
|
|
106
|
+
studio:
|
|
107
|
+
SUPABASE_PUBLIC_URL: https://{{DOMAIN}}
|
|
108
|
+
STUDIO_DEFAULT_ORGANIZATION: "{{PROJECT_NAME}}"
|
|
109
|
+
# Kong reads its declarative config from /usr/local/kong/kong.yml — written
|
|
110
|
+
# by the chart's kong-entrypoint.sh after env substitution. The chart's
|
|
111
|
+
# default `KONG_DECLARATIVE_CONFIG` already points at this path, so leave
|
|
112
|
+
# it un-overridden. (Setting it to `/usr/local/kong/declarative/kong.yml`
|
|
113
|
+
# — which doesn't exist — was the cause of the 2026-04-26 e2e kong
|
|
114
|
+
# CrashLoopBackOff.)
|
|
115
|
+
|
|
116
|
+
# We front Kong with our own Traefik IngressRoute (k8s/base/traefik) — the
|
|
117
|
+
# chart's ingress object is off so we don't end up with two ingresses
|
|
118
|
+
# pointing at the same Service.
|
|
119
|
+
ingress:
|
|
120
|
+
enabled: false
|
|
121
|
+
|
|
122
|
+
# Persistence:
|
|
123
|
+
# - db: bump default 5Gi → 10Gi.
|
|
124
|
+
# - functions: disable PVC creation. The chart's persistence.functions PVC
|
|
125
|
+
# is independent of deployment.functions.enabled — without this, studio
|
|
126
|
+
# stays Pending forever waiting for a `supabase-functions` PVC that
|
|
127
|
+
# persistence.yaml gates on `deployment.functions.enabled` (off here).
|
|
128
|
+
persistence:
|
|
129
|
+
db:
|
|
130
|
+
enabled: true
|
|
131
|
+
size: 10Gi
|
|
132
|
+
functions:
|
|
133
|
+
enabled: false
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vibecarbon-template",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.32.1",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev:init": "node scripts/dev-init.js",
|
|
9
|
+
"dev:start": "node scripts/docker-up.js && pnpm db:migrate && exec node scripts/dev.js",
|
|
10
|
+
"dev:start:all": "node scripts/docker-up.js --all && pnpm db:migrate && exec node scripts/dev.js",
|
|
11
|
+
"dev:stop": "node scripts/docker-down.js",
|
|
12
|
+
"dev:stop:all": "node scripts/docker-down.js --all",
|
|
13
|
+
"dev:reset": "node scripts/docker-down.js -v --rmi",
|
|
14
|
+
"dev:reset:all": "node scripts/docker-down.js -v --rmi --all",
|
|
15
|
+
"dev": "exec node scripts/dev.js",
|
|
16
|
+
"dev:server": "tsx watch --env-file=.env.local src/server/index.ts",
|
|
17
|
+
"dev:client": "vite",
|
|
18
|
+
"build": "pnpm build:client && pnpm build:server",
|
|
19
|
+
"build:client": "vite build && tsx scripts/generate-sitemap.ts && tsx scripts/generate-rss.ts",
|
|
20
|
+
"build:server": "esbuild src/server/index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/server/index.js --external:pino --external:pino-pretty --banner:js=\"import{createRequire as __cr}from'module';const require=__cr(import.meta.url);\"",
|
|
21
|
+
"start": "node dist/server/index.js",
|
|
22
|
+
"lint": "biome check .",
|
|
23
|
+
"lint:fix": "biome check --write .",
|
|
24
|
+
"format": "biome format --write .",
|
|
25
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.server.json --noEmit",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"test:unit": "vitest run --project unit",
|
|
29
|
+
"test:component": "vitest run --project component",
|
|
30
|
+
"test:integration": "vitest run --project integration",
|
|
31
|
+
"test:coverage": "vitest run --coverage",
|
|
32
|
+
"test:prepush": "pnpm lint && pnpm test:unit && pnpm test:component && pnpm test:integration",
|
|
33
|
+
"docker:up": "node scripts/docker-up.js",
|
|
34
|
+
"docker:up:all": "node scripts/docker-up.js --all",
|
|
35
|
+
"docker:down": "node scripts/docker-down.js",
|
|
36
|
+
"docker:down:all": "node scripts/docker-down.js --all",
|
|
37
|
+
"docker:logs": "node scripts/docker-logs.js",
|
|
38
|
+
"docker:reset": "node scripts/docker-down.js -v --rmi",
|
|
39
|
+
"docker:reset:all": "node scripts/docker-down.js -v --rmi --all",
|
|
40
|
+
"docker:prod:up": "node scripts/docker-up.js --prod",
|
|
41
|
+
"docker:prod:down": "node scripts/docker-down.js --prod",
|
|
42
|
+
"db:migrate": "docker compose exec db bash -c 'for f in /migrations/*.sql; do psql -U postgres -d postgres -f \"$f\" || exit 1; done' && docker compose exec db psql -U postgres -d postgres -f /tmp/super-admin.sql",
|
|
43
|
+
"db:reset": "node scripts/docker-down.js -v && node scripts/docker-up.js",
|
|
44
|
+
"k8s:apply": "node scripts/k8s-apply.js",
|
|
45
|
+
"k8s:delete": "node scripts/k8s-delete.js",
|
|
46
|
+
"k8s:local:up": "./k8s/test-local.sh",
|
|
47
|
+
"k8s:local:status": "./k8s/test-local.sh status",
|
|
48
|
+
"k8s:local:debug": "./k8s/test-local.sh debug",
|
|
49
|
+
"k8s:local:down": "./k8s/test-local.sh delete",
|
|
50
|
+
"stripe:listen": "stripe listen --forward-to localhost:3000/api/webhooks/stripe"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@base-ui/react": "^1.3.0",
|
|
54
|
+
"@fontsource-variable/noto-sans": "^5.2.10",
|
|
55
|
+
"@fontsource/jetbrains-mono": "^5.2.8",
|
|
56
|
+
"@hono/node-server": "^1.19.13",
|
|
57
|
+
"@hono/zod-openapi": "^1.2.3",
|
|
58
|
+
"@hookform/resolvers": "^5.2.2",
|
|
59
|
+
"@mdx-js/react": "^3.1.1",
|
|
60
|
+
"@mdx-js/rollup": "^3.1.1",
|
|
61
|
+
"@scalar/api-reference-react": "^0.9.15",
|
|
62
|
+
"@supabase/ssr": "^0.9.0",
|
|
63
|
+
"@supabase/supabase-js": "^2.100.0",
|
|
64
|
+
"@tanstack/react-query": "^5.95.2",
|
|
65
|
+
"class-variance-authority": "^0.7.1",
|
|
66
|
+
"clsx": "^2.1.1",
|
|
67
|
+
"cmdk": "^1.1.1",
|
|
68
|
+
"date-fns": "^4.1.0",
|
|
69
|
+
"embla-carousel-react": "^8.6.0",
|
|
70
|
+
"framer-motion": "^12.38.0",
|
|
71
|
+
"hono": "^4.12.16",
|
|
72
|
+
"i18next": "^25.10.9",
|
|
73
|
+
"i18next-browser-languagedetector": "^8.2.1",
|
|
74
|
+
"input-otp": "^1.4.2",
|
|
75
|
+
"ioredis": "^5.10.1",
|
|
76
|
+
"lenis": "^1.3.20",
|
|
77
|
+
"lucide-react": "^1.7.0",
|
|
78
|
+
"next-themes": "^0.4.6",
|
|
79
|
+
"nodemailer": "^8.0.5",
|
|
80
|
+
"pino": "^10.3.1",
|
|
81
|
+
"react": "^19.2.4",
|
|
82
|
+
"react-day-picker": "^9.14.0",
|
|
83
|
+
"react-dom": "^19.2.4",
|
|
84
|
+
"react-hook-form": "^7.72.0",
|
|
85
|
+
"react-i18next": "^16.6.6",
|
|
86
|
+
"react-resizable-panels": "^4.7.6",
|
|
87
|
+
"react-router-dom": "^7.13.2",
|
|
88
|
+
"recharts": "^3.8.1",
|
|
89
|
+
"rehype-autolink-headings": "^7.1.0",
|
|
90
|
+
"rehype-slug": "^6.0.0",
|
|
91
|
+
"remark-frontmatter": "^5.0.0",
|
|
92
|
+
"remark-mdx-frontmatter": "^5.2.0",
|
|
93
|
+
"sonner": "^2.0.7",
|
|
94
|
+
"stripe": "^20.4.1",
|
|
95
|
+
"tailwind-merge": "^3.5.0",
|
|
96
|
+
"vaul": "^1.1.2",
|
|
97
|
+
"zod": "^4.3.6"
|
|
98
|
+
},
|
|
99
|
+
"pnpm": {
|
|
100
|
+
"onlyBuiltDependencies": [
|
|
101
|
+
"esbuild",
|
|
102
|
+
"msw"
|
|
103
|
+
],
|
|
104
|
+
"allowedDeprecatedVersions": {
|
|
105
|
+
"node-domexception": "1.0.0"
|
|
106
|
+
},
|
|
107
|
+
"overrides": {
|
|
108
|
+
"dompurify": "^3.4.0",
|
|
109
|
+
"unhead": ">=2.1.13",
|
|
110
|
+
"defu": ">=6.1.5",
|
|
111
|
+
"path-to-regexp@>=8.0.0": ">=8.4.0",
|
|
112
|
+
"postcss@<8.5.10": ">=8.5.10",
|
|
113
|
+
"ip-address@<10.1.1": ">=10.1.1",
|
|
114
|
+
"fast-uri@<3.1.2": ">=3.1.2",
|
|
115
|
+
"protobufjs@<7.5.6": "^7.5.6",
|
|
116
|
+
"@protobufjs/utf8@<1.1.1": "^1.1.1"
|
|
117
|
+
},
|
|
118
|
+
"peerDependencyRules": {
|
|
119
|
+
"allowedVersions": {
|
|
120
|
+
"typescript": "*"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"devDependencies": {
|
|
125
|
+
"@biomejs/biome": "^2.4.9",
|
|
126
|
+
"@hono/swagger-ui": "^0.6.1",
|
|
127
|
+
"@scalar/hono-api-reference": "^0.10.5",
|
|
128
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
129
|
+
"@tailwindcss/vite": "^4.2.2",
|
|
130
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
131
|
+
"@testing-library/react": "^16.3.0",
|
|
132
|
+
"@testing-library/user-event": "^14.6.1",
|
|
133
|
+
"@types/node": "^25.5.0",
|
|
134
|
+
"@types/nodemailer": "^7.0.11",
|
|
135
|
+
"@types/pg": "^8.20.0",
|
|
136
|
+
"@types/react": "^19.2.14",
|
|
137
|
+
"@types/react-dom": "^19.2.3",
|
|
138
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
139
|
+
"@vitest/coverage-v8": "^4.1.1",
|
|
140
|
+
"concurrently": "^9.2.1",
|
|
141
|
+
"esbuild": "^0.27.4",
|
|
142
|
+
"jsdom": "^26.1.0",
|
|
143
|
+
"pg": "^8.20.0",
|
|
144
|
+
"pino-pretty": "^13.1.3",
|
|
145
|
+
"remark-gfm": "^4.0.1",
|
|
146
|
+
"shadcn": "^4.1.0",
|
|
147
|
+
"tailwindcss": "^4.2.2",
|
|
148
|
+
"tsx": "^4.21.0",
|
|
149
|
+
"tw-animate-css": "^1.4.0",
|
|
150
|
+
"typescript": "^6.0.2",
|
|
151
|
+
"vite": "^8.0.5",
|
|
152
|
+
"vitest": "^4.1.1"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
# Vibecarbon Runtime Base Image
|
|
3
|
+
# This image contains the pre-installed node_modules for the Vibecarbon template.
|
|
4
|
+
# Using this as a base for your app image reduces build times from minutes to seconds.
|
|
5
|
+
|
|
6
|
+
FROM node:22-alpine3.23 AS base
|
|
7
|
+
|
|
8
|
+
RUN npm install -g pnpm@9
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
# Pre-install dependencies
|
|
12
|
+
COPY package.json pnpm-lock.yaml ./
|
|
13
|
+
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
|
|
14
|
+
pnpm install --frozen-lockfile
|
|
15
|
+
|
|
16
|
+
# This image is meant to be a base, so we stop here.
|
|
17
|
+
# The app build will continue FROM this image.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Sign HS256 JWTs from a shared dev secret. Used by dev-init.js and
|
|
3
|
+
// generate-dev-configs.sh to produce ANON / SERVICE_ROLE keys at runtime
|
|
4
|
+
// so the carbon template doesn't ship literal JWT strings — those would
|
|
5
|
+
// trip every secret-scanner that looked at the repo (GitHub's, ours,
|
|
6
|
+
// gitleaks, ...). The values are still deterministic for a given
|
|
7
|
+
// JWT_SECRET, so dev workflows that depend on stable keys keep working
|
|
8
|
+
// across runs.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// node scripts/_dev-jwt.mjs <role>
|
|
12
|
+
//
|
|
13
|
+
// Where <role> is `anon` or `service_role`. JWT_SECRET must be set in
|
|
14
|
+
// the environment. Prints the JWT to stdout (no newline) on success;
|
|
15
|
+
// non-zero exit on bad input.
|
|
16
|
+
|
|
17
|
+
import { createHmac } from 'node:crypto';
|
|
18
|
+
|
|
19
|
+
const role = process.argv[2];
|
|
20
|
+
if (role !== 'anon' && role !== 'service_role') {
|
|
21
|
+
process.stderr.write('usage: _dev-jwt.mjs (anon|service_role)\n');
|
|
22
|
+
process.exit(2);
|
|
23
|
+
}
|
|
24
|
+
const secret = process.env.JWT_SECRET;
|
|
25
|
+
if (!secret) {
|
|
26
|
+
process.stderr.write('JWT_SECRET must be set in the environment\n');
|
|
27
|
+
process.exit(2);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const b64url = (buf) =>
|
|
31
|
+
Buffer.from(buf).toString('base64').replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
32
|
+
|
|
33
|
+
const header = b64url(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
|
|
34
|
+
const payload = b64url(
|
|
35
|
+
JSON.stringify({
|
|
36
|
+
iss: 'supabase-demo',
|
|
37
|
+
role,
|
|
38
|
+
// 100-year expiry: dev tokens shouldn't churn during a single
|
|
39
|
+
// engineer's career. Production tokens are signed elsewhere with
|
|
40
|
+
// shorter lifetimes; this is local-dev only.
|
|
41
|
+
exp: Math.floor(Date.now() / 1000) + 100 * 365 * 24 * 60 * 60,
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
const sig = b64url(createHmac('sha256', secret).update(`${header}.${payload}`).digest());
|
|
45
|
+
process.stdout.write(`${header}.${payload}.${sig}`);
|