diff --git a/test/integration/mesh.test.ts b/test/integration/mesh.test.ts index fd40bf4..0a1102e 100644 --- a/test/integration/mesh.test.ts +++ b/test/integration/mesh.test.ts @@ -774,6 +774,11 @@ test("rotating a credential moves both ends, and the old one stops working", { `"serves":{"realdatabase":{"port":5433}},` + `"needs":{"superuser":"${store}/superuser"},` + `"grants":{"realdatabase":"${store}/grants"},` + + // Both halves. `grants` is where each consumer's sealed password lands; `receives` is the + // manifest saying who asked and for what. Without the second the provisioner finds a + // directory of unexplained secrets and says nothing has been granted — which is true, and + // reads exactly like a credential that was never delivered. + `"receives":{"realdatabase":"${store}/grants/mesh.json"},` + `"listens":[{"port":5433,"from":"mesh","why":"a database the mesh provisions"}],` + `"resources":[` + `{"id":"state","type":"directory","path":"${store}","mode":"0755"},` + @@ -937,3 +942,78 @@ test("a route is a grant: a workload is reached by the name it asked for", { } assert.ok(gone, "the proxy still serves a name whose module was unassigned"); }); + +test("model access is answered by a record, and the key the mesh took is one it cannot read", { + skip, timeout: 900_000, +}, async () => { + // novox/hq ADR 0024. The first provision no machine answers: a hosted model is on nobody's + // node and is reached over the public internet, so the rule that refuses two ends sharing no + // private network must not apply to it. + await must("anchor", `printf %s '{"module":"assistant","version":"1",` + + `"requires":["model-access"],` + + `"binds":{"model-access":"/etc/assistant/model.json"},` + + `"secrets":{"model-access":"/etc/assistant/key"},` + + `"resources":[{"id":"dir","type":"directory","path":"/etc/assistant","mode":"0755"}]}' ` + + `> /tmp/assistant.json`); + await must("anchor", `docker cp /tmp/assistant.json mesh-control:/assistant.json`); + await mesh("module add /assistant.json"); + await mesh("assign laptop assistant"); + + await mesh(`licence add anthropic personal --serves '{"model":"a-model"}'`); + await mesh(`licence add anthropic the-organisation --serves '{"model":"a-model"}'`); + + // Refused until somebody says which, and the refusal names both candidates and the command. + // ADR 0024 warns this will be felt — which is correct, and correct is not the same as usable. + const refused = await on("anchor", `docker exec mesh-control /mesh-control plan laptop`); + assert.ok(!refused.ok, `a consumer was given model access without anybody saying which:\n${refused.out}`); + for (const want of ["personal", "the-organisation", "licence use"]) { + assert.match(refused.out, new RegExp(want), + `the refusal does not name ${want}:\n${refused.out}`); + } + + await mesh("licence use personal laptop assistant"); + + // Chosen, and still no key: the mesh has one thing to deliver and has not been given it. + const noKey = await on("anchor", `docker exec mesh-control /mesh-control plan laptop`); + assert.ok(!noKey.ok, `a module was planned with a licence that has no key:\n${noKey.out}`); + assert.match(noKey.out, /licence key personal/, noKey.out); + + // The accept verb. Given on standard input rather than as an argument, because a key in a + // command line is a key in shell history and in every process listing taken while it ran. + const secret = "sk-test-" + "0123456789abcdef".repeat(2); + const accepted = await must("anchor", + `printf %s ${quote(secret)} | docker exec -i mesh-control /mesh-control licence key personal`); + assert.match(accepted, /sealed to 1 holder/, accepted); + assert.doesNotMatch(accepted, new RegExp(secret), + "the key was echoed back, so the one copy that matters is on a terminal"); + + await mesh("push laptop"); + await new Promise((r) => setTimeout(r, 15_000)); + + // What is public arrives, and says it is a record rather than leaving an empty address that a + // reader would take for something the mesh failed to fill in. + const bound = await must("laptop", `cat /etc/assistant/model.json`); + assert.match(bound, /personal/, `the consumer was not told which licence it is on:\n${bound}`); + assert.match(bound, /a-model/, `what the licence serves did not arrive:\n${bound}`); + assert.match(bound, /not a machine/, `the binding leaves an unexplained empty address:\n${bound}`); + + // And the key arrives, readable only by this machine. + assert.equal((await must("laptop", `cat /etc/assistant/key`)).trim(), secret, + "the key that arrived is not the key that was given"); + assert.match(await must("laptop", `stat -c %a /etc/assistant/key`), /^600/); + + // The mesh cannot read it back. This is the whole argument: what is stored is unusable by + // whoever holds it, the control plane included. + const stored = await must("anchor", + `docker exec mesh-store psql -U postgres -d licences -qAt ` + + `-c "select coalesce(sealed,'') from licence_holder"`); + assert.ok(stored.trim().length > 0, "nothing was stored, so nothing was sealed"); + assert.doesNotMatch(stored, new RegExp(secret), + "the key is in the control plane's own database in the open"); + + // Nor is it anywhere it could have been read on the way. + for (const where of ["/var/lib/mesh-host/declared.json", "/var/lib/mesh-host/state.json"]) { + const held = await on("laptop", `grep -c ${quote(secret)} ${where} 2>/dev/null || echo 0`); + assert.equal(held.out.trim(), "0", `the key is in the open in ${where}`); + } +});