From 4907dd28ecbf74b96a8f3d3c8239d7f60bf8c10d Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 16 Sep 2026 08:09:01 -0700 Subject: [PATCH 1/2] feat: allow an external Redis, with credentials from a secret The bundled Redis stays the default and nothing changes for an install that uses it. `currents.redis.host` already pointed elsewhere, but the URI was hardcoded to `redis://:6379`, so the only reachable external Redis was one with no encryption in transit and no credentials. `host`, `readerHost`, `port` and `tls.enabled` now compose the URI, and `connection.secretName` reads it whole from a secret instead. The secret is the path for anything needing an AUTH token: a composed URI is rendered into the pod spec, so a token set that way is readable by anyone who can describe a pod. `REDIS_URI_SLAVE` was the primary's address, so read-only traffic went to the primary. It now follows `readerHost` / `connection.readerKey` when either is set, and falls back to the primary when neither is. Documents what a replacement has to provide, because two of these are only visible under load: the JSON commands the orchestration Lua scripts call, which on ElastiCache means Redis 6.2.6+ or Valkey, and a primary/replica group rather than cluster mode, which rejects the multi-key scripts. Also that auth is an AUTH token in the URI and that IAM auth is not supported. Co-Authored-By: Claude Opus 5 (1M context) --- charts/currents/Chart.yaml | 2 +- charts/currents/templates/_common.tpl | 20 +++++++++-- charts/currents/values.yaml | 20 +++++++++++ docs/configuration.md | 9 ++++- docs/eks/dependencies.md | 50 +++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/charts/currents/Chart.yaml b/charts/currents/Chart.yaml index 6a362bf..677a933 100644 --- a/charts/currents/Chart.yaml +++ b/charts/currents/Chart.yaml @@ -5,7 +5,7 @@ home: https://currents.dev type: application # The chart version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.7.5 +version: 0.8.0 # Version number of the application being deployed. # Versions are not expected to follow Semantic Versioning. They should reflect the version the application is using. appVersion: "2026-07-26-004" diff --git a/charts/currents/templates/_common.tpl b/charts/currents/templates/_common.tpl index 349fe2c..7946123 100644 --- a/charts/currents/templates/_common.tpl +++ b/charts/currents/templates/_common.tpl @@ -92,10 +92,26 @@ Create the name of the service account to use {{- end -}} {{- define "currents.connectionConfigEnv" -}} +{{- $redis := .Values.currents.redis }} +{{- if $redis.connection.secretName }} - name: REDIS_URI - value: {{ printf "redis://%s:6379" (tpl .Values.currents.redis.host .) }} + valueFrom: + secretKeyRef: + name: {{ $redis.connection.secretName }} + key: {{ $redis.connection.key }} +- name: REDIS_URI_SLAVE + valueFrom: + secretKeyRef: + name: {{ $redis.connection.secretName }} + key: {{ $redis.connection.readerKey | default $redis.connection.key }} +{{- else }} +{{- $scheme := ternary "rediss" "redis" $redis.tls.enabled }} +{{- $reader := default $redis.host $redis.readerHost }} +- name: REDIS_URI + value: {{ printf "%s://%s:%v" $scheme (tpl $redis.host .) $redis.port | quote }} - name: REDIS_URI_SLAVE - value: {{ printf "redis://%s:6379" (tpl .Values.currents.redis.host .) }} + value: {{ printf "%s://%s:%v" $scheme (tpl $reader .) $redis.port | quote }} +{{- end }} {{- if .Values.currents.mongoConnection.secretName }} - name: MONGODB_URI valueFrom: diff --git a/charts/currents/values.yaml b/charts/currents/values.yaml index 6038175..979478e 100644 --- a/charts/currents/values.yaml +++ b/charts/currents/values.yaml @@ -125,6 +125,26 @@ currents: # -- (tpl) set the redis hostname to talk to # @default -- `{{ .Release.Name }}-redis-master` host: "{{ .Release.Name }}-redis-master" + # -- (tpl) hostname for read-only traffic. A managed Redis usually publishes a separate + # reader endpoint; leaving this empty sends reads to `host`, which is the primary. + readerHost: "" + # -- The port to connect on + port: 6379 + tls: + # -- Connect with `rediss://`. Set this for a managed Redis with encryption in transit. + enabled: false + # -- Read the connection URI from a K8s secret instead of composing it from the fields + # above. Needed for any Redis that requires credentials: the composed URI is rendered + # into the pod spec, so an AUTH token set that way would be readable by anyone who can + # describe a pod. Leave unset to use the bundled Redis. + # @section -- Frequently Used + connection: + # -- Secret holding the full URI, e.g. `rediss://:@host:6379` + secretName: "" + # -- Secret key for the primary URI + key: uri + # -- Secret key for the read-only URI. Defaults to `key` when unset. + readerKey: "" clickhouse: user: # -- The ClickHouse username to use diff --git a/docs/configuration.md b/docs/configuration.md index 3e33f82..222b2f0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,6 +1,6 @@ # Configuration Reference -![Version: 0.7.5](https://img.shields.io/badge/Version-0.7.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2026-07-26-004](https://img.shields.io/badge/AppVersion-2026--07--26--004-informational?style=flat-square) +![Version: 0.8.0](https://img.shields.io/badge/Version-0.8.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2026-07-26-004](https://img.shields.io/badge/AppVersion-2026--07--26--004-informational?style=flat-square) ## Requirements @@ -46,6 +46,7 @@ The following table lists the configurable parameters of the `currents` chart an | currents.email.smtp.secretPasswordKey | string | `"password"` | The K8s secret key to use for the SMTP password | | currents.betterAuth.key | string | `"secret"` | The K8s secret key for the Better Auth secret | | currents.apiInternalToken.key | string | `"token"` | The K8s secret key to use for the internal API token | +| currents.redis.connection | object | `{"key":"uri","readerKey":"","secretName":""}` | Read the connection URI from a K8s secret instead of composing it from the fields above. Needed for any Redis that requires credentials: the composed URI is rendered into the pod spec, so an AUTH token set that way would be readable by anyone who can describe a pod. Leave unset to use the bundled Redis. | | currents.clickhouse.user.username | string | `"currents"` | The ClickHouse username to use | | currents.clickhouse.tls.enabled | bool | `true` | Whether to use TLS for the ClickHouse connection | | currents.objectStorage.secretIdKey | string | `"keyId"` | The K8s secret key to use for the object storage access key ID | @@ -102,6 +103,12 @@ The following table lists the configurable parameters of the `currents` chart an | currents.email.linksBaseUrl | string | `""` | Base URL for links in emails (defaults to APP_BASE_URL if empty) | | currents.ingress.enabled | bool | `false` | Whether to enable the both default ingresses (server, and director) | | currents.redis.host | tpl | `{{ .Release.Name }}-redis-master` | set the redis hostname to talk to | +| currents.redis.readerHost | tpl | `""` | hostname for read-only traffic. A managed Redis usually publishes a separate reader endpoint; leaving this empty sends reads to `host`, which is the primary. | +| currents.redis.port | int | `6379` | The port to connect on | +| currents.redis.tls.enabled | bool | `false` | Connect with `rediss://`. Set this for a managed Redis with encryption in transit. | +| currents.redis.connection.secretName | string | `""` | Secret holding the full URI, e.g. `rediss://:@host:6379` | +| currents.redis.connection.key | string | `"uri"` | Secret key for the primary URI | +| currents.redis.connection.readerKey | string | `""` | Secret key for the read-only URI. Defaults to `key` when unset. | | currents.clickhouse.port | int | `8123` | The ClickHouse port to use | | currents.objectStorage.internalEndpoint | string | `""` | The object storage internal endpoint to use (for internal communication) | | currents.objectStorage.region | string | `""` | The region to use for the object storage | diff --git a/docs/eks/dependencies.md b/docs/eks/dependencies.md index a5137ab..0ee1efd 100644 --- a/docs/eks/dependencies.md +++ b/docs/eks/dependencies.md @@ -100,6 +100,56 @@ This will setup a 1-node 1-shard ClickHouse Replicated Server (10Gb Storage) --set operator.enabled=false ``` +### Redis (optional — bundled by default) + +The chart ships a Redis and uses it unless you point it elsewhere, so there is nothing to do +here for a standard install. Replace it if you would rather not operate it yourself. + +**What the replacement has to provide.** Currents stores orchestration state as JSON documents +and reads them from inside Lua scripts, so the server must support the `JSON.GET` / `JSON.SET` +commands. On ElastiCache that means **Redis 6.2.6 or newer, or Valkey**; older engines will start +the app but fail spec claiming under load. Run a real test run against it before cutting over, not +just a health check. + +Use a **replication group with a primary and a replica**, not cluster mode. Currents runs multi-key +Lua scripts, and cluster mode rejects those when the keys land in different slots. This is the +topology the hosted service runs. + +**Authentication is an AUTH token supplied in the URI.** IAM authentication is not supported — +the client takes a static credential and has nothing to refresh a short-lived one. + +1. Create the secret holding the connection URI. Keep the token here rather than in your values + file: a URI composed from plain values is rendered into the pod spec, where anyone who can + describe a pod can read it. + + ```sh + kubectl create secret generic currents-redis \ + --from-literal=uri="rediss://:@master..cache.amazonaws.com:6379" \ + --from-literal=readerUri="rediss://:@replica..cache.amazonaws.com:6379" + ``` + + `rediss://` selects encryption in transit. Use `redis://` only if the group has it disabled. + +2. Point the chart at it and turn the bundled Redis off: + + ```yaml + redis: + enabled: false + + currents: + redis: + connection: + secretName: currents-redis + key: uri + readerKey: readerUri + ``` + + `readerKey` is optional. Without it, read-only traffic goes to the primary; with it, to the + reader endpoint. + +For a Redis that needs no credentials, `currents.redis.host`, `readerHost`, `port` and +`tls.enabled` compose the URI directly and no secret is required. + ### Object Storage (provider) Follow this step if you plan to use provider (S3, Cloudflare) object storage (recommended). From 5994764decdcf9722b464fa44c2ed8f65df408ea Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 16 Sep 2026 09:03:44 -0700 Subject: [PATCH 2/2] docs: correct the claim that the bundled Redis is the default `redis.enabled` is false, so the chart deploys no Redis unless asked. The section added in the previous commit said the opposite, and the reviewer was right that it is the documentation and not the default that is wrong: the quickstart already has operators set `redis.enabled: true`, and values.yaml marks it Required. States that Redis is required and neither option is automatic, and that `currents.redis.host` names a service which only exists when the bundled chart is enabled -- setting one without the other points the install at nothing, which was the reviewer's underlying observation. Co-Authored-By: Claude Opus 5 (1M context) --- charts/currents/values.yaml | 3 ++- docs/configuration.md | 2 +- docs/eks/dependencies.md | 20 +++++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/charts/currents/values.yaml b/charts/currents/values.yaml index 979478e..b46505e 100644 --- a/charts/currents/values.yaml +++ b/charts/currents/values.yaml @@ -122,7 +122,8 @@ currents: # @section -- Frequently Used key: token redis: - # -- (tpl) set the redis hostname to talk to + # -- (tpl) set the redis hostname to talk to. The default names the bundled Redis's service, + # which only exists when `redis.enabled` is true — point this at your own server otherwise. # @default -- `{{ .Release.Name }}-redis-master` host: "{{ .Release.Name }}-redis-master" # -- (tpl) hostname for read-only traffic. A managed Redis usually publishes a separate diff --git a/docs/configuration.md b/docs/configuration.md index 222b2f0..47f4beb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -102,7 +102,7 @@ The following table lists the configurable parameters of the `currents` chart an | currents.email.inviteBcc | string | `""` | BCC address for invitation emails | | currents.email.linksBaseUrl | string | `""` | Base URL for links in emails (defaults to APP_BASE_URL if empty) | | currents.ingress.enabled | bool | `false` | Whether to enable the both default ingresses (server, and director) | -| currents.redis.host | tpl | `{{ .Release.Name }}-redis-master` | set the redis hostname to talk to | +| currents.redis.host | tpl | `{{ .Release.Name }}-redis-master` | set the redis hostname to talk to. The default names the bundled Redis's service, which only exists when `redis.enabled` is true — point this at your own server otherwise. | | currents.redis.readerHost | tpl | `""` | hostname for read-only traffic. A managed Redis usually publishes a separate reader endpoint; leaving this empty sends reads to `host`, which is the primary. | | currents.redis.port | int | `6379` | The port to connect on | | currents.redis.tls.enabled | bool | `false` | Connect with `rediss://`. Set this for a managed Redis with encryption in transit. | diff --git a/docs/eks/dependencies.md b/docs/eks/dependencies.md index 0ee1efd..b0c3410 100644 --- a/docs/eks/dependencies.md +++ b/docs/eks/dependencies.md @@ -100,12 +100,21 @@ This will setup a 1-node 1-shard ClickHouse Replicated Server (10Gb Storage) --set operator.enabled=false ``` -### Redis (optional — bundled by default) +### Redis -The chart ships a Redis and uses it unless you point it elsewhere, so there is nothing to do -here for a standard install. Replace it if you would rather not operate it yourself. +Redis is required, and the chart does not deploy one unless you ask it to. Pick one of: -**What the replacement has to provide.** Currents stores orchestration state as JSON documents +- **Bundled** — set `redis.enabled: true`, as the [quickstart](./quickstart.md) does. Convenient, + and usable in production, but it has no high availability during a version upgrade. +- **Your own** — leave `redis.enabled` at its default of `false` and point the chart at an + existing server, as below. This is the option to take if you already operate Redis, or would + rather not operate one at all. + +`currents.redis.host` defaults to the bundled Redis's service name, so it only resolves when +`redis.enabled` is `true`. Setting one without the other leaves the install pointed at a service +that was never deployed. + +**What your own Redis has to provide.** Currents stores orchestration state as JSON documents and reads them from inside Lua scripts, so the server must support the `JSON.GET` / `JSON.SET` commands. On ElastiCache that means **Redis 6.2.6 or newer, or Valkey**; older engines will start the app but fail spec claiming under load. Run a real test run against it before cutting over, not @@ -130,7 +139,8 @@ the client takes a static credential and has nothing to refresh a short-lived on `rediss://` selects encryption in transit. Use `redis://` only if the group has it disabled. -2. Point the chart at it and turn the bundled Redis off: +2. Point the chart at it, leaving the bundled Redis off (set it explicitly if you previously + turned it on): ```yaml redis: