Skip to content

feat(operator): add PowerSync and self-contained deployment - #13

Merged
birdmanmandbir merged 21 commits into
mainfrom
worker/extend-cloudnative-sequin
Jul 22, 2026
Merged

feat(operator): add PowerSync and self-contained deployment#13
birdmanmandbir merged 21 commits into
mainfrom
worker/extend-cloudnative-sequin

Conversation

@birdmanmandbir

@birdmanmandbir birdmanmandbir commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • keep the optional PowerSync integration; remove Sequin, Meilisearch, bundled Redis, Dagger, and Forgejo CI
  • require explicit edition 3 Sync Streams rules and run PowerSync 1.20.4 with the API, sync, and compact roles used by the current deployment
  • manage the PowerSync database roles and publication through CloudNativePG, with a grant job for schema and table access
  • support generated or fully user-supplied PowerSync role secrets
  • add rollout-aware readiness, probes, bounded resources, secret handling, image pull secrets, and safer compact-job lifecycle settings
  • add a self-contained Tanka environment that renders the repository-local chart and CRD with no Jsonnet dependencies
  • publish amd64 controller images and OCI Helm charts through GitHub Actions and GHCR
  • retain the Kong memory reduction: one nginx worker, 512 MiB request, 1 GiB limit

Tanka ownership

The guion environment installs only the shared operator in cnsupa-system. Application-specific SupabaseProject resources remain in their application repositories. The old fd Flux operator manifests were already removed by fd commit c66d9bc; this PR does not change fd.

Verification

  • make test test-tanka test-delivery
  • ./bin/golangci-lint-v2.5.0 run --timeout=5m
  • go build -o bin/manager cmd/main.go
  • shellcheck hack/*.sh
  • tk fmt --test tanka
  • helm lint charts/cloudnative-supabase
  • parsed all GitHub workflow YAML files
  • built the controller image for linux/amd64
  • Kubernetes server-side dry-run of the Tanka-rendered operator resources

Release note

The controller package is already public. After the first OCI chart release, a GuionAI org admin must make guionai/charts/cloudnative-supabase public once; GitHub creates new package namespaces as private by default.

birdmanmandbir and others added 15 commits February 7, 2026 18:03
Complete design for extending cloudnative-supabase operator with three
optional add-ons:

- Sequin: CDC/event streaming (with Redis dependency)
- Powersync: Offline-first sync (with auto CDC config)
- Meilisearch: Full-text search

Key decisions:
- Operator-managed via optional CRD specs (presence = enabled)
- Auto-generate all secrets (zero-config deployment)
- Fully automatic CDC setup (roles, publications, schemas)
- Hybrid Redis (external or bundled)
- Production-ready defaults from flicknote-deploy
- Deploy after core Supabase services

Includes FlickNote reference example matching exact flicknote-deploy
configuration.
Solution: Use dbmate migration Job (mirrors flicknote-deploy pattern)
- ConfigMap with single migration (cdc_grants.sql)
- Job runs after CNPG cluster ready (waits for auth.users table)
- Custom migrations table (cloudnative_supabase_schema_migrations)
- Idempotent via dbmate tracking

Resolves open question from initial design.
Reference: flick-backend-31/tanka/charts/db-init
… and CDC setup

Add optional SequinSpec, PowersyncSpec, and MeilisearchSpec to SupabaseProject CRD.
Implement Sequin secret generation (secretKeyBase, vaultKey, apiToken + DB role passwords),
deployment builder with external Redis support, and CDC permissions Job using postgres
psql for idempotent database creation and grant management.

- CRD: Add SequinSpec, PowersyncSpec, MeilisearchSpec with ImageSpec, RedisSpec
- Secrets: Auto-generate Sequin app secret + sequin/sequin_replication role passwords
- Deployment: Sequin deployment with health probes, env from secrets, external Redis
- Service: ClusterIP service with HTTP (7376) and metrics (4000) ports
- CDC Job: Creates sequin database + applies replication grants after DB ready
- CNPG: Extend cluster with sequin/sequin_replication managed roles when enabled
- Controller: Add reconcileSequin, reconcileCDCPermissions, reconcileSequinSecrets
- RBAC: Add batch/v1 Jobs permissions, controller owns Job resources
- Status: Add SequinReady, CDCReady conditions and Sequin service status

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fig, and controller

- Add Powersync config.json ConfigMap builder with env var placeholders
- Add sync rules ConfigMap builder (inline, configMapRef, or default)
- Add Powersync API deployment (entry-api.js, client-facing, multi-replica)
- Add Powersync Replication deployment (entry-replication.js, single instance)
- Add Powersync Compact CronJob (entry-compact.js, configurable schedule)
- Add Powersync API service (ports 8080 http + 9464 metrics)
- Add Powersync secret generation and CNPG powersync_storage role
- Extend CDC permissions Job for conditional Powersync grants
- Add reconcilePowersync controller with ConfigMap, deployment, service, CronJob
- Add CronJob RBAC permissions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… secrets, and controller

- Add Meilisearch master key secret generation (auto-gen or user-provided)
- Add MeilisearchMasterKey to SecretNamesStatus
- Add Meilisearch StatefulSet builder with PVC, health probes, and env vars
- Add Meilisearch service (port 7700)
- Add reconcileMeilisearch controller with StatefulSet + Service
- Add createOrUpdateStatefulSet helper (immutable VolumeClaimTemplates)
- Add StatefulSet RBAC permissions
- Wire into Phase 7 of reconcile loop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Redis 7 StatefulSet builder (AOF persistence, non-root, TCP+CLI probes)
- Add Redis ClusterIP service builder
- Add RedisPersistenceSpec and Redis resources/storage to RedisSpec CRD
- Add redis:7 image defaults
- Update Sequin deployment to auto-resolve bundled Redis URL when external is nil
- Add reconcileSequinRedis to controller (deploys when external Redis not configured)
- Matches flicknote-deploy Redis chart pattern (security context, probes, 2Gi storage)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sync Helm chart CRD with generated spec (Sequin, Powersync, Meilisearch types)
- Add StatefulSet, Job, and CronJob RBAC to Helm chart ClusterRole
- Update sample CR with commented examples for optional services

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 50 tests across 7 test files covering all new resource builders:
- Sequin: deployment, env vars, external/bundled Redis URL resolution
- Redis: StatefulSet, service, storage customization, security context
- Powersync: API/Replication deployments, CronJob, env vars, volumes
- Meilisearch: StatefulSet, PVC, master key secret ref, image pull secrets
- Secrets: generation for Sequin/Powersync/Meilisearch, uniqueness
- ConfigMaps: Powersync config JSON structure, sync rules (inline/external/default)
- Services: port validation, labels/selectors for all new services

Also add CDC & Search quick start documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR workflow: lint (golangci-lint), test (pkg + resources), manifest check
CI workflow: lint, test, then build+push via Dagger to in-cluster registry

Dagger module builds Go binary in golang:1.25, packages into distroless
container, and publishes to registry-docker-registry.arc-systems.svc:5000.
Uses in-cluster Dagger engine for builds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tests

Fix gofmt formatting, extract test constants to eliminate goconst/unparam
warnings, fix staticcheck SA5011 nil-dereference patterns, rename shadowed
import parameter, and lowercase error strings per ST1005.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix two critical bugs in CDC permissions reconciliation:

1. createOrCheckJob now returns (completed bool, err error) instead of
   just error. When the Job is still running, reconcileCDCPermissions
   sets CDCReady=False/Reason=JobRunning and requeues instead of
   prematurely setting CDCReady=True and deploying Sequin/Powersync.

2. A SHA-256 hash of the CDC setup script is stored as an annotation
   on the Job. When the spec changes (e.g., Powersync added after
   Sequin was already enabled), the hash mismatch triggers deletion
   of the old Job so a new one runs with the updated permissions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split Powersync's database concerns into two distinct roles:
- powersync_storage: internal sync state tables (no REPLICATION)
- powersync_replication: CDC/WAL reading (REPLICATION + BypassRLS)

Previously powersync_storage was used for both the storage and
replication connections, but lacked the REPLICATION privilege needed
for logical replication. The replication URI now uses the dedicated
powersync_replication role with proper credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@birdmanmandbir birdmanmandbir changed the title feat(sequin): Phase 1 Sequin integration - CRD, deployment, CDC setup feat(powersync): add offline sync and GHCR releases Jul 22, 2026
@birdmanmandbir birdmanmandbir changed the title feat(powersync): add offline sync and GHCR releases feat(operator): add PowerSync and self-contained deployment Jul 22, 2026
@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Review fixes are in c5739c3:

  • gate PowerSync on CNPG managed-role, publication, CDC Job, and current Deployment readiness
  • validate edition 3 Sync Streams and roll pods when config changes
  • clean up owned runtime resources when PowerSync or compact is disabled
  • use terminal Job conditions and current Publication generations
  • harden PowerSync probes and compact memory defaults
  • require immutable Tanka image tags and add delivery checks, multi-arch PR builds, and OCI pull verification

Verified locally with make test test-tanka test-delivery, golangci-lint, go vet, go build, shellcheck, tk fmt --test, helm lint, Helm render, workflow YAML parsing, and Kubernetes server-side dry-run. No resources were applied.

@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Scope correction in c990bc7: controller images now build and publish for linux/amd64 only. This replaces the earlier multi-architecture claim in the PR description. PR, main CI, release, and the Makefile default all exclude arm64; the delivery test enforces it.

@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Triage Update

Fixed

  • P1 rollout readiness — 282c527 now requires the current generation to have all desired replicas updated, ready, and available, with none unavailable. Regression tests cover old ready pods masking an incomplete rollout.
  • P2 user-supplied PowerSync secrets — 282c527 adds storage and replication secret refs, conditional CRD validation, controller-side Secret validation, and preserves the refs in status without generating defaults.
  • P3 amd64-only description — the PR summary and verification section now state amd64 only; the stale multi-arch claims were removed.

False Positive

  • None.

Deferred

  • None.

Remaining

  • GitHub checks for 282c527 are running.

Local verification: make test test-tanka test-delivery, manager build, golangci-lint, shellcheck, Tanka formatting, Helm lint/render, workflow YAML parsing, and Kubernetes server-side dry-run all passed.

@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

CI follow-up: 8a95354 syncs the generated CRD into the Helm chart and makes test-delivery.sh use standard grep instead of runner-missing rg. The exact failed CI sequence now passes locally; new GitHub checks are running.

@birdmanmandbir

Copy link
Copy Markdown
Contributor Author

Triage Complete

Fixed

  • P1 rollout readiness — 282c527
  • P2 user-supplied PowerSync secrets — 282c527
  • P3 amd64-only PR description — updated in place
  • CI follow-up for generated Helm CRD and portable delivery checks — 8a95354

False Positive

  • None.

Deferred

  • None.

Remaining

  • None. GitHub build, lint, and amd64 Docker checks pass at 8a95354.

@birdmanmandbir
birdmanmandbir merged commit a555200 into main Jul 22, 2026
3 checks passed
@birdmanmandbir
birdmanmandbir deleted the worker/extend-cloudnative-sequin branch July 22, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant