// redis's provisioner — the adapter that makes redis a provider of the mesh `redis-cache` // interface. The reconcile loop, the contributions file, and reading the mesh's minted password are // the sdk harness's; this writes only the per-service half: how redis creates and removes a // per-consumer cache (novox/hq ADR 0039/0040/0048). // // The `redis-cache` interface: a consumer connects as `as` with the password the mesh minted, and // stores its keys under `:*`, isolated from every other consumer by an ACL user scoped to // exactly that prefix. // // **The login and password are the mesh's, not the provisioner's (ADR 0048).** The mesh derives the // login and hands it to both ends so they agree, and mints the password and delivers a copy to each. // redis creates exactly that login with exactly that password — a name or password the provisioner // invented is one the consumer could never present. import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner"; import { emit } from "@novox/mesh-sdk/events"; import { RedisClient } from "../client.js"; const redis = RedisClient.fromEnv(); /** Emit a lifecycle event without letting a broker hiccup fail the provisioning itself. */ async function announce(type: string, body: Record): Promise { try { await emit(type, body); } catch (err) { console.error(`[provisioner:redis-cache] emit ${type} failed: ${err}`); } } runProvisioner("redis-cache", { async create(p: Provision): Promise { // The keyspace is scoped to the consumer's own login, so one cannot read another's keys. const keyspacePrefix = p.as; await redis.createAclUser(p.as, p.password, keyspacePrefix); await announce("module.redis.cache.provisioned", { consumer: p.consumer ?? "", username: p.as, keyspacePrefix, }); }, async remove(p: { as: string }): Promise { await redis.deleteAclUser(p.as); await announce("module.redis.cache.deprovisioned", { username: p.as }); }, // This server keeps its ACL users in memory only, so a restart of it forgets every consumer while // this provisioner keeps running. Asked every minute, so a forgotten user is made again instead // of every consumer failing to authenticate in silence (novox/hq issue 120). async holds(p: Provision): Promise { return redis.holdsAclUser(p.as, p.password); }, });