Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
026421fd6e | ||
|
|
af89bb11ff | ||
|
|
7c18cdbd39 | ||
|
|
4fb16b2e6b | ||
|
|
c5af8635c8 | ||
|
|
87366c5f36 | ||
|
|
f8ca36aacf | ||
|
|
812355bf31 | ||
|
|
016ddb2b3a | ||
|
|
ea17bf46d2 | ||
|
|
4258f01614 | ||
|
|
4d9b4fdfa6 | ||
|
|
eff11b1d4d |
@@ -33,7 +33,7 @@
|
|||||||
"type": "file",
|
"type": "file",
|
||||||
"path": "/etc/fail2ban/jail.local",
|
"path": "/etc/fail2ban/jail.local",
|
||||||
"mode": "0644",
|
"mode": "0644",
|
||||||
"content": "[INCLUDES]\n\nbefore = paths-arch.conf\n\n[DEFAULT]\n\n# Never act on the machine itself or on a tunnel peer: the mesh's private range is\n# ${machine:mesh-range}, named here rather than written as a value the module cannot\n# know (novox/hq ADR 0112). Without this, fail2ban could ban the mesh's own nodes.\nignoreip = 127.0.0.1/8 ::1 ${machine:mesh-range}\n\nbantime = 10m\nfindtime = 10m\nmaxretry = 5\n\nbanaction = ufw\nbanaction_allports = iptables-allports\n\n[sshd]\nenabled = true\nport = ssh\nlogpath = %(sshd_log)s\nbackend = %(sshd_backend)s\n"
|
"content": "[INCLUDES]\n\nbefore = paths-arch.conf\n\n[DEFAULT]\n\n# Never act on the machine itself or on a tunnel peer: the mesh's private range is\n# ${machine:mesh-range}, named here rather than written as a value the module cannot\n# know (novox/hq ADR 0112). Without this, fail2ban could ban the mesh's own nodes.\nignoreip = 127.0.0.1/8 ::1 ${machine:mesh-range}\n\nbantime = 10m\nfindtime = 10m\nmaxretry = 5\n\n# Ban through iptables, not through a firewall front-end the machine may not have. ufw is\n# installed on two of this mesh's machines and absent on the other two, and fail2ban finds out\n# only at ban time: the service reports healthy, the jail counts the attempt, the ban command\n# exits 127, and nothing is blocked. Proven on 2026-09-28 -- 'ufw: command not found' on a\n# machine the mesh reported as protected.\n#\n# The action below is this module's own, already used by the recidive jail on every machine\n# here, and it bans in DOCKER-USER as well as INPUT, so a container's published port is\n# covered too.\nbanaction = iptables-allports-dualchain\nbanaction_allports = iptables-allports-dualchain\n\n[sshd]\nenabled = true\nport = ssh\nlogpath = %(sshd_log)s\nbackend = %(sshd_backend)s\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "jail-sshd",
|
"id": "jail-sshd",
|
||||||
@@ -42,6 +42,14 @@
|
|||||||
"mode": "0644",
|
"mode": "0644",
|
||||||
"content": "[sshd]\nenabled = true\nport = ssh\nlogpath = %(sshd_log)s\nbackend = %(sshd_backend)s\nmaxretry = 5\n"
|
"content": "[sshd]\nenabled = true\nport = ssh\nlogpath = %(sshd_log)s\nbackend = %(sshd_backend)s\nmaxretry = 5\n"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "log",
|
||||||
|
"type": "file",
|
||||||
|
"path": "/var/log/fail2ban.log",
|
||||||
|
"mode": "0640",
|
||||||
|
"create-once": true,
|
||||||
|
"content": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "jail-recidive",
|
"id": "jail-recidive",
|
||||||
"type": "file",
|
"type": "file",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ COPY . .
|
|||||||
# The compiler is invoked by its real path rather than through node_modules/.bin, whose entries are
|
# The compiler is invoked by its real path rather than through node_modules/.bin, whose entries are
|
||||||
# symlinks to a launcher that requires its library relatively — resolved away when the base image
|
# symlinks to a launcher that requires its library relatively — resolved away when the base image
|
||||||
# was assembled.
|
# was assembled.
|
||||||
RUN node /app/node_modules/typescript/bin/tsc pg.d.ts store.ts index.ts tools/index.ts \
|
RUN node /app/node_modules/typescript/bin/tsc pg.d.ts store.ts index.ts tools/index.ts prepare/index.ts \
|
||||||
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
--module NodeNext --moduleResolution NodeNext --target ES2022 --outDir dist
|
||||||
|
|
||||||
# **A module may need something the base image does not carry.** The base holds what every module
|
# **A module may need something the base image does not carry.** The base holds what every module
|
||||||
@@ -48,3 +48,8 @@ COPY --from=build /deps/node_modules /app/modules/mesh-catalog/node_modules
|
|||||||
# to listen for what the builder announces. Serve binds the broker first, then imports these, so
|
# to listen for what the builder announces. Serve binds the broker first, then imports these, so
|
||||||
# `on()` has something to subscribe to.
|
# `on()` has something to subscribe to.
|
||||||
ENV MESH_TOOL_MODULES=/app/modules/mesh-catalog/dist/index.js,/app/modules/mesh-catalog/dist/tools/index.js
|
ENV MESH_TOOL_MODULES=/app/modules/mesh-catalog/dist/index.js,/app/modules/mesh-catalog/dist/tools/index.js
|
||||||
|
|
||||||
|
# And what prepares this module's state, for the runtime's `prepare` mode (novox/hq ADR 0135). Named
|
||||||
|
# here, beside the entrypoints above, because the module knows which of its files prepares its state
|
||||||
|
# and nothing else could: the mesh asks one word and this says what answers it.
|
||||||
|
ENV MESH_PREPARE=/app/modules/mesh-catalog/dist/prepare/index.js
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ import { Graph, type Made } from "./store.js";
|
|||||||
|
|
||||||
const graph = Graph.fromEnv();
|
const graph = Graph.fromEnv();
|
||||||
|
|
||||||
// Before subscribing, and idempotent. The runtime is restarted until its store is reachable, which
|
// The schema is not brought up here. The mesh prepares this module's state before it starts this
|
||||||
// is the same arrangement model-usage uses: a schema step that had to reach the provider over the
|
// version, and does not start it if that failed (novox/hq ADR 0135) — see prepare/index.ts. Doing it
|
||||||
// overlay would block the very apply that brings the overlay up.
|
// at start made a schema that could not be reached a crash loop instead of a stop, with the graph
|
||||||
await graph.migrate();
|
// keeping a gap and nothing saying so. The reason it used to be here — that a step blocking the apply
|
||||||
|
// would block the very apply that brings the overlay up — stopped being true when a step's failure
|
||||||
|
// became this module's business and not the machine's (ADR 0136).
|
||||||
|
|
||||||
/** What the builder says when it has built something. */
|
/** What the builder says when it has built something. */
|
||||||
interface Built {
|
interface Built {
|
||||||
@@ -47,7 +49,15 @@ interface Built {
|
|||||||
replay?: boolean;
|
replay?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
await on("mesh-build-machine.built", async (event) => {
|
/**
|
||||||
|
* What a build means for the graph, wherever it came from.
|
||||||
|
*
|
||||||
|
* Two emitters say the same thing and neither is a mistake: the build machine says it as it happens,
|
||||||
|
* and the control plane says what it already held when this module asks what it missed
|
||||||
|
* (novox/hq ADR 0134). A replay is marked as one in its body, so nothing acts on a module that moved
|
||||||
|
* months ago — see `replay` above.
|
||||||
|
*/
|
||||||
|
const placeTheBuild = async (event: { body: unknown }): Promise<void> => {
|
||||||
const body = event.body as Built;
|
const body = event.body as Built;
|
||||||
if (!body.module || !body.commit) {
|
if (!body.module || !body.commit) {
|
||||||
// Said rather than dropped: a build that announced itself without saying what it built is a
|
// Said rather than dropped: a build that announced itself without saying what it built is a
|
||||||
@@ -89,7 +99,11 @@ await on("mesh-build-machine.built", async (event) => {
|
|||||||
because: next.because,
|
because: next.because,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// As it happens, and what the mesh already held when this module asked what it missed.
|
||||||
|
await on("mesh-build-machine.built", placeTheBuild);
|
||||||
|
await on("mesh-controller.built-before", placeTheBuild);
|
||||||
|
|
||||||
// **And ask for what was built before this catalogue existed** (novox/hq 04-ISSUES/050).
|
// **And ask for what was built before this catalogue existed** (novox/hq 04-ISSUES/050).
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -29,13 +29,16 @@
|
|||||||
"broker": "/var/lib/mesh/mesh-catalog/broker"
|
"broker": "/var/lib/mesh/mesh-catalog/broker"
|
||||||
},
|
},
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"mesh-build-machine.built"
|
"mesh-build-machine.built",
|
||||||
|
"mesh-controller.built-before"
|
||||||
],
|
],
|
||||||
"emits": [
|
"emits": [
|
||||||
"registered",
|
"registered",
|
||||||
"upgraded",
|
"upgraded",
|
||||||
"rebuild-needed"
|
"rebuild-needed",
|
||||||
|
"catching-up"
|
||||||
],
|
],
|
||||||
|
"prepares": true,
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"id": "mesh-state",
|
"id": "mesh-state",
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// The catalogue's state, brought to the shape this version needs (novox/hq ADR 0135).
|
||||||
|
//
|
||||||
|
// **The mesh runs this before the version that needs it, and does not start that version if it
|
||||||
|
// fails** — and the refusal reaches this module and nothing else on the machine
|
||||||
|
// (novox/hq ADR 0136). That is the whole difference from where this used to happen: at start, inside
|
||||||
|
// the runtime, a schema that could not be brought up was a crash loop, the graph kept a gap, and
|
||||||
|
// nothing anywhere said so.
|
||||||
|
//
|
||||||
|
// Nothing here connects to the broker. Preparation runs before the version that would use it, so
|
||||||
|
// there is nothing yet to talk to; the runtime's `prepare` mode imports this and awaits it, and this
|
||||||
|
// process exiting non-zero is how the host knows not to start the runtime.
|
||||||
|
import { Graph } from "../store.js";
|
||||||
|
|
||||||
|
const graph = Graph.fromEnv();
|
||||||
|
await graph.migrate();
|
||||||
|
console.log("[mesh-catalog] the module graph's schema is what this version needs");
|
||||||
|
await graph.close();
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
"pg.d.ts",
|
"pg.d.ts",
|
||||||
"store.ts",
|
"store.ts",
|
||||||
"index.ts",
|
"index.ts",
|
||||||
"tools/index.ts"
|
"tools/index.ts",
|
||||||
|
"prepare/index.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user