Review of 081: the bed asserts baserow chose its own cache and it challenges for its password; the check reads only what a module hands its software; stale comments

This commit is contained in:
2026-09-22 12:34:59 +02:00
parent 36f2fd1c2c
commit 2dfffd6dac
5 changed files with 40 additions and 16 deletions
+2 -3
View File
@@ -6,9 +6,8 @@
# the baserow and letta CONSUMERS of the one store (baserow keeps its cache inside its own container,
# novox/hq 081). Both
# machines sit on one shared segment and enrol into the one mesh; only enrolment crosses to anchor,
# over the underlay both machines already share. Provider and consumers are co-located on laptop, so
# no cross-node module comms and no overlay are needed — and the 5432-vs-foundation conflict is gone
# because the foundation store is on the OTHER node.
# over the underlay both machines already share. The consumers' databases are minted on the one
# foundation store on anchor and reached over the overlay; laptop runs no provider of its own.
scenario: two-node-db
segments:
+1 -1
View File
@@ -3,7 +3,7 @@
# scenarios/whole-mesh-novox.yml; same topology, a different (larger, media-heavy) module set.
#
# Foundation (store, broker, control) rides `anchor` and NOTHING else; ALL of ace's services ride the
# `ace` node. An overlay is placed so the two DB consumers (baserow, letta) reach the postgres/redis
# `ace` node. An overlay is placed so the two DB consumers (baserow, letta) reach the postgres
# providers co-located with them. The media stack (sonarr/radarr/lidarr/plex/bazarr/nzbget/
# qbittorrent/bookshelf) shares the operator-owned library directories under /services/media (ADR
# 0051 `accesses`); the test pre-creates them on the node, as the operator would, before the push —
@@ -21,9 +21,23 @@ import { catalogueIsPresent, catalogueDir } from "./integration/harness.ts";
const CACHE = "redis-cache";
/** What a module hands its software: every file it writes, and every container's environment and
* arguments. A comment, a `why`, or a declared exception is not handed to anything. */
function handedToSoftware(manifest: string): string[] {
const m = JSON.parse(manifest) as { resources?: Record<string, unknown>[] };
const out: string[] = [];
for (const r of m.resources ?? []) {
if (typeof r["content"] === "string") out.push(r["content"] as string);
if (r["env"] && typeof r["env"] === "object") out.push(...Object.values(r["env"] as Record<string, string>).map(String));
if (Array.isArray(r["args"])) out.push(...(r["args"] as unknown[]).map(String));
}
return out;
}
/** Whether a manifest hands its software the login it is granted for the cache. */
function presentsTheLogin(manifest: string): boolean {
return manifest.includes(`\${bound:${CACHE}:as}`);
const login = `\${bound:${CACHE}:as}`;
return handedToSoftware(manifest).some((given) => given.includes(login));
}
test("the check sees a module that hands its software the password and not the login", () => {
@@ -33,6 +47,11 @@ test("the check sees a module that hands its software the password and not the l
{ id: "env", type: "file", path: "/x", content: `USER=\${bound:${CACHE}:as}\nPASSWORD=\${secret:${CACHE}}\n` }] });
assert.equal(presentsTheLogin(passwordOnly), false);
assert.equal(presentsTheLogin(withLogin), true);
// Named only where no software reads it — a declared reason — is not presenting it.
const onlyInAReason = JSON.stringify({ module: "m", requires: [CACHE], resources: [
{ id: "srv", type: "container", name: "s", image: "x", env: { PASSWORD: `\${secret:${CACHE}}` },
"secrets-in-environment": `the login is \${bound:${CACHE}:as}` }] });
assert.equal(presentsTheLogin(onlyInAReason), false);
});
test("every catalogue module that takes the shared cache presents the login it was granted", (t) => {
+15 -9
View File
@@ -28,7 +28,7 @@
* scripts/build-module-runtime.sh postgres /tmp/postgres.tar
* scripts/build-module-runtime.sh baserow /tmp/baserow.tar
* scripts/build-module-runtime.sh letta /tmp/letta.tar
* The service images (postgres:17-alpine, baserow/baserow:latest, letta/letta:latest)
* The service images (postgres, baserow, letta, each pinned by digest)
* must be in the local daemon too; scenarios/two-node-db.yml stocks all of them, and each node pulls
* what it runs from the scenario's own registry by digest.
*/
@@ -486,19 +486,25 @@ test("consumers on a joined node get their databases from the one foundation sto
}
// baserow's cache is its own: with no REDIS_HOST the image runs one inside the container, under a
// password it makes itself, and the mesh grants nothing (novox/hq 081). Up means it answers on
// loopback — PONG, or the challenge for the password it holds — and baserow logged no refusal.
// password it makes itself, and the mesh grants nothing (novox/hq 081). Three things say so:
// baserow said it chose its own; the cache answers on loopback with the challenge for that password
// (PONG would be a cache anyone can use, and fails); and nothing was refused a login.
const baserowLog = async () => (await on(NODE, `docker logs baserow 2>&1`)).out;
let chose = "";
let cache = { out: "", ok: false };
const untilCache = Date.now() + 180_000;
const untilCache = Date.now() + 240_000;
while (Date.now() < untilCache) {
chose = await baserowLog();
cache = await on(NODE, `docker exec baserow redis-cli -h 127.0.0.1 ping 2>&1`);
if (/PONG|NOAUTH/.test(cache.out)) break;
if (/Using embedded baserow redis/.test(chose) && /NOAUTH/.test(cache.out)) break;
await new Promise((r) => setTimeout(r, 5000));
}
assert.match(cache.out, /PONG|NOAUTH/, `baserow's own cache is not running inside its container:\n${cache.out}`);
const baserowLog = (await on(NODE, `docker logs baserow 2>&1 | tail -400`)).out;
assert.doesNotMatch(baserowLog, /WRONGPASS|NOPERM|NOAUTH|Error .*connecting to .*6379/i,
`baserow could not use its cache:\n${baserowLog.split("\n").filter((l) => /redis|6379|NOAUTH|WRONGPASS|NOPERM/i.test(l)).slice(-15).join("\n")}`);
assert.match(chose, /Using embedded baserow redis/,
`baserow did not start its own cache:\n${chose.split("\n").filter((l) => /redis/i.test(l)).slice(-15).join("\n")}`);
assert.match(cache.out, /NOAUTH/,
`baserow's own cache does not answer with its password challenge inside the container:\n${cache.out}`);
assert.doesNotMatch(chose, /WRONGPASS|NOPERM/,
`baserow was refused by its cache:\n${chose.split("\n").filter((l) => /WRONGPASS|NOPERM/.test(l)).slice(-15).join("\n")}`);
// Helper: wait for the mesh to write a consumer's bound file with an `as`, and parse it.
async function waitForBinding(path: string): Promise<{ as: string; provision: string }> {
+2 -2
View File
@@ -4,7 +4,7 @@
* whole-mesh-novox.test.ts; same harness and topology, a larger, media-heavy module set.
*
* Foundation (store, broker, control) rides `anchor` and nothing else; ALL of ace's services ride the
* `ace` node. An overlay is placed so the two DB consumers (baserow, letta) reach the postgres/redis
* `ace` node. An overlay is placed so the two DB consumers (baserow, letta) reach the postgres
* providers co-located with them. The media stack shares the operator-owned library directories
* under /services/media (ADR 0051 `accesses`): the mesh writes an `access` resource that CONFIRMS
* each path exists and mounts it, but creates and chowns none of it — so before() pre-creates those
@@ -264,7 +264,7 @@ test("the whole ace service set resolves, installs and converges on one node in
}, async () => {
for (const d of DROPPED) console.log(`DROPPED ${d.name}: ${d.why}`);
// The overlay, so a consumer's binding `at` is non-empty (baserow/letta reach postgres/redis).
// The overlay, so a consumer's binding `at` is non-empty (baserow/letta reach postgres).
await mesh("overlay place anchor --hub --endpoint 192.0.2.10:51820 --site lab");
await mesh(`overlay place ${NODE} --site lab`);
await mesh("assign anchor networking");