From 58ea0243c473128ae93be427587f4363b29e6085 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Thu, 6 Aug 2026 01:49:27 -0700 Subject: [PATCH 1/2] feat(deploy): wire Stripe credentials through ExternalSecrets, gated off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the three env vars billing needs — STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_ID — sourced from Secret Manager via the existing api-keys ExternalSecret. The remote refs are gated behind `billing.enabled` (default false), following the errorTracking precedent directly above them. That gate is not stylistic: ESO rejects the ENTIRE ExternalSecret when any referenced remote key is missing, so referencing a Stripe secret before it exists would stop api-keys syncing altogether and take JWT, LiteLLM and every OAuth credential down with it. An empty secret container is not enough either — `describe` succeeds while `versions list` is empty, and ESO needs a version. The deployment env vars are deliberately NOT gated. Every ref is `optional: true`, so with the flag off the keys are simply absent and billingService.STRIPE_ENABLED() reports false, which makes /api/billing/* return 503 billing_not_configured rather than half-working. Keeping them unconditional means enabling billing is a one-line values change with no template edit to forget. The price id is not secret but lives in Secret Manager anyway, so swapping test credentials for live ones is `gcloud secrets versions add` rather than a chart edit and redeploy. Rendered both ways: flag off yields zero stripe refs with jwt-secret intact; flag on yields all three. helm lint clean in both states. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8 --- .../templates/core/backend-deployment.yaml | 29 +++++++++++++++++++ .../commonly/templates/secrets/api-keys.yaml | 17 +++++++++++ k8s/helm/commonly/values-dev.yaml | 7 +++++ k8s/helm/commonly/values.yaml | 16 ++++++++++ 4 files changed, 69 insertions(+) diff --git a/k8s/helm/commonly/templates/core/backend-deployment.yaml b/k8s/helm/commonly/templates/core/backend-deployment.yaml index 29633c70..6e39e127 100644 --- a/k8s/helm/commonly/templates/core/backend-deployment.yaml +++ b/k8s/helm/commonly/templates/core/backend-deployment.yaml @@ -356,6 +356,35 @@ spec: key: deepgram-api-key optional: true + # Billing (Stripe). Deliberately NOT gated on billing.enabled — every + # ref is `optional: true`, so when the flag is off the keys are simply + # absent and the backend reports billing_not_configured. Keeping these + # unconditional means turning billing on is a one-line values change + # with no deployment-template edit to forget. + # + # STRIPE_SECRET_KEY is the flag the code actually reads: billingService + # .STRIPE_ENABLED() is Boolean(process.env.STRIPE_SECRET_KEY), so an + # absent key disables checkout, portal AND the webhook together. There + # is no state where we accept a webhook we cannot verify. + - name: STRIPE_SECRET_KEY + valueFrom: + secretKeyRef: + name: api-keys + key: stripe-secret-key + optional: true + - name: STRIPE_WEBHOOK_SECRET + valueFrom: + secretKeyRef: + name: api-keys + key: stripe-webhook-secret + optional: true + - name: STRIPE_PRICE_ID + valueFrom: + secretKeyRef: + name: api-keys + key: stripe-price-id + optional: true + # LiteLLM Configuration - name: LITELLM_MASTER_KEY valueFrom: diff --git a/k8s/helm/commonly/templates/secrets/api-keys.yaml b/k8s/helm/commonly/templates/secrets/api-keys.yaml index 0c4b8eec..c1b74889 100644 --- a/k8s/helm/commonly/templates/secrets/api-keys.yaml +++ b/k8s/helm/commonly/templates/secrets/api-keys.yaml @@ -43,6 +43,23 @@ spec: key: commonly-dev-sentry-dsn {{- end }} + {{- if .Values.billing.enabled }} + # Billing (Stripe). Same gating rule as error tracking above: ESO rejects the + # entire ExternalSecret when any referenced remote key is missing, so all + # three must exist WITH a version in Secret Manager before this flag flips. + # The price id is not secret, but it lives here so swapping test -> live is a + # `gcloud secrets versions add`, not a chart edit and redeploy. + - secretKey: stripe-secret-key + remoteRef: + key: commonly-dev-stripe-secret-key + - secretKey: stripe-webhook-secret + remoteRef: + key: commonly-dev-stripe-webhook-secret + - secretKey: stripe-price-id + remoteRef: + key: commonly-dev-stripe-price-id + {{- end }} + # Discord Integration - secretKey: discord-bot-token remoteRef: diff --git a/k8s/helm/commonly/values-dev.yaml b/k8s/helm/commonly/values-dev.yaml index 9596557c..9b4d153e 100644 --- a/k8s/helm/commonly/values-dev.yaml +++ b/k8s/helm/commonly/values-dev.yaml @@ -8,6 +8,13 @@ global: errorTracking: enabled: true +# Flip to true only once all three commonly-dev-stripe-* secrets have a version +# in Secret Manager — verify with `gcloud secrets versions list `. An +# empty secret container passes `describe` and still breaks the whole api-keys +# ExternalSecret. See the note in values.yaml. +billing: + enabled: false + agentProvisioning: enabled: true createWorkspacePvc: false diff --git a/k8s/helm/commonly/values.yaml b/k8s/helm/commonly/values.yaml index 5014e8fb..9be16eb7 100644 --- a/k8s/helm/commonly/values.yaml +++ b/k8s/helm/commonly/values.yaml @@ -18,6 +18,22 @@ createNamespace: true errorTracking: enabled: false +# Paid tier (Stripe). Opt-in, and OFF by default so self-hosted deployments +# never reach for billing credentials they do not have. +# +# Flipping this to true makes the api-keys ExternalSecret reference three +# remote keys — commonly-dev-stripe-{secret-key,webhook-secret,price-id}. ESO +# rejects the WHOLE ExternalSecret when any referenced key is missing, so every +# other secret in it (JWT, LiteLLM, OAuth) would stop syncing too. Populate all +# three in Secret Manager BEFORE enabling. `gcloud secrets versions list ` +# must show at least one version for each; an empty secret container is not +# enough. +# +# The backend degrades honestly when this is off: STRIPE_SECRET_KEY is absent, +# so /api/billing/* returns 503 billing_not_configured rather than half-working. +billing: + enabled: false + # Agent provisioning (RBAC + PVCs) agentProvisioning: enabled: true From e15848806608f1c085e7d7e52fc383ed4ad957b9 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Thu, 6 Aug 2026 02:34:38 -0700 Subject: [PATCH 2/2] =?UTF-8?q?feat(deploy):=20enable=20billing=20on=20dev?= =?UTF-8?q?=20=E2=80=94=20all=20three=20Stripe=20secrets=20verified=20pres?= =?UTF-8?q?ent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flipped only after confirming each of commonly-dev-stripe-{secret-key, webhook-secret,price-id} has an ENABLED version, and that each value carries the expected prefix with no trailing whitespace. The whitespace check is not ceremony: a newline captured by `echo` instead of `printf '%s'` survives into the secret and surfaces later as a webhook signature rejection that looks nothing like its cause. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8 --- k8s/helm/commonly/values-dev.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/k8s/helm/commonly/values-dev.yaml b/k8s/helm/commonly/values-dev.yaml index 9b4d153e..b8682377 100644 --- a/k8s/helm/commonly/values-dev.yaml +++ b/k8s/helm/commonly/values-dev.yaml @@ -8,12 +8,12 @@ global: errorTracking: enabled: true -# Flip to true only once all three commonly-dev-stripe-* secrets have a version -# in Secret Manager — verify with `gcloud secrets versions list `. An -# empty secret container passes `describe` and still breaks the whole api-keys -# ExternalSecret. See the note in values.yaml. +# All three commonly-dev-stripe-* secrets verified present with an ENABLED +# version on 2026-08-06 before this was flipped. Do not set true anywhere the +# secrets are absent: an empty secret container passes `describe` and still +# breaks the whole api-keys ExternalSecret. See the note in values.yaml. billing: - enabled: false + enabled: true agentProvisioning: enabled: true