Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ charts/cloudnative-supabase/crds # chart copy of generated CRD
durable resources before validating external input.
2. Validate the externally managed `projectCredentialsSecret` bundle.
3. Create-once implementation Secrets (database role passwords, GoTrue
fallback, optional email hook and PowerSync credentials).
fallback, optional email hook, and PowerSync database/API credentials).
4. Validate immutable recovery intent, then create init SQL and public JWKS
ConfigMaps.
5. Reconcile independent recovery and steady-state backup resources.
Expand All @@ -37,7 +37,9 @@ charts/cloudnative-supabase/crds # chart copy of generated CRD
fields outside that projection, rejecting bootstrap mutation and storage
shrink.
7. Wait for ready database instances.
8. Reconcile Auth, REST, Studio, Meta, Envoy, then optional PowerSync.
8. Reconcile Auth, REST, Studio, Meta, Envoy, then optional PowerSync. The
PowerSync config hash covers the generated native metrics and diagnostics
configuration, while token values remain outside hashes.

When backup and recovery are both enabled, their deterministic ObjectStore
names and configured destination paths must differ. A credentials Secret may be
Expand Down Expand Up @@ -69,7 +71,23 @@ secret and normalized Auth issuer. Envoy receives opaque keys and internal role
tokens. Studio receives opaque keys and the internal role-token variables it
supports. PostgREST receives only public JWKS. PowerSync uses the Auth JWKS URL,
audience `authenticated`, and disabled Supabase HMAC mode; it has no JWT
secret environment variable.
secret environment variable. The default PowerSync image is
`journeyapps/powersync-service:1.21.0`; its generated config enables native
Prometheus metrics on named port 9464 and loads administrative API tokens from
`PS_POWERSYNC_API_TOKEN`.

The operator creates the PowerSync API token only when PowerSync is enabled, in
the same-namespace create-once Secret `<project>-powersync-api-token` under key
`token`. It is runtime-owned, independent of the five external project
credentials, and never appears in ConfigMaps, status, annotations, or logs.
Existing non-empty values are retained; invalid or foreign-controlled
same-name Secrets fail safely without adoption. Explicit token rotation is an
operator Secret operation and requires restarting the API/replication processes
that consume it; a monitoring workload can use its mounted Secret without
Secret-value API permission. PowerSync replication uses a Recreate Deployment
strategy; the client-facing API retains its rolling/default strategy and
existing availability probes. The configured image override feeds API,
replication, and compaction.

`publishableKey` and `secretKey` use Supabase's canonical self-hosted opaque-key
format: their role-specific prefix is followed by exactly 22 unpadded
Expand Down
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ The Envoy assets are adapted from the official self-hosted Supabase assets at
upstream commit
[`95ca3024398080ff18c9abcd1c6c8beae73fd9e1`](https://github.com/supabase/supabase/commit/95ca3024398080ff18c9abcd1c6c8beae73fd9e1).
Pinned images are Envoy `envoyproxy/envoy:v1.39.0`, GoTrue
`supabase/gotrue:v2.189.0`, and PostgREST `postgrest/postgrest:v14.12`.
`supabase/gotrue:v2.189.0`, PostgREST `postgrest/postgrest:v14.12`, and
PowerSync `journeyapps/powersync-service:1.21.0` when PowerSync is enabled.

## Project credentials

Expand Down Expand Up @@ -162,6 +163,58 @@ operator defaults. `auth.goTrueEnv` remains available for provider settings,
but JWT keys, fallback secret, key ID, issuer, audience, lifetime, valid
methods, and role settings are operator-owned and cannot be overridden.

## Optional PowerSync profile

Adding `spec.powersync` enables the managed PowerSync 1.21.0 profile. The
default image is used by the API Deployment, the singleton replication
Deployment, and the optional compaction CronJob. An explicit
`spec.powersync.image` override remains available and is applied consistently
to all three managed PowerSync commands.

The API and replication containers expose a named `metrics` port on TCP 9464,
and the generated PowerSync configuration sets
`telemetry.prometheus_port: 9464`. The API Service exposes both HTTP 8080 and
metrics 9464; the replication pod exposes its named metrics port for a pod
scrape. Both roles serve `GET /metrics`. Kubernetes startup, readiness, and
liveness probes use filesystem `Exec` actions; the API's existing HTTP health
route remains available to external monitors. Replication uses a Recreate
rollout so two replication processes do not overlap during replacement.

The operator creates one independent, create-once Secret in the project
namespace: `<project>-powersync-api-token`, with the required key `token`.
PowerSync loads that value through a `secretKeyRef`; it is not part of the
five-field project credential bundle and is never copied into a ConfigMap or
project status. Monitoring in the same namespace can mount this Secret and
use the token as `Authorization: Bearer <token>` for the authenticated
`POST /api/admin/v1/diagnostics` endpoint. A pre-existing non-empty token is
preserved. A missing or invalid key blocks the PowerSync secret phase without
printing the value, and a same-name Secret controlled by another resource is
not adopted.

To rotate the internal token, replace only the `token` key using the approved
Secret management path. The operator does not rotate it during reconciliation;
restart or roll out the API and replication processes (and any compaction
process that is running) so they reload the value. A monitor only needs access
to its mounted Secret and does not need Secret-value API permission. Disabling
PowerSync prevents new PowerSync implementation Secrets from being generated;
existing create-once implementation credentials follow the operator's normal
retention behavior.

For an upgrade, validate the development project first. Confirm the generated
images and named ports, scrape `/metrics` for both roles, authenticate a
Diagnostics request, and run an existing-client smoke check. Also confirm that
the database role Secret bytes, edition-3 sync rules, project credential
identity, and application data remain unchanged. This is an operator
verification procedure; the repository does not claim a live deployment.

The 1.20.4-to-1.21.0 change has no new operational PostgreSQL storage
migration script, but that is not a blanket rollback guarantee. Rolling an
image back does not reverse persistent PowerSync state or database changes.
Keep an appropriate backup and use the recovery/runbook decision for the
observed failure instead of assuming an image rollback restores the prior
state. No sync-rule conversion, dual-running path, or automatic data recovery
is provided by this feature.

## CNPG configuration source of truth

`SupabaseProject` is the single supported customization interface for the
Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/supabaseproject_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ type ImageSpec struct {

// PowersyncSpec defines Powersync offline-first sync configuration
type PowersyncSpec struct {
// Image configuration (default: journeyapps/powersync-service:1.20.4)
// Image configuration (default: journeyapps/powersync-service:1.21.0)
// +optional
Image ImageSpec `json:"image,omitempty"`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ spec:
type: string
type: object
image:
description: 'Image configuration (default: journeyapps/powersync-service:1.20.4)'
description: 'Image configuration (default: journeyapps/powersync-service:1.21.0)'
properties:
pullPolicy:
default: IfNotPresent
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/supabase.guion.dev_supabaseprojects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ spec:
type: string
type: object
image:
description: 'Image configuration (default: journeyapps/powersync-service:1.20.4)'
description: 'Image configuration (default: journeyapps/powersync-service:1.21.0)'
properties:
pullPolicy:
default: IfNotPresent
Expand Down
8 changes: 6 additions & 2 deletions config/samples/supabase_v1alpha1_supabaseproject.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ spec:

# Meta and Envoy use defaults (all core services are always enabled).

# Optional PowerSync offline-first sync. Sync Streams are required and should
# select explicit columns guarded by auth.user_id().
# Optional PowerSync offline-first sync. The managed default is
# journeyapps/powersync-service:1.21.0 with native metrics on named port
# 9464 and an operator-generated same-namespace <project>-powersync-api-token
# Secret (key: token) for authenticated Diagnostics. Sync Streams are required
# and should select explicit columns guarded by auth.user_id(). An explicit
# image override applies to API, replication, and compaction.
# powersync:
# api:
# replicas: 1
Expand Down
Loading
Loading