From 4a771fd08ceae9982f46309a8a559228b5fe6def Mon Sep 17 00:00:00 2001 From: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta Date: Wed, 24 Jun 2026 17:55:36 -0400 Subject: [PATCH] Wire Postgres search backend through chart Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta (cherry picked from commit 86368981635df8b2f050069827b70c7ceea0e5fd) Co-authored-by: Max Co-authored-by: Tyler Longwell Signed-off-by: Tyler Longwell --- crates/buzz-relay/src/config.rs | 12 ++-- crates/buzz-relay/src/handlers/event.rs | 20 +++++-- .../src/handlers/identity_archive.rs | 20 +++++-- .../buzz-relay/src/handlers/mesh_signaling.rs | 20 +++++-- deploy/charts/buzz/README.md | 48 ++++++++------- deploy/charts/buzz/ci/quickstart-values.yaml | 8 +-- deploy/charts/buzz/examples/argocd-app.yaml | 5 +- .../buzz/examples/flux-helmrelease.yaml | 5 +- .../charts/buzz/examples/secret-sample.yaml | 1 + deploy/charts/buzz/templates/NOTES.txt | 6 +- deploy/charts/buzz/templates/_helpers.tpl | 6 ++ deploy/charts/buzz/templates/_validate.tpl | 6 +- deploy/charts/buzz/templates/deployment.yaml | 4 ++ .../buzz/templates/quickstart-typesense.yaml | 2 +- .../charts/buzz/templates/secret-chart.yaml | 6 +- deploy/charts/buzz/tests/networking_test.yaml | 8 --- .../buzz/tests/quickstart_bundled_test.yaml | 9 ++- .../buzz/tests/quickstart_guards_test.yaml | 3 +- deploy/charts/buzz/tests/render_test.yaml | 59 +++++++++++++++++-- deploy/charts/buzz/tests/secrets_test.yaml | 53 +++++++++++++---- deploy/charts/buzz/tests/validation_test.yaml | 19 +----- deploy/charts/buzz/values.schema.json | 7 +++ deploy/charts/buzz/values.yaml | 21 ++++--- 23 files changed, 232 insertions(+), 116 deletions(-) diff --git a/crates/buzz-relay/src/config.rs b/crates/buzz-relay/src/config.rs index df0056314..04d43a13d 100644 --- a/crates/buzz-relay/src/config.rs +++ b/crates/buzz-relay/src/config.rs @@ -159,7 +159,7 @@ impl Config { "BUZZ_SEARCH_BACKEND={bad:?} (expected `typesense`, `postgres`, or `disabled`)" )) })?, - Err(_) => buzz_search::SearchBackend::Typesense, + Err(_) => buzz_search::SearchBackend::Postgres, }; let relay_url = @@ -562,17 +562,21 @@ mod tests { } #[test] - fn search_backend_defaults_to_typesense() { + fn search_backend_defaults_to_postgres() { let _guard = ENV_MUTEX.lock().unwrap(); std::env::remove_var("BUZZ_SEARCH_BACKEND"); let config = Config::from_env().expect("default config"); - assert_eq!(config.search_backend, buzz_search::SearchBackend::Typesense); + assert_eq!(config.search_backend, buzz_search::SearchBackend::Postgres); } #[test] - fn search_backend_parses_postgres_and_disabled() { + fn search_backend_parses_typesense_postgres_and_disabled() { let _guard = ENV_MUTEX.lock().unwrap(); + std::env::set_var("BUZZ_SEARCH_BACKEND", "typesense"); + let config = Config::from_env().expect("config"); + assert_eq!(config.search_backend, buzz_search::SearchBackend::Typesense); + std::env::set_var("BUZZ_SEARCH_BACKEND", "postgres"); let config = Config::from_env().expect("config"); assert_eq!(config.search_backend, buzz_search::SearchBackend::Postgres); diff --git a/crates/buzz-relay/src/handlers/event.rs b/crates/buzz-relay/src/handlers/event.rs index 4cdcf8b2a..aa6595b00 100644 --- a/crates/buzz-relay/src/handlers/event.rs +++ b/crates/buzz-relay/src/handlers/event.rs @@ -1250,13 +1250,21 @@ mod tests { .await .expect("pubsub manager"), ); - let audit = buzz_audit::AuditService::new(pool); + let audit = buzz_audit::AuditService::new(pool.clone()); let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(buzz_search::SearchConfig { - url: config.typesense_url.clone(), - api_key: config.typesense_key.clone(), - collection: "events".to_string(), - }); + let search = match config.search_backend { + buzz_search::SearchBackend::Typesense => { + buzz_search::SearchService::new(buzz_search::SearchConfig { + url: config.typesense_url.clone(), + api_key: config.typesense_key.clone(), + collection: "events".to_string(), + }) + } + buzz_search::SearchBackend::Postgres => { + buzz_search::SearchService::with_postgres(pool.clone()) + } + buzz_search::SearchBackend::Disabled => buzz_search::SearchService::disabled(), + }; let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( db.clone(), buzz_workflow::WorkflowConfig::default(), diff --git a/crates/buzz-relay/src/handlers/identity_archive.rs b/crates/buzz-relay/src/handlers/identity_archive.rs index 1e8ed3634..47fa24778 100644 --- a/crates/buzz-relay/src/handlers/identity_archive.rs +++ b/crates/buzz-relay/src/handlers/identity_archive.rs @@ -443,13 +443,21 @@ mod tests { .await .ok()?, ); - let audit = buzz_audit::AuditService::new(pool); + let audit = buzz_audit::AuditService::new(pool.clone()); let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(buzz_search::SearchConfig { - url: config.typesense_url.clone(), - api_key: config.typesense_key.clone(), - collection: "events".to_string(), - }); + let search = match config.search_backend { + buzz_search::SearchBackend::Typesense => { + buzz_search::SearchService::new(buzz_search::SearchConfig { + url: config.typesense_url.clone(), + api_key: config.typesense_key.clone(), + collection: "events".to_string(), + }) + } + buzz_search::SearchBackend::Postgres => { + buzz_search::SearchService::with_postgres(pool.clone()) + } + buzz_search::SearchBackend::Disabled => buzz_search::SearchService::disabled(), + }; let workflow_engine = Arc::new(buzz_workflow::WorkflowEngine::new( db.clone(), buzz_workflow::WorkflowConfig::default(), diff --git a/crates/buzz-relay/src/handlers/mesh_signaling.rs b/crates/buzz-relay/src/handlers/mesh_signaling.rs index 0f3815169..f43d4c038 100644 --- a/crates/buzz-relay/src/handlers/mesh_signaling.rs +++ b/crates/buzz-relay/src/handlers/mesh_signaling.rs @@ -507,13 +507,21 @@ mod tests { .await .expect("pubsub manager"), ); - let audit = buzz_audit::AuditService::new(pool); + let audit = buzz_audit::AuditService::new(pool.clone()); let auth = buzz_auth::AuthService::new(config.auth.clone()); - let search = buzz_search::SearchService::new(buzz_search::SearchConfig { - url: config.typesense_url.clone(), - api_key: config.typesense_key.clone(), - collection: "events".to_string(), - }); + let search = match config.search_backend { + buzz_search::SearchBackend::Typesense => { + buzz_search::SearchService::new(buzz_search::SearchConfig { + url: config.typesense_url.clone(), + api_key: config.typesense_key.clone(), + collection: "events".to_string(), + }) + } + buzz_search::SearchBackend::Postgres => { + buzz_search::SearchService::with_postgres(pool.clone()) + } + buzz_search::SearchBackend::Disabled => buzz_search::SearchService::disabled(), + }; let workflow_engine = std::sync::Arc::new(buzz_workflow::WorkflowEngine::new( db.clone(), buzz_workflow::WorkflowConfig::default(), diff --git a/deploy/charts/buzz/README.md b/deploy/charts/buzz/README.md index ab08200b2..480e35af0 100644 --- a/deploy/charts/buzz/README.md +++ b/deploy/charts/buzz/README.md @@ -1,13 +1,13 @@ # Buzz Helm Chart -[Buzz](https://github.com/block/buzz) is a Nostr-based messaging platform for human–agent collaboration: a single relay binary serving WebSocket + REST + web UI, backed by PostgreSQL, Redis, Typesense, and S3-compatible object storage. +[Buzz](https://github.com/block/buzz) is a Nostr-based messaging platform for human–agent collaboration: a single relay binary serving WebSocket + REST + web UI, backed by PostgreSQL, Redis, and S3-compatible object storage. NIP-50 search defaults to Postgres full-text search, with Typesense available as a fallback backend. This chart has two operating profiles selected by values: | Profile | When | What you get | |---|---|---| -| **Production** (default) | Self-hosted multi-tenant, regulated, or GitOps-managed | External managed Postgres/Redis/Typesense/S3, `secrets.existingSecret:`, no chart-side autogen, HA-capable (`replicaCount ≥ 2`) | -| **Quickstart** (eval) | Eval, single-node, one-off demo | In-cluster Postgres + Redis + MinIO + Typesense subcharts/Deployments, chart auto-generates relay + service secrets, single replica | +| **Production** (default) | Self-hosted multi-tenant, regulated, or GitOps-managed | External managed Postgres/Redis/S3, Postgres FTS search by default, `secrets.existingSecret:`, no chart-side autogen, HA-capable (`replicaCount ≥ 2`) | +| **Quickstart** (eval) | Eval, single-node, one-off demo | In-cluster Postgres + Redis + MinIO, chart auto-generates relay + service secrets, single replica | ## Quickstart (eval only) @@ -18,18 +18,18 @@ helm install buzz oci://ghcr.io/block/buzz/charts/buzz --version 0.1.0 \ --set postgresql.enabled=true \ --set redis.enabled=true \ --set minio.enabled=true \ - --set typesense.enabled=true \ --set relayUrl=wss://buzz.example.com \ --set ownerPubkey=<64-char-hex-pubkey> ``` -This brings up **everything in-cluster** — Postgres, Redis, MinIO (with its -bucket created by a post-install Job), and Typesense — and composes the relay's -`BUZZ_S3_ENDPOINT` / `TYPESENSE_URL` plus autogenerated credentials -automatically. No external services required. The `quickstart=true` flag is an -intent marker surfaced in NOTES.txt; the bundled services are opted in via the -four `*.enabled` flags above (see `ci/quickstart-values.yaml` for the exact set -CI installs). Eval-only: every bundled service is a single replica with no HA. +This brings up the required quickstart dependencies in-cluster — Postgres, +Redis, and MinIO (with its bucket created by a post-install Job) — and composes +the relay's `BUZZ_S3_ENDPOINT` plus autogenerated credentials automatically. +Search defaults to Postgres FTS (`search.backend=postgres`), so no Typesense +service is required. The `quickstart=true` flag is an intent marker surfaced in +NOTES.txt; the bundled services are opted in via the per-service `*.enabled` +flags (see `ci/quickstart-values.yaml` for the exact set CI installs). +Eval-only: every bundled service is a single replica with no HA. ## Production (GitOps) @@ -50,7 +50,7 @@ See: | `relayUrl` | Public `wss://` URL clients connect to | Always | | `ownerPubkey` | 64-char lowercase hex Nostr pubkey of the relay operator | When `relay.requireRelayMembership=true` (default) | | `secrets.existingSecret` | Name of pre-created Secret | Production / GitOps | -| `externalPostgresql.url` / `externalRedis.url` / `typesense.url` / `s3.endpoint` | External service URLs | Production — when the matching bundled service is disabled (the default) | +| `externalPostgresql.url` / `externalRedis.url` / `s3.endpoint` | External service URLs | Production — when the matching bundled service is disabled (the default) | The chart fails at `helm install` / `helm template` time with a clear message if any of these are missing or malformed (see `templates/_validate.tpl`). @@ -81,17 +81,21 @@ Save these. Losing any of them is data loss. See NOTES.txt printed by `helm inst ## Honest limitations (v1) -- **Bundled MinIO + Typesense are eval-only.** The quickstart profile runs an - in-cluster MinIO and Typesense (single replica, no HA, `lookup`-autogenerated - credentials) so the relay starts with zero external services. Production - leaves `minio.enabled` / `typesense.enabled` off and points `s3.endpoint` + - `typesense.url` (or `BUZZ_S3_*` / `TYPESENSE_URL` in `existingSecret`) at - managed S3-compatible storage and Typesense. The bundled Deployments are not - GitOps-safe and are not intended for production traffic. +- **Bundled MinIO is eval-only.** The quickstart profile runs an in-cluster MinIO + (single replica, no HA, `lookup`-autogenerated credentials) so the relay starts + with zero external object-storage dependencies. Production leaves + `minio.enabled` off and points `s3.endpoint` (or `BUZZ_S3_*` in + `existingSecret`) at managed S3-compatible storage. The bundled Deployment is + not GitOps-safe and is not intended for production traffic. +- **Typesense is a fallback search backend.** Default search is Postgres FTS + (`search.backend=postgres`). To use Typesense instead, set + `search.backend=typesense` and provide either `typesense.url`/`apiKey`, + `TYPESENSE_URL`/`TYPESENSE_API_KEY` in `secrets.existingSecret`, or + `typesense.enabled=true` for eval-only in-cluster Typesense. - **Minimal-mode is not yet supported.** The relay's `BUZZ_PUBSUB=local` / - `BUZZ_SEARCH=pg` / filesystem media paths are upstream work in progress — - even quickstart currently stands up real Redis, Typesense, and S3 rather than - the relay's single-node fallbacks. + filesystem media paths are upstream work in progress — even quickstart + currently stands up real Redis and S3 rather than the relay's single-node + fallbacks. - **OCI publish to GHCR + cosign signing** is a follow-up PR. For now, install the chart from source: `helm install buzz ./deploy/charts/buzz` after cloning the repo. ## Development diff --git a/deploy/charts/buzz/ci/quickstart-values.yaml b/deploy/charts/buzz/ci/quickstart-values.yaml index 71d4e76a4..3c7ac4e66 100644 --- a/deploy/charts/buzz/ci/quickstart-values.yaml +++ b/deploy/charts/buzz/ci/quickstart-values.yaml @@ -1,7 +1,9 @@ # Quickstart / eval: subcharts on, autogen secrets, single replica. # This is the scenario `ct install` exercises against a kind cluster — it -# spins up postgres + redis + minio + typesense in-cluster so the relay can -# actually start and pass its S3 conformance probe. +# spins up postgres + redis + minio in-cluster so the relay can actually +# start and pass its S3 conformance probe. Search defaults to Postgres FTS, so +# the quickstart no longer needs in-cluster Typesense unless explicitly testing +# search.backend=typesense. quickstart: true postgresql: enabled: true @@ -9,8 +11,6 @@ redis: enabled: true minio: enabled: true -typesense: - enabled: true relayUrl: wss://buzz.test.local ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000001" relay: diff --git a/deploy/charts/buzz/examples/argocd-app.yaml b/deploy/charts/buzz/examples/argocd-app.yaml index 612f6e4b2..f56ad78fe 100644 --- a/deploy/charts/buzz/examples/argocd-app.yaml +++ b/deploy/charts/buzz/examples/argocd-app.yaml @@ -37,9 +37,8 @@ spec: externalRedis: url: "" - typesense: - url: "http://typesense.buzz.svc.cluster.local:8108" - # apiKey lives in buzz-secrets + search: + backend: postgres s3: endpoint: "https://s3.us-east-1.amazonaws.com" diff --git a/deploy/charts/buzz/examples/flux-helmrelease.yaml b/deploy/charts/buzz/examples/flux-helmrelease.yaml index a6b6276c5..251d25dec 100644 --- a/deploy/charts/buzz/examples/flux-helmrelease.yaml +++ b/deploy/charts/buzz/examples/flux-helmrelease.yaml @@ -37,9 +37,8 @@ spec: secrets: existingSecret: buzz-secrets - typesense: - url: "http://typesense.buzz.svc.cluster.local:8108" - # apiKey lives in buzz-secrets + search: + backend: postgres s3: endpoint: "https://s3.us-east-1.amazonaws.com" diff --git a/deploy/charts/buzz/examples/secret-sample.yaml b/deploy/charts/buzz/examples/secret-sample.yaml index c842038b4..968399436 100644 --- a/deploy/charts/buzz/examples/secret-sample.yaml +++ b/deploy/charts/buzz/examples/secret-sample.yaml @@ -11,6 +11,7 @@ # REDIS_URL — redis://... (required when replicaCount > 1) # TYPESENSE_URL # TYPESENSE_API_KEY +# Only required when Helm value search.backend=typesense. # BUZZ_S3_ACCESS_KEY # BUZZ_S3_SECRET_KEY apiVersion: v1 diff --git a/deploy/charts/buzz/templates/NOTES.txt b/deploy/charts/buzz/templates/NOTES.txt index 35a1080ad..5560d9d73 100644 --- a/deploy/charts/buzz/templates/NOTES.txt +++ b/deploy/charts/buzz/templates/NOTES.txt @@ -26,19 +26,19 @@ ────────────────────────────────────────────────────────────────────────────── Profile ────────────────────────────────────────────────────────────────────────────── -{{ if or .Values.postgresql.enabled .Values.redis.enabled .Values.minio.enabled .Values.typesense.enabled }} +{{ if or .Values.postgresql.enabled .Values.redis.enabled .Values.minio.enabled (and .Values.typesense.enabled (eq (include "buzz.searchBackend" .) "typesense")) }} ⚠ QUICKSTART / EVALUATION PROFILE {{ if .Values.postgresql.enabled }}- In-cluster Postgres subchart (CloudPirates){{ end }} {{ if .Values.redis.enabled }}- In-cluster Redis subchart (CloudPirates){{ end }} {{ if .Values.minio.enabled }}- In-cluster MinIO (eval-only, single replica; bucket "{{ .Values.s3.bucket }}" created by post-install Job){{ end }} - {{ if .Values.typesense.enabled }}- In-cluster Typesense (eval-only, single replica){{ end }} + {{ if and .Values.typesense.enabled (eq (include "buzz.searchBackend" .) "typesense") }}- In-cluster Typesense (eval-only, single replica){{ end }} - Chart auto-generates secrets via the `lookup` pattern. This is NOT GitOps-safe — secrets will silently rotate under ArgoCD/Flux. For production, see examples/argocd-app.yaml or examples/flux-helmrelease.yaml. {{ else }} ✓ PRODUCTION PROFILE - External Postgres, Redis (if enabled), Typesense, S3. + External Postgres, Redis (if enabled), S3; search backend: {{ include "buzz.searchBackend" . }}. {{ if .Values.secrets.existingSecret }}- Secrets sourced from: {{ .Values.secrets.existingSecret }}{{ end }} {{ end }} diff --git a/deploy/charts/buzz/templates/_helpers.tpl b/deploy/charts/buzz/templates/_helpers.tpl index e7ac0ed16..f40963f7d 100644 --- a/deploy/charts/buzz/templates/_helpers.tpl +++ b/deploy/charts/buzz/templates/_helpers.tpl @@ -116,6 +116,12 @@ secrets.existingSecret, use that. Otherwise use the chart-managed one. {{- end -}} {{- end -}} +{{/* Search backend flag. Defaults to Postgres FTS; Typesense remains opt-in fallback. */}} +{{- define "buzz.searchBackend" -}} +{{- $search := default dict .Values.search -}} +{{- default "postgres" $search.backend -}} +{{- end -}} + {{/* In-cluster Typesense URL, used when typesense.enabled and url unset. */}} {{- define "buzz.typesenseUrl" -}} {{- if .Values.typesense.url -}} diff --git a/deploy/charts/buzz/templates/_validate.tpl b/deploy/charts/buzz/templates/_validate.tpl index a6435f606..ad072a140 100644 --- a/deploy/charts/buzz/templates/_validate.tpl +++ b/deploy/charts/buzz/templates/_validate.tpl @@ -50,9 +50,9 @@ surface at template time regardless of which manifest helm renders first. {{- fail "Postgres source missing: enable postgresql.enabled=true, set externalPostgresql.url, or provide secrets.existingSecret with key DATABASE_URL." -}} {{- end -}} -{{/* Typesense source must exist somewhere */}} -{{- if not (or .Values.typesense.enabled .Values.typesense.url .Values.secrets.existingSecret) -}} - {{- fail "Typesense source missing: enable typesense.enabled=true (quickstart in-cluster), set typesense.url + typesense.apiKey, or provide secrets.existingSecret with keys TYPESENSE_URL + TYPESENSE_API_KEY." -}} +{{/* Typesense source is required only when search.backend=typesense. */}} +{{- if and (eq (include "buzz.searchBackend" .) "typesense") (not (or .Values.typesense.enabled .Values.typesense.url .Values.secrets.existingSecret)) -}} + {{- fail "Typesense source missing for search.backend=typesense: enable typesense.enabled=true (quickstart in-cluster), set typesense.url + typesense.apiKey, or provide secrets.existingSecret with keys TYPESENSE_URL + TYPESENSE_API_KEY." -}} {{- end -}} {{/* S3 / object-storage source must exist somewhere (relay hard-fails its diff --git a/deploy/charts/buzz/templates/deployment.yaml b/deploy/charts/buzz/templates/deployment.yaml index e234fb8e4..7c59c8294 100644 --- a/deploy/charts/buzz/templates/deployment.yaml +++ b/deploy/charts/buzz/templates/deployment.yaml @@ -1,4 +1,5 @@ {{- include "buzz.validate" . -}} +{{- $searchBackend := include "buzz.searchBackend" . -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -80,6 +81,7 @@ spec: - { name: BUZZ_REQUIRE_RELAY_MEMBERSHIP, value: {{ .Values.relay.requireRelayMembership | quote }} } - { name: BUZZ_ALLOW_NIP_OA_AUTH, value: {{ .Values.relay.allowNipOaAuth | quote }} } - { name: BUZZ_PUBKEY_ALLOWLIST, value: {{ .Values.relay.pubkeyAllowlist | quote }} } + - { name: BUZZ_SEARCH_BACKEND, value: {{ $searchBackend | quote }} } {{- if .Values.relay.corsOrigins }} - { name: BUZZ_CORS_ORIGINS, value: {{ join "," .Values.relay.corsOrigins | quote }} } {{- end }} @@ -130,6 +132,7 @@ spec: name: {{ include "buzz.envSecretName" . }} key: REDIS_URL optional: {{ and (eq (.Values.replicaCount | int) 1) (not .Values.redis.enabled) (not .Values.externalRedis.url) }} + {{- if eq $searchBackend "typesense" }} - name: TYPESENSE_URL valueFrom: secretKeyRef: @@ -140,6 +143,7 @@ spec: secretKeyRef: name: {{ include "buzz.envSecretName" . }} key: TYPESENSE_API_KEY + {{- end }} - name: BUZZ_S3_ACCESS_KEY valueFrom: secretKeyRef: diff --git a/deploy/charts/buzz/templates/quickstart-typesense.yaml b/deploy/charts/buzz/templates/quickstart-typesense.yaml index 7184d5c5f..75f093edb 100644 --- a/deploy/charts/buzz/templates/quickstart-typesense.yaml +++ b/deploy/charts/buzz/templates/quickstart-typesense.yaml @@ -4,7 +4,7 @@ single replica, no TLS, API key from the chart-managed Secret. Production deploys leave typesense.enabled=false and point typesense.url/apiKey (or secrets.existingSecret) at a managed Typesense service. */ -}} -{{- if .Values.typesense.enabled -}} +{{- if and .Values.typesense.enabled (eq (include "buzz.searchBackend" .) "typesense") -}} {{- if .Values.secrets.existingSecret -}} {{- fail "typesense.enabled=true (quickstart) is incompatible with secrets.existingSecret. Quickstart autogenerates the Typesense key in the chart-managed Secret; for external Typesense set typesense.enabled=false and provide TYPESENSE_URL/TYPESENSE_API_KEY." -}} {{- end -}} diff --git a/deploy/charts/buzz/templates/secret-chart.yaml b/deploy/charts/buzz/templates/secret-chart.yaml index 0a7677bad..edf2ef842 100644 --- a/deploy/charts/buzz/templates/secret-chart.yaml +++ b/deploy/charts/buzz/templates/secret-chart.yaml @@ -73,8 +73,9 @@ data: REDIS_URL: {{ .Values.externalRedis.url | b64enc | quote }} {{- end }} - {{- /* Typesense — bundled (quickstart) composes URL + autogen key; else - pass through external values. */}} + {{- /* Typesense — only when search.backend=typesense. Bundled quickstart + composes URL + autogen key; else pass through external values. */}} + {{- if eq (include "buzz.searchBackend" .) "typesense" }} {{- if .Values.typesense.enabled }} {{- $tsKey := "" }} {{- if (index $existingData "TYPESENSE_API_KEY") }} @@ -94,6 +95,7 @@ data: TYPESENSE_API_KEY: {{ .Values.typesense.apiKey | b64enc | quote }} {{- end }} {{- end }} + {{- end }} {{- /* S3 creds — bundled MinIO (quickstart) autogenerates; else pass through external values. */}} diff --git a/deploy/charts/buzz/tests/networking_test.yaml b/deploy/charts/buzz/tests/networking_test.yaml index e925bbb2d..882679f2e 100644 --- a/deploy/charts/buzz/tests/networking_test.yaml +++ b/deploy/charts/buzz/tests/networking_test.yaml @@ -9,8 +9,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -29,8 +27,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -47,8 +43,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -68,8 +62,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s diff --git a/deploy/charts/buzz/tests/quickstart_bundled_test.yaml b/deploy/charts/buzz/tests/quickstart_bundled_test.yaml index 7acb8ab75..547e7125c 100644 --- a/deploy/charts/buzz/tests/quickstart_bundled_test.yaml +++ b/deploy/charts/buzz/tests/quickstart_bundled_test.yaml @@ -1,6 +1,6 @@ suite: quickstart bundled services -# The dev quickstart must stand up MinIO + Typesense in-cluster so the relay's -# startup S3 conformance probe passes with zero external dependencies. +# The dev quickstart must stand up MinIO in-cluster so the relay's startup S3 +# conformance probe passes. Typesense is only bundled when search.backend=typesense. templates: - templates/quickstart-minio.yaml - templates/quickstart-minio-init.yaml @@ -17,6 +17,7 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true redis.enabled: true + search.backend: typesense typesense.enabled: true minio.enabled: true asserts: @@ -40,6 +41,7 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true redis.enabled: true + search.backend: typesense typesense.enabled: true minio.enabled: true asserts: @@ -58,6 +60,7 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true redis.enabled: true + search.backend: typesense typesense.enabled: true minio.enabled: true asserts: @@ -76,6 +79,7 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true redis.enabled: true + search.backend: typesense typesense.enabled: true minio.enabled: true asserts: @@ -103,6 +107,7 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true redis.enabled: true + search.backend: typesense typesense.enabled: true minio.enabled: true asserts: diff --git a/deploy/charts/buzz/tests/quickstart_guards_test.yaml b/deploy/charts/buzz/tests/quickstart_guards_test.yaml index 9054cf5be..7b490e9b8 100644 --- a/deploy/charts/buzz/tests/quickstart_guards_test.yaml +++ b/deploy/charts/buzz/tests/quickstart_guards_test.yaml @@ -10,8 +10,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true - typesense.url: http://ts:8108 - typesense.apiKey: k minio.enabled: true secrets.existingSecret: "buzz-secrets" asserts: @@ -28,6 +26,7 @@ tests: s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s + search.backend: typesense typesense.enabled: true secrets.existingSecret: "buzz-secrets" asserts: diff --git a/deploy/charts/buzz/tests/render_test.yaml b/deploy/charts/buzz/tests/render_test.yaml index 970aeeab8..353ce7c30 100644 --- a/deploy/charts/buzz/tests/render_test.yaml +++ b/deploy/charts/buzz/tests/render_test.yaml @@ -8,14 +8,12 @@ templates: - templates/service.yaml - templates/pvc-git.yaml tests: - - it: renders cleanly in production profile (external pg/redis/typesense) + - it: renders cleanly in production profile (external pg/redis/s3, postgres search) set: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d externalRedis.url: redis://h:6379 - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -39,8 +37,6 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d externalRedis.url: redis://h:6379 - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -55,3 +51,56 @@ tests: path: spec.accessModes[0] value: ReadWriteMany template: templates/pvc-git.yaml + + - it: postgres backend does not require a Typesense source + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + externalPostgresql.url: postgres://u:p@h:5432/d + s3.endpoint: http://minio:9000 + s3.accessKey: a + s3.secretKey: s + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: BUZZ_SEARCH_BACKEND + value: "postgres" + template: templates/deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: TYPESENSE_URL + template: templates/deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: TYPESENSE_API_KEY + template: templates/deployment.yaml + + - it: disabled backend does not require a Typesense source + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + externalPostgresql.url: postgres://u:p@h:5432/d + search.backend: disabled + s3.endpoint: http://minio:9000 + s3.accessKey: a + s3.secretKey: s + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: BUZZ_SEARCH_BACKEND + value: "disabled" + template: templates/deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: TYPESENSE_URL + template: templates/deployment.yaml + - notContains: + path: spec.template.spec.containers[0].env + content: + name: TYPESENSE_API_KEY + template: templates/deployment.yaml diff --git a/deploy/charts/buzz/tests/secrets_test.yaml b/deploy/charts/buzz/tests/secrets_test.yaml index 1a3690c1f..eaedf83ac 100644 --- a/deploy/charts/buzz/tests/secrets_test.yaml +++ b/deploy/charts/buzz/tests/secrets_test.yaml @@ -8,8 +8,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -31,8 +29,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -47,8 +43,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -70,8 +64,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -93,8 +85,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s @@ -106,6 +96,47 @@ tests: value: "true" template: templates/deployment.yaml + - it: postgres search Secret omits Typesense keys + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + externalPostgresql.url: postgres://u:p@h:5432/d + s3.endpoint: http://minio:9000 + s3.accessKey: a + s3.secretKey: s + typesense.url: http://ts:8108 + typesense.apiKey: k + asserts: + - notExists: + path: data.TYPESENSE_URL + template: templates/secret-chart.yaml + - notExists: + path: data.TYPESENSE_API_KEY + template: templates/secret-chart.yaml + + - it: typesense search Secret includes Typesense keys + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + externalPostgresql.url: postgres://u:p@h:5432/d + search.backend: typesense + typesense.url: http://ts:8108 + typesense.apiKey: k + s3.endpoint: http://minio:9000 + s3.accessKey: a + s3.secretKey: s + asserts: + - equal: + path: data.TYPESENSE_URL + decodeBase64: true + value: http://ts:8108 + template: templates/secret-chart.yaml + - equal: + path: data.TYPESENSE_API_KEY + decodeBase64: true + value: k + template: templates/secret-chart.yaml + - it: quickstart composes DATABASE_URL/REDIS_URL at the actual subchart Service hosts release: name: rel @@ -114,8 +145,6 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" postgresql.enabled: true redis.enabled: true - typesense.url: http://ts:8108 - typesense.apiKey: k s3.endpoint: http://minio:9000 s3.accessKey: a s3.secretKey: s diff --git a/deploy/charts/buzz/tests/validation_test.yaml b/deploy/charts/buzz/tests/validation_test.yaml index 56e656ae8..1b9e503eb 100644 --- a/deploy/charts/buzz/tests/validation_test.yaml +++ b/deploy/charts/buzz/tests/validation_test.yaml @@ -7,8 +7,6 @@ tests: relayUrl: "" ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k asserts: - failedTemplate: errorMessage: "relayUrl is required: set --set relayUrl=wss://your.domain" @@ -18,8 +16,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k asserts: - failedTemplate: errorPattern: "ownerPubkey is required when relay.requireRelayMembership=true" @@ -29,8 +25,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "NOTAHEX" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k asserts: - failedTemplate: errorPattern: "ownerPubkey: Does not match pattern" @@ -40,8 +34,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k replicaCount: 3 asserts: - failedTemplate: @@ -53,8 +45,6 @@ tests: ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d externalRedis.url: redis://h:6379 - typesense.url: http://ts:8108 - typesense.apiKey: k replicaCount: 3 persistence.git.accessMode: ReadWriteOnce asserts: @@ -66,8 +56,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k ingress.enabled: true httproute.enabled: true asserts: @@ -78,17 +66,16 @@ tests: set: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" - typesense.url: http://ts:8108 - typesense.apiKey: k asserts: - failedTemplate: errorPattern: "Postgres source missing" - - it: fails when Typesense source is missing + - it: fails when Typesense source is missing for typesense backend set: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d + search.backend: typesense asserts: - failedTemplate: errorPattern: "Typesense source missing" @@ -98,8 +85,6 @@ tests: relayUrl: wss://buzz.example.com ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" externalPostgresql.url: postgres://u:p@h:5432/d - typesense.url: http://ts:8108 - typesense.apiKey: k asserts: - failedTemplate: errorPattern: "S3/object-storage source missing" diff --git a/deploy/charts/buzz/values.schema.json b/deploy/charts/buzz/values.schema.json index 38f7b204d..cc4aecf25 100644 --- a/deploy/charts/buzz/values.schema.json +++ b/deploy/charts/buzz/values.schema.json @@ -162,6 +162,13 @@ "url": { "type": "string", "pattern": "^(rediss?://.+)?$" } } }, + "search": { + "type": "object", + "additionalProperties": false, + "properties": { + "backend": { "type": "string", "enum": ["postgres", "typesense", "disabled"] } + } + }, "typesense": { "type": "object", "additionalProperties": false, diff --git a/deploy/charts/buzz/values.yaml b/deploy/charts/buzz/values.yaml index 15a70ec28..143457b6e 100644 --- a/deploy/charts/buzz/values.yaml +++ b/deploy/charts/buzz/values.yaml @@ -6,11 +6,11 @@ # refs everywhere, no chart-side autogeneration, GitOps-safe (ArgoCD/Flux). # HA-ready: replicaCount >= 2 (requires Redis and RWX storage for git). # -# QUICKSTART — bundles in-cluster Postgres + Redis + MinIO + Typesense and -# auto-generates relay secrets via the `lookup` pattern (NOT GitOps-safe — -# see README), single replica, evaluation only. Opt in by enabling each -# bundled service: postgresql.enabled, redis.enabled, minio.enabled, -# typesense.enabled. See ci/quickstart-values.yaml and the README. +# QUICKSTART — bundles in-cluster Postgres + Redis + MinIO and auto-generates +# relay secrets via the `lookup` pattern (NOT GitOps-safe — see README), +# single replica, evaluation only. Opt in by enabling each bundled service: +# postgresql.enabled, redis.enabled, minio.enabled. Typesense is optional only +# when search.backend=typesense. See ci/quickstart-values.yaml and the README. # # See examples/argocd-app.yaml and examples/flux-helmrelease.yaml for the # canonical GitOps configurations. @@ -56,8 +56,8 @@ ownerPubkey: "" # BUZZ_GIT_HOOK_HMAC_SECRET — 32+ chars; required when replicaCount > 1 # DATABASE_URL — full Postgres URL (preferred over externalPostgresql.url) # REDIS_URL — full Redis URL with auth -# TYPESENSE_URL — Typesense base URL -# TYPESENSE_API_KEY — Typesense API key +# TYPESENSE_URL — Typesense base URL (only when search.backend=typesense) +# TYPESENSE_API_KEY — Typesense API key (only when search.backend=typesense) # BUZZ_S3_ACCESS_KEY — S3 access key # BUZZ_S3_SECRET_KEY — S3 secret key secrets: @@ -211,6 +211,13 @@ redis: externalRedis: url: "" # redis://:pass@host:6379 +# ── Search ─────────────────────────────────────────────────────────────────── +# Postgres FTS is the default. Set backend=typesense only while using the +# fallback Typesense index; set backend=disabled to fail NIP-50 search closed +# (queries return no hits, without weakening auth/channel filtering). +search: + backend: postgres # postgres | typesense | disabled + # ── Typesense ──────────────────────────────────────────────────────────────── # Production: point url/apiKey at an external Typesense service (or supply # TYPESENSE_URL/TYPESENSE_API_KEY via secrets.existingSecret).