Feature Specification: Component valkey
Created: 2026-10-07
Status: Implemented
Input: Reconstructed from the code at 093e68b6f (core/components/shared/valkey/default.nix, core/components/shared/valkey/valkey.nix, core/components/shared/valkey/publish-coordinates.nix, core/components/shared/valkey/restore-stream.py, core/components/shared/valkey/valkey-test.nix, core/components/hosting-provider/redis-commander.nix, core/setups/hosts-common.nix, core/resources/redis/), pull requests #1381, #1560, #1571, #1595, #1597, #1656, #1741, #1747, #1749, #1760, #1809, #1822, #1835, #1852 and #1996.
User Scenarios & Testing (mandatory)
User Story 1 - Give each application its own cache instance (Priority: P1)
Applications of a group need a redis-compatible cache. The valkey
component declares provides = [ "redis" ], so the node answers the redis
requests of the group (see redis). The
module valkey.nix runs one services.redis.servers.<ns> instance with
pkgs.valkey for each entry of fediversity.valkey.namespaces. The port of
each instance comes from portOf namespace in core/resources/redis/port.nix.
Why this priority: the applications do not start without their cache.
Independent Test: test-valkey-service. It runs the node with the
OpenBao valkey database engine, and checks the ACL, the password rotation
and the backup to garage. contracts-redis-valkey,
nix-unit-setups-valkey-config, nix-unit-setups-valkey-gate and
nix-unit-resources-redis-port cover the decisions.
Acceptance Scenarios:
- Given a group whose nodes request
redis, When the setups evaluate, Thenfediversity.valkey.namespaceslists one namespace per request andhostis thevalkeymachine. - Given an empty
namespaceslist, When the node evaluates, Then it runs no instance. - Given a TLS certificate, When an instance starts, Then the plain
portis0,tls-portis the derived port andtls-auth-clientsisno.
User Story 2 - Keep the cache passwords secret and rotated (Priority: P1)
Each instance has two users. The default user has a bootstrap password.
The generateFiles instance valkey-<ns> makes it with openssl rand -hex 32
(owner redis-<ns>, mode 0400). The password stays the same across
deploys. The app user gets a password that OpenBao rotates every 24h
(see generateFiles).
Why this priority: an instance without a password is open to every node on the network.
Independent Test: test-valkey-service.
Acceptance Scenarios:
- Given a missing bootstrap password, When the instance starts, Then it fails, because the unit
Requiresgenerate-varsand asserts that the password is not empty. - Given a rotated
apppassword atdatabase/static-creds/<ns>(<ns>-<deployment namespace>in a tenant deployment), Whenvalkey-<ns>-apply-passwordruns, Then it applies the password withACL SETUSER ... resetpassover stdin, reads it back and runsACL SAVE. - Given a new render of the vars, When
services.vars-openbao.reloadUnitsfires, Thenvalkey-<ns>-apply-passwordruns again.
User Story 3 - Back up and restore a namespace (Priority: P2)
An admin needs a copy of the cache data. A timer streams valkey-cli --rdb -
to restic through the streamingBackup contract, into the garage of the
group (see garage). The restore program
restore-stream.py boots a scratch valkey on a fixed offset port. It sends
RESTORE ... REPLACE frames through valkey-cli --pipe to the live
instance, without a restart.
Why this priority: the cache can be rebuilt, but some applications keep queues in it.
Independent Test: test-valkey-service.
Acceptance Scenarios:
- Given
backup.enable = true, When the timer fires (OnCalendar = "daily",Persistent = true), Then restic writes a snapshot to the bucketbuckets.${namespace}. - Given a snapshot, When an admin runs the restore, Then the live instance gets the keys and keeps running.
User Story 4 - Inspect the cache in a web UI (Priority: P3)
An admin inspects the keys of the instances. The component requests
"redis-ui".oidc with forward-auth on redis.<apex> (the label is in
admin-ui-subdomain.nix, which served-domains.nix also reads), for the group
fediversity-admins with one_factor. The nixos-module request of the
component imports redis-commander.nix, which sets its units only when
fediversity.forwardAuth.domains has an entry for 127.0.0.1:8081 and the
node serves at least one namespace. The redis-commander-config oneshot
renders /run/redis-commander/node-config.env with one connection per
namespace. The connection uses TLS with the PKI CA when the instances serve
TLS, and plain text otherwise. redis-commander listens on 127.0.0.1:8081.
Why this priority: the UI is for diagnosis only.
Independent Test: nix-unit-setups-valkey-config checks that the redis-commander-config oneshot reads the PKI CA, is ordered after the runtime fetch of the CA, and owns its retry. It also checks that the vhost of the UI asks authelia for each request and sends the request to the address and port that redis-commander binds, that the firewall does not open that port, and that a node without the forwardAuth entry of the UI runs no redis-commander. No check starts redis-commander itself.
Acceptance Scenarios:
- Given
deployAdminUIsis false, When the setups evaluate, Then theapplyof theoidcresource does not deploy the UI. - Given an empty
namespaceslist, When the node evaluates, Thenredis-commanderis not enabled.
Edge Cases
- A null
fediversity.valkey.tls.certFiledisables TLS. The instances then serve plaintext on the management network. - The setup gives the valkey node a certificate only when the policy
resources."cache".redis.tls.enableis true. Its default is true when the PKI plan of the deployment holds the valkey host. Each consumer reads the same value from thetlsfield of itsrediscontract result, so the server and its clients agree on TLS or plain text. - A first boot adds an
appline without a password to the ACL file. A later start keeps an existingappline and rewrites only thedefaultline. valkey-<ns>-apply-passwordretries without a start limit. It uses only soft edges, so a failure does not stop the instance.valkey-publish-coordinatesruns only whennamespacesis not empty,hostis set and the vars go to OpenBao. It writes onlyhost=<host>to<kvPrefixOf>/public/valkey-<ns>/instance, because a consumer derives the port.- The CLI tools get the
defaultpassword inREDISCLI_AUTH, not in argv.
Requirements (mandatory)
Functional Requirements
- FR-001: The component MUST declare
provides = [ "redis" ]and usepkgs.valkey. - FR-002: The implementation MUST request
"spiffe".spiffe.enable = trueand importvalkey.nixthrough"code".nixos-module. - FR-003: The implementation MUST request
"valkey-cache".rediswithnamespace = "valkey"andconsumerName = "valkey". - FR-004:
valkey.nixMUST run one instance per namespace on the port thatportOfderives, and none for an empty list. - FR-005: An instance MUST NOT start without a non-empty bootstrap password.
- FR-006:
valkey-<ns>-apply-passwordMUST apply the rotatedapppassword from OpenBao and verify it beforeACL SAVE. - FR-007: The backup MUST stream to restic through the
streamingBackupcontract. It MUST get the S3 key pair as the systemd credentialsrestic-s3. - FR-008: The restore MUST NOT restart the live instance.
- FR-009:
redis-commanderMUST listen only on127.0.0.1:8081and rely on forward-auth for authentication.
Key Entities
fediversity.valkey: the optionsnamespaces,host,tls.{certFile,keyFile,issueService,caCertFile,serverName}andbackup.{enable,garageApiUrl,buckets,timerConfig}.- Instance
<ns>:services.redis.servers.<ns>with RDB saves at900 1,300 10and60 10000, andappendOnly. valkey-tls: the group that reads the TLS key. The setup issues the certificate fromopenbao-pkiwithkeyMode = "0640"and restartsredis-<ns>.serviceon renewal (seessl).valkey-publish-coordinates: the unit that publishes the host of each instance to OpenBao KV.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001:
test-valkey-serviceandcontracts-redis-valkeypass. - SC-002:
nix-unit-setups-valkey-config,nix-unit-setups-valkey-gate,nix-unit-setups-valkey-coordinates,nix-unit-setups-valkey-tls-agreementandnix-unit-resources-redis-portpass. - SC-003:
applications-catalogpasses with thevalkeyicon fromsources.valkey-assets.
Assumptions
- The
hosting-providerandoperatorgroups both declare avalkeymachine. - The setup sets
backup.garageApiUrlfrom theexternalgarage resource andbucketsfromcacheBackupBucketFor. - The OpenBao valkey database engine (
hosting-provider/openbao/valkey-engine.nix) owns the rotation period. - The colors of the icon must not change, because of the trademark policy of valkey.
Known Deviations
redis-commanderhas no native OIDC. It relies on forward-auth only.