Announce every state another module reads as readable, and refuse a catalogue that does not (issue 774)

The controller grants a read only from the owner's announcement (ADR 0495 clause 3). gitea announced `bound`
unreadable, so mesh-provisioner bound no forge account; disk-load announced `readings` unreadable, refused to restic,
nfs-server and mounts; restic announced `good` unreadable, which the self-check reads. Nothing in the catalogue
compared readers with announcements since the gate's manifest check went (clause 5).

Quick fix: mark those three states readable. Thorough fix: also checks/readable, which reads every owner's Go and
TypeScript announcement and refuses any `reads` of a state it does not announce readable, so the next owner to
announce cannot repeat it. Chose thorough: the same kind hit three owners at once.
Reads of other repositories' owners (mesh-controller.lease, mesh-grants.accounts) are not judged here.
This commit is contained in:
2026-10-30 18:07:53 +01:00
parent bc27189627
commit aec2ed3c24
9 changed files with 450 additions and 10 deletions
+2 -1
View File
@@ -24,7 +24,8 @@ import (
func logf(format string, args ...any) { fmt.Fprintf(os.Stderr, "[disk-load] "+format+"\n", args...) }
func main() {
if err := stdio.Keeps(announce.State{Name: StateReadings}); err != nil {
// Readable: restic, nfs-server, mounts and the self-check read it (their manifests' `reads`); checks/readable holds it.
if err := stdio.Keeps(announce.State{Name: StateReadings, Readable: true}); err != nil {
logf("%v", err)
os.Exit(1)
}
+4 -3
View File
@@ -1,10 +1,11 @@
// The states this module keeps on the bus and whether other modules may read them (novox/hq ADR 0495 clause 2; issue
// 1790): the module says it itself, from its code, as it starts (index.ts calls keeps with this list), and a reader is
// granted what it said (clause 3). A state is readable when another module's manifest reads it; none reads gitea's.
// test/kept.test.ts holds the list to the manifest's `state`.
// granted what it said (clause 3). A state is readable when another module's manifest reads it: mesh-provisioner reads
// `bound` (the forge accounts it binds). test/kept.test.ts holds the list to the manifest's `state`, and the catalogue's
// checks/readable every state another manifest reads to a readable announcement.
export const KEPT: readonly { readonly name: string; readonly readable?: boolean }[] = [
{ name: "announced" },
{ name: "bound" },
{ name: "bound", readable: true },
];
/** The announcement of these states, in the layout mesh-sdk's go/announce reads (novox/hq ADR 0495 clause 2): version 1,
+6 -5
View File
@@ -2,20 +2,21 @@ import assert from "node:assert/strict";
import { test } from "node:test";
import { readFileSync } from "node:fs";
// 1790: the states the module announces are the states its manifest declares, no more, no fewer; none is readable
// because no other module's manifest reads one of them.
// 1790: the states the module announces are the states its manifest declares, no more, no fewer; `bound` is readable,
// because mesh-provisioner's manifest reads it (the sweep of 2026-10-30, issue 774: announced unreadable, the read was
// refused and no forge account was bound).
test("the states the module announces are the states its manifest declares", async () => {
const { KEPT } = await import("../kept.ts");
const manifest = JSON.parse(readFileSync(new URL("../module.json", import.meta.url), "utf8"));
const declared = (manifest.state as (string | { name: string })[]).map((s) => (typeof s === "string" ? s : s.name)).sort();
assert.deepEqual(KEPT.map((k) => k.name).sort(), declared);
assert.ok(KEPT.every((k) => !k.readable), "no other module's manifest reads a state of gitea");
assert.deepEqual(KEPT.filter((k) => k.readable).map((k) => k.name), ["bound"], "bound alone is readable: mesh-provisioner reads it");
});
test("the announcement has the layout mesh-sdk's go/announce reads: version 1, the module, the node, the states sorted by name", async () => {
const { statesAnnouncement } = await import("../kept.ts");
assert.deepEqual(statesAnnouncement("gitea", "ace"), { v: 1, module: "gitea", node: "ace", states: [{ name: "announced" }, { name: "bound" }] });
assert.deepEqual(statesAnnouncement("gitea", undefined), { v: 1, module: "gitea", states: [{ name: "announced" }, { name: "bound" }] });
assert.deepEqual(statesAnnouncement("gitea", "ace"), { v: 1, module: "gitea", node: "ace", states: [{ name: "announced" }, { name: "bound", readable: true }] });
assert.deepEqual(statesAnnouncement("gitea", undefined), { v: 1, module: "gitea", states: [{ name: "announced" }, { name: "bound", readable: true }] });
});
test("index.ts says the announcement as the event `states` before the tools are served", () => {
+2 -1
View File
@@ -56,7 +56,8 @@ func main() {
case "awake":
counting(b)
case "":
if err := stdio.Keeps(announce.State{Name: health.GoodState}); err != nil {
// Readable: the self-check reads it (its manifest's `reads`); checks/readable holds it.
if err := stdio.Keeps(announce.State{Name: health.GoodState, Readable: true}); err != nil {
say("%v", err)
os.Exit(1)
}