Merge pull request 'Resync hq ADR references (0044-0054 -> 0039-0049)' (#5) from feat/adr-ref-resync into main
This commit was merged in pull request #5.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
// The audit handler — appends one line per event to an append-only audit log. It is the whole of
|
// The audit handler — appends one line per event to an append-only audit log. It is the whole of
|
||||||
// the module's code: an audit logger is not a privileged component, only a module that listens to
|
// the module's code: an audit logger is not a privileged component, only a module that listens to
|
||||||
// everything and writes it down (novox/hq ADR 0046).
|
// everything and writes it down (novox/hq ADR 0041).
|
||||||
|
|
||||||
import { appendFile, mkdir } from "node:fs/promises";
|
import { appendFile, mkdir } from "node:fs/promises";
|
||||||
import { dirname } from "node:path";
|
import { dirname } from "node:path";
|
||||||
@@ -12,7 +12,7 @@ export function auditLogPath(env: NodeJS.ProcessEnv = process.env): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Append an event to the trail as one JSON line, keeping the metadata an audit needs first. The id
|
/** Append an event to the trail as one JSON line, keeping the metadata an audit needs first. The id
|
||||||
* is the event's own x-event-id (ADR 0047) — the handle a reader dedups the at-least-once trail on. */
|
* is the event's own x-event-id (ADR 0042) — the handle a reader dedups the at-least-once trail on. */
|
||||||
export async function record(event: Event, path: string): Promise<void> {
|
export async function record(event: Event, path: string): Promise<void> {
|
||||||
const line =
|
const line =
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Bazarr API client — bazarr's own code, living in the module (novox/hq ADR 0044). Bazarr
|
// The Bazarr API client — bazarr's own code, living in the module (novox/hq ADR 0039). Bazarr
|
||||||
// manages subtitles for a Sonarr/Radarr library: it tracks which episodes and movies are still
|
// manages subtitles for a Sonarr/Radarr library: it tracks which episodes and movies are still
|
||||||
// missing subtitles, searches providers for them, and records what it downloaded. This client
|
// missing subtitles, searches providers for them, and records what it downloaded. This client
|
||||||
// talks its /api surface (keyed by an X-API-KEY header); bazarr's tools and events import it.
|
// talks its /api surface (keyed by an X-API-KEY header); bazarr's tools and events import it.
|
||||||
@@ -36,7 +36,7 @@ export interface HistoryEntry {
|
|||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// the missing-subtitle list, and when it succeeds a subtitle appears in its history. That is worth
|
// the missing-subtitle list, and when it succeeds a subtitle appears in its history. That is worth
|
||||||
// announcing to the mesh.
|
// announcing to the mesh.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.bazarr.subtitle.downloaded — a subtitle was fetched for an episode or movie
|
// module.bazarr.subtitle.downloaded — a subtitle was fetched for an episode or movie
|
||||||
//
|
//
|
||||||
// Bazarr has nothing on the mesh it usefully reacts to (a download completing is Sonarr/Radarr's
|
// Bazarr has nothing on the mesh it usefully reacts to (a download completing is Sonarr/Radarr's
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-bazarr",
|
"name": "@novox/module-bazarr",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "bazarr — subtitle management. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "bazarr — subtitle management. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// bazarr's tools — its own code (novox/hq ADR 0044), importing bazarr's client. They return
|
// bazarr's tools — its own code (novox/hq ADR 0039), importing bazarr's client. They return
|
||||||
// structured data; the mesh serves them through the sdk's tool harness.
|
// structured data; the mesh serves them through the sdk's tool harness.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Bookshelf API client — bookshelf's own code, living in the module (novox/hq ADR 0044).
|
// The Bookshelf API client — bookshelf's own code, living in the module (novox/hq ADR 0039).
|
||||||
// Ported from the shared hal `arr` client, but self-contained: in nox each Servarr app owns its own
|
// Ported from the shared hal `arr` client, but self-contained: in nox each Servarr app owns its own
|
||||||
// copy, so a change to Bookshelf's API rebuilds only bookshelf and nothing else. Both this module's
|
// copy, so a change to Bookshelf's API rebuilds only bookshelf and nothing else. Both this module's
|
||||||
// tools and its events entrypoint import it, and nothing outside bookshelf does.
|
// tools and its events entrypoint import it, and nothing outside bookshelf does.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// download queue and turns its comings and goings into mesh events — the same mechanism radarr uses,
|
// download queue and turns its comings and goings into mesh events — the same mechanism radarr uses,
|
||||||
// applied to a Servarr book manager.
|
// applied to a Servarr book manager.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.bookshelf.book.grabbed — a release entered the queue (Bookshelf grabbed it)
|
// module.bookshelf.book.grabbed — a release entered the queue (Bookshelf grabbed it)
|
||||||
// module.bookshelf.download.completed — a release left the queue, imported. This routing key is
|
// module.bookshelf.download.completed — a release left the queue, imported. This routing key is
|
||||||
// what the plex module consumes (module.*.download.completed)
|
// what the plex module consumes (module.*.download.completed)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-bookshelf",
|
"name": "@novox/module-bookshelf",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "bookshelf — ebook/audiobook management (Readarr fork). Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "bookshelf — ebook/audiobook management (Readarr fork). Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// bookshelf's tools — ported from the shared hal `arr` sdk (novox/hq ADR 0044), importing
|
// bookshelf's tools — ported from the shared hal `arr` sdk (novox/hq ADR 0039), importing
|
||||||
// bookshelf's own client. They return structured data (not the pre-formatted text hal returned); the
|
// bookshelf's own client. They return structured data (not the pre-formatted text hal returned); the
|
||||||
// mesh serves them through the sdk's tool harness. Bookshelf has no calendar endpoint, so there is
|
// mesh serves them through the sdk's tool harness. Bookshelf has no calendar endpoint, so there is
|
||||||
// no calendar tool — matching hal, which excluded it from its calendar-capable apps.
|
// no calendar tool — matching hal, which excluded it from its calendar-capable apps.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// cloudflare-dns's own code (novox/hq ADR 0044). It provides the mesh `public-dns` interface
|
// cloudflare-dns's own code (novox/hq ADR 0039). It provides the mesh `public-dns` interface
|
||||||
// (ADR 0049): a public name that resolves to the mesh's public ingress. Cloudflare is one registrar
|
// (ADR 0044): a public name that resolves to the mesh's public ingress. Cloudflare is one registrar
|
||||||
// behind the neutral interface — a consumer names `public-dns`, never Cloudflare — so this file is
|
// behind the neutral interface — a consumer names `public-dns`, never Cloudflare — so this file is
|
||||||
// the only place Cloudflare's API appears, and swapping registrars swaps only this module.
|
// the only place Cloudflare's API appears, and swapping registrars swaps only this module.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export class CloudflareClient {
|
|||||||
|
|
||||||
static fromEnv(env: NodeJS.ProcessEnv = process.env): CloudflareClient {
|
static fromEnv(env: NodeJS.ProcessEnv = process.env): CloudflareClient {
|
||||||
// Which zone, domain and ingress are a mesh's own facts, not this module's — so they are
|
// Which zone, domain and ingress are a mesh's own facts, not this module's — so they are
|
||||||
// settings, merged into a config file the mesh manages (novox/hq ADR 0051), read here. The
|
// settings, merged into a config file the mesh manages (novox/hq ADR 0046), read here. The
|
||||||
// token is the one secret and stays an own-secret. Env is honoured as a fallback for a
|
// token is the one secret and stays an own-secret. Env is honoured as a fallback for a
|
||||||
// hand-run instance, but the deployed path is the config file settings fill.
|
// hand-run instance, but the deployed path is the config file settings fill.
|
||||||
const config = readConfig(env.MESH_CLOUDFLARE_CONFIG_FILE);
|
const config = readConfig(env.MESH_CLOUDFLARE_CONFIG_FILE);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-cloudflare-dns",
|
"name": "@novox/module-cloudflare-dns",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "cloudflare-dns — a public-dns provider (ADR 0049): registers public names at Cloudflare.
|
"description": "cloudflare-dns — a public-dns provider (ADR 0044): registers public names at Cloudflare.
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// cloudflare-dns's provisioner — the adapter making it a provider of the mesh `public-dns` interface
|
// cloudflare-dns's provisioner — the adapter making it a provider of the mesh `public-dns` interface
|
||||||
// (novox/hq ADR 0049). The reconcile loop and the contributions file are the sdk harness's; this
|
// (novox/hq ADR 0044). The reconcile loop and the contributions file are the sdk harness's; this
|
||||||
// writes only the per-registrar half: register a consumer's public name at Cloudflare, pointing it
|
// writes only the per-registrar half: register a consumer's public name at Cloudflare, pointing it
|
||||||
// at the mesh's ingress, and remove it when the consumer is withdrawn (ADR 0053).
|
// at the mesh's ingress, and remove it when the consumer is withdrawn (ADR 0048).
|
||||||
//
|
//
|
||||||
// The `public-dns` interface hands a consumer { fqdn, target, ttl } — a name that resolves publicly
|
// The `public-dns` interface hands a consumer { fqdn, target, ttl } — a name that resolves publicly
|
||||||
// and what it resolves to. Like umami's analytics it is a *data* provision, not a credential one:
|
// and what it resolves to. Like umami's analytics it is a *data* provision, not a credential one:
|
||||||
// nothing the mesh mints is set here (a DNS record is public, and the only secret is this module's
|
// nothing the mesh mints is set here (a DNS record is public, and the only secret is this module's
|
||||||
// own Cloudflare token, which never leaves). So the password the harness carries is unused; the name
|
// own Cloudflare token, which never leaves). So the password the harness carries is unused; the name
|
||||||
// is derived from the login the mesh gave the consumer, which the consumer can derive too. Delivering
|
// is derived from the login the mesh gave the consumer, which the consumer can derive too. Delivering
|
||||||
// the record back to the consumer is the data-provision return path ADR 0053 leaves out of scope.
|
// the record back to the consumer is the data-provision return path ADR 0048 leaves out of scope.
|
||||||
|
|
||||||
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
|
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
|
||||||
import { emit } from "@novox/mesh-sdk/events";
|
import { emit } from "@novox/mesh-sdk/events";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// dnsmasq's own code, living in the module (novox/hq ADR 0044). dnsmasq here is a pure resolver: it
|
// dnsmasq's own code, living in the module (novox/hq ADR 0039). dnsmasq here is a pure resolver: it
|
||||||
// answers the mesh's generated wildcard names (<service>.<node>.<suffix>) and forwards nothing. So
|
// answers the mesh's generated wildcard names (<service>.<node>.<suffix>) and forwards nothing. So
|
||||||
// its code reads what it was told to answer, and can resolve through itself to prove that it does.
|
// its code reads what it was told to answer, and can resolve through itself to prove that it does.
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// announces, from the resolver's own vantage, that a name became (or stopped being) resolvable on
|
// announces, from the resolver's own vantage, that a name became (or stopped being) resolvable on
|
||||||
// this node: DNS having actually propagated here, distinct from the mesh's own node.* events.
|
// this node: DNS having actually propagated here, distinct from the mesh's own node.* events.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.dnsmasq.name.added — this node's resolver now answers a machine's name
|
// module.dnsmasq.name.added — this node's resolver now answers a machine's name
|
||||||
// module.dnsmasq.name.removed — it no longer does
|
// module.dnsmasq.name.removed — it no longer does
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// dnsmasq's tools — a resolver's two useful questions: what does it answer, and does it answer a
|
// dnsmasq's tools — a resolver's two useful questions: what does it answer, and does it answer a
|
||||||
// given name. Moved into the module (novox/hq ADR 0044); the mesh serves them through the sdk.
|
// given name. Moved into the module (novox/hq ADR 0039); the mesh serves them through the sdk.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
import { DnsmasqClient } from "../client.js";
|
import { DnsmasqClient } from "../client.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// fail2ban's own code, in the module (novox/hq ADR 0044). The jails and the daemon are declared
|
// fail2ban's own code, in the module (novox/hq ADR 0039). The jails and the daemon are declared
|
||||||
// resources — the mesh writes /etc/fail2ban/jail.d/* and keeps fail2ban.service running (see
|
// resources — the mesh writes /etc/fail2ban/jail.d/* and keeps fail2ban.service running (see
|
||||||
// module.json). This code exists only to read and steer the *live* state the daemon owns at
|
// module.json). This code exists only to read and steer the *live* state the daemon owns at
|
||||||
// runtime: which IPs are banned right now, and the manual ban/unban an operator reaches for. That
|
// runtime: which IPs are banned right now, and the manual ban/unban an operator reaches for. That
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// The firewall's own code, in the module (novox/hq ADR 0044). The mesh computes this node's whole
|
// The firewall's own code, in the module (novox/hq ADR 0039). The mesh computes this node's whole
|
||||||
// rule set from every module's `listens` and writes it to /etc/nftables.conf (novox/hq ADR 0050);
|
// rule set from every module's `listens` and writes it to /etc/nftables.conf (novox/hq ADR 0045);
|
||||||
// the module loads it (the nftables service, reloaded whenever the rules change). This code exists
|
// the module loads it (the nftables service, reloaded whenever the rules change). This code exists
|
||||||
// only to read back what is actually enforced — the enforcement itself is declarative.
|
// only to read back what is actually enforced — the enforcement itself is declarative.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-firewall",
|
"name": "@novox/module-firewall",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "firewall — applies the mesh-computed packet filter (ADR 0050). Its diagnostic tool lives here.
|
"description": "firewall — applies the mesh-computed packet filter (ADR 0045). Its diagnostic tool lives here.
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Gitea API client — gitea's own code, living in the module (novox/hq ADR 0044). Moved out of
|
// The Gitea API client — gitea's own code, living in the module (novox/hq ADR 0039). Moved out of
|
||||||
// the shared hal sdk, where a change to Gitea's API rebuilt everything; here it rebuilds only
|
// the shared hal sdk, where a change to Gitea's API rebuilt everything; here it rebuilds only
|
||||||
// gitea. Both this module's tools and its events entrypoint import it, and nothing outside gitea
|
// gitea. Both this module's tools and its events entrypoint import it, and nothing outside gitea
|
||||||
// does.
|
// does.
|
||||||
@@ -44,7 +44,7 @@ export interface GiteaLabel {
|
|||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// gitea's events. The tool runtime imports this once the broker is bound. It watches the forge and
|
// gitea's events. The tool runtime imports this once the broker is bound. It watches the forge and
|
||||||
// emits what appeared.
|
// emits what appeared.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.gitea.repo.created — a repository appeared, however it was made (push, web UI, or tool)
|
// module.gitea.repo.created — a repository appeared, however it was made (push, web UI, or tool)
|
||||||
//
|
//
|
||||||
// issue.opened and pull.merged are emitted from the tools (tools/index.ts), at the instant the mesh
|
// issue.opened and pull.merged are emitted from the tools (tools/index.ts), at the instant the mesh
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-gitea",
|
"name": "@novox/module-gitea",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "gitea — git hosting. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "gitea — git hosting. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// gitea's tools — moved here from the shared sdk (novox/hq ADR 0044), importing gitea's own client.
|
// gitea's tools — moved here from the shared sdk (novox/hq ADR 0039), importing gitea's own client.
|
||||||
// They return structured data; the mesh serves them through the sdk's tool harness.
|
// They return structured data; the mesh serves them through the sdk's tool harness.
|
||||||
//
|
//
|
||||||
// Two tools emit an event at the natural point of the action they take (novox/hq ADR 0046/0047):
|
// Two tools emit an event at the natural point of the action they take (novox/hq ADR 0041/0042):
|
||||||
// create-issue emits issue.opened, merge-pull-request emits pull.merged — the mesh's own hand on
|
// create-issue emits issue.opened, merge-pull-request emits pull.merged — the mesh's own hand on
|
||||||
// the forge, announced the instant it moves. repo.created is deliberately NOT emitted here: repos
|
// the forge, announced the instant it moves. repo.created is deliberately NOT emitted here: repos
|
||||||
// are far more often born from a `git push` or the web UI than from this tool, so the events
|
// are far more often born from a `git push` or the web UI than from this tool, so the events
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Grafana's API client — grafana's own code, living in the module (novox/hq ADR 0044). Ported from
|
// Grafana's API client — grafana's own code, living in the module (novox/hq ADR 0039). Ported from
|
||||||
// the shared hal sdk, where a change here rebuilt everything; here it rebuilds only grafana. Both
|
// the shared hal sdk, where a change here rebuilt everything; here it rebuilds only grafana. Both
|
||||||
// this module's tools and its events entrypoint import it, and nothing outside grafana does.
|
// this module's tools and its events entrypoint import it, and nothing outside grafana does.
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export interface GrafanaAlert {
|
|||||||
activeAt?: string;
|
activeAt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// grafana's events. The tool runtime imports this once the broker is bound. It watches unified
|
// grafana's events. The tool runtime imports this once the broker is bound. It watches unified
|
||||||
// alerting and announces when an alert instance starts firing.
|
// alerting and announces when an alert instance starts firing.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.grafana.alert.firing — an alert instance entered the Alerting state
|
// module.grafana.alert.firing — an alert instance entered the Alerting state
|
||||||
//
|
//
|
||||||
// A Grafana with no alerting configured simply never has a firing alert, so this observes nothing
|
// A Grafana with no alerting configured simply never has a firing alert, so this observes nothing
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-grafana",
|
"name": "@novox/module-grafana",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "grafana — monitoring dashboards. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "grafana — monitoring dashboards. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// grafana's tools — moved here from the shared hal sdk (novox/hq ADR 0044), importing grafana's own
|
// grafana's tools — moved here from the shared hal sdk (novox/hq ADR 0039), importing grafana's own
|
||||||
// client. They return structured data; the mesh serves them through the sdk's tool harness.
|
// client. They return structured data; the mesh serves them through the sdk's tool harness.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// The Home Assistant API client — home-assistant's own code, living in the module (novox/hq
|
// The Home Assistant API client — home-assistant's own code, living in the module (novox/hq
|
||||||
// ADR 0044). Both this module's tools and its events entrypoint import it, and nothing outside
|
// ADR 0039). Both this module's tools and its events entrypoint import it, and nothing outside
|
||||||
// home-assistant does. Talks to the HA REST API (/api) with a long-lived access token.
|
// home-assistant does. Talks to the HA REST API (/api) with a long-lived access token.
|
||||||
|
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
@@ -20,7 +20,7 @@ export interface HAConfig {
|
|||||||
state?: string;
|
state?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// entities whose state changing is a real signal — a door opening, a lock turning, a switch
|
// entities whose state changing is a real signal — a door opening, a lock turning, a switch
|
||||||
// flipping — and emits when one does.
|
// flipping — and emits when one does.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.home-assistant.state.changed — a watched entity's state value changed
|
// module.home-assistant.state.changed — a watched entity's state value changed
|
||||||
//
|
//
|
||||||
// Bounded on purpose. Home Assistant has hundreds of entities and many (temperature, humidity,
|
// Bounded on purpose. Home Assistant has hundreds of entities and many (temperature, humidity,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-home-assistant",
|
"name": "@novox/module-home-assistant",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "home-assistant — home automation platform. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "home-assistant — home automation platform. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// home-assistant's tools — its own code (novox/hq ADR 0044), importing its own client. They return
|
// home-assistant's tools — its own code (novox/hq ADR 0039), importing its own client. They return
|
||||||
// structured data; the mesh serves them through the sdk's tool harness.
|
// structured data; the mesh serves them through the sdk's tool harness.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Icecast's API client — icecast's own code, living in the module (novox/hq ADR 0044). Icecast is an
|
// Icecast's API client — icecast's own code, living in the module (novox/hq ADR 0039). Icecast is an
|
||||||
// audio streaming server: sources push mountpoints in, listeners pull them out. Its `/status-json.xsl`
|
// audio streaming server: sources push mountpoints in, listeners pull them out. Its `/status-json.xsl`
|
||||||
// endpoint reports the live mountpoints and their listener counts — the one thing worth watching, and
|
// endpoint reports the live mountpoints and their listener counts — the one thing worth watching, and
|
||||||
// the basis for both the status tool and the stream started/stopped events.
|
// the basis for both the status tool and the stream started/stopped events.
|
||||||
@@ -35,7 +35,7 @@ interface RawSource {
|
|||||||
server_type?: string;
|
server_type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// icecast's events. The tool runtime imports this once the broker is bound. It watches the streaming
|
// icecast's events. The tool runtime imports this once the broker is bound. It watches the streaming
|
||||||
// server and announces when a mountpoint goes live or drops.
|
// server and announces when a mountpoint goes live or drops.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.icecast.stream.started / .stopped — a mountpoint appeared or disappeared
|
// module.icecast.stream.started / .stopped — a mountpoint appeared or disappeared
|
||||||
//
|
//
|
||||||
// A mountpoint exists only while a source is connected, so the set of mounts diffed over time is
|
// A mountpoint exists only while a source is connected, so the set of mounts diffed over time is
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-icecast",
|
"name": "@novox/module-icecast",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "icecast — audio streaming server. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "icecast — audio streaming server. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// icecast's tools (novox/hq ADR 0044), importing icecast's own client.
|
// icecast's tools (novox/hq ADR 0039), importing icecast's own client.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
import { IcecastClient } from "../client.js";
|
import { IcecastClient } from "../client.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The InfluxDB API client — influxdb's own code, living in the module (novox/hq ADR 0044). Only
|
// The InfluxDB API client — influxdb's own code, living in the module (novox/hq ADR 0039). Only
|
||||||
// this module's tools import it. Talks to the InfluxDB 2.x HTTP API (/api/v2) with a token.
|
// this module's tools import it. Talks to the InfluxDB 2.x HTTP API (/api/v2) with a token.
|
||||||
|
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
@@ -17,7 +17,7 @@ export interface InfluxBucket {
|
|||||||
retentionSeconds?: number;
|
retentionSeconds?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-influxdb",
|
"name": "@novox/module-influxdb",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "influxdb — time-series database. Its API client and tools live here (novox/hq ADR 0044).",
|
"description": "influxdb — time-series database. Its API client and tools live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// influxdb's tools — its own code (novox/hq ADR 0044), importing its own client. They return
|
// influxdb's tools — its own code (novox/hq ADR 0039), importing its own client. They return
|
||||||
// structured data; the mesh serves them through the sdk's tool harness. Read-only: health, bucket
|
// structured data; the mesh serves them through the sdk's tool harness. Read-only: health, bucket
|
||||||
// listing, and Flux queries — no write path is exposed.
|
// listing, and Flux queries — no write path is exposed.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Jackett API client — jackett's own code, living in the module (novox/hq ADR 0044). Jackett is
|
// The Jackett API client — jackett's own code, living in the module (novox/hq ADR 0039). Jackett is
|
||||||
// an indexer proxy: it normalises many torrent trackers behind one Torznab surface. This client
|
// an indexer proxy: it normalises many torrent trackers behind one Torznab surface. This client
|
||||||
// talks its /api/v2.0 REST API, and only jackett's tools import it.
|
// talks its /api/v2.0 REST API, and only jackett's tools import it.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export interface JackettResult {
|
|||||||
link?: string;
|
link?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-jackett",
|
"name": "@novox/module-jackett",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "jackett — indexer proxy. Its API client and tools live here (novox/hq ADR 0044).",
|
"description": "jackett — indexer proxy. Its API client and tools live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// jackett's tools — its own code (novox/hq ADR 0044), importing jackett's client. Jackett has
|
// jackett's tools — its own code (novox/hq ADR 0039), importing jackett's client. Jackett has
|
||||||
// nothing worth watching (an indexer proxy answers queries; it has no timeline of its own), so it
|
// nothing worth watching (an indexer proxy answers queries; it has no timeline of its own), so it
|
||||||
// is a tools-only module: no events entrypoint, no broker. What is useful is asking it things.
|
// is a tools-only module: no events entrypoint, no broker. What is useful is asking it things.
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// The Keycloak admin API client — keycloak's own code, living in the module (novox/hq ADR 0044).
|
// The Keycloak admin API client — keycloak's own code, living in the module (novox/hq ADR 0039).
|
||||||
// Moved out of the shared hal sdk, where a change to Keycloak's admin API rebuilt everything; here
|
// Moved out of the shared hal sdk, where a change to Keycloak's admin API rebuilt everything; here
|
||||||
// it rebuilds only keycloak. Both this module's tools and its events entrypoint import it, and
|
// it rebuilds only keycloak. Both this module's tools and its events entrypoint import it, and
|
||||||
// nothing outside keycloak does.
|
// nothing outside keycloak does.
|
||||||
|
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// keycloak's events. Keycloak's worth to the mesh is in what it changes — an identity created, a
|
// keycloak's events. Keycloak's worth to the mesh is in what it changes — an identity created, a
|
||||||
// client registered, a password reset — so its events are emitted from the admin actions themselves
|
// client registered, a password reset — so its events are emitted from the admin actions themselves
|
||||||
// (novox/hq ADR 0046/0047), not scraped back by polling. This module is the single vocabulary for
|
// (novox/hq ADR 0041/0042), not scraped back by polling. This module is the single vocabulary for
|
||||||
// them: every keycloak event goes through one of the helpers here, and the tools call them at the
|
// them: every keycloak event goes through one of the helpers here, and the tools call them at the
|
||||||
// point the change succeeds.
|
// point the change succeeds.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-keycloak",
|
"name": "@novox/module-keycloak",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "keycloak — identity and access. Its admin API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "keycloak — identity and access. Its admin API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// keycloak's tools — moved here from the shared sdk (novox/hq ADR 0044), importing keycloak's own
|
// keycloak's tools — moved here from the shared sdk (novox/hq ADR 0039), importing keycloak's own
|
||||||
// client. They return structured data (not the hal MCP `{content:[...]}` shape); the mesh serves
|
// client. They return structured data (not the hal MCP `{content:[...]}` shape); the mesh serves
|
||||||
// them through the sdk's tool harness. Write actions announce themselves through the module's event
|
// them through the sdk's tool harness. Write actions announce themselves through the module's event
|
||||||
// surface at the point they succeed.
|
// surface at the point they succeed.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Lidarr API client — lidarr's own code, living in the module (novox/hq ADR 0044). Ported from
|
// The Lidarr API client — lidarr's own code, living in the module (novox/hq ADR 0039). Ported from
|
||||||
// the shared hal `arr` client, but self-contained: in nox each Servarr app owns its own copy, so a
|
// the shared hal `arr` client, but self-contained: in nox each Servarr app owns its own copy, so a
|
||||||
// change to Lidarr's API rebuilds only lidarr and nothing else. Both this module's tools and its
|
// change to Lidarr's API rebuilds only lidarr and nothing else. Both this module's tools and its
|
||||||
// events entrypoint import it, and nothing outside lidarr does.
|
// events entrypoint import it, and nothing outside lidarr does.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// lidarr's events. The tool runtime imports this once the broker is bound. It watches the download
|
// lidarr's events. The tool runtime imports this once the broker is bound. It watches the download
|
||||||
// queue and turns its comings and goings into mesh events.
|
// queue and turns its comings and goings into mesh events.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.lidarr.album.grabbed — a release entered the queue (Lidarr grabbed it)
|
// module.lidarr.album.grabbed — a release entered the queue (Lidarr grabbed it)
|
||||||
// module.lidarr.download.completed — a release left the queue, imported. The download.completed
|
// module.lidarr.download.completed — a release left the queue, imported. The download.completed
|
||||||
// routing key matches what a media consumer subscribes to
|
// routing key matches what a media consumer subscribes to
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-lidarr",
|
"name": "@novox/module-lidarr",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "lidarr — music management. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "lidarr — music management. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// lidarr's tools — ported from the shared hal sdk (novox/hq ADR 0044), importing lidarr's own
|
// lidarr's tools — ported from the shared hal sdk (novox/hq ADR 0039), importing lidarr's own
|
||||||
// client. They return structured data (not pre-formatted text as hal did); the mesh serves them
|
// client. They return structured data (not pre-formatted text as hal did); the mesh serves them
|
||||||
// through the sdk's tool harness.
|
// through the sdk's tool harness.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Mailu API client — mailu's own code, living in the module (novox/hq ADR 0044). Moved out of
|
// The Mailu API client — mailu's own code, living in the module (novox/hq ADR 0039). Moved out of
|
||||||
// the shared hal sdk, where a change to Mailu's surface rebuilt everything; here it rebuilds only
|
// the shared hal sdk, where a change to Mailu's surface rebuilt everything; here it rebuilds only
|
||||||
// mailu. Both this module's tools and its events entrypoint import it, and nothing outside mailu does.
|
// mailu. Both this module's tools and its events entrypoint import it, and nothing outside mailu does.
|
||||||
//
|
//
|
||||||
@@ -45,7 +45,7 @@ export interface MailMessage {
|
|||||||
// The fields we ask doveadm for, once — kept together so read and search stay identical in shape.
|
// The fields we ask doveadm for, once — kept together so read and search stay identical in shape.
|
||||||
const FETCH_FIELDS = "date.received hdr.subject hdr.from body.snippet";
|
const FETCH_FIELDS = "date.received hdr.subject hdr.from body.snippet";
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// server's accounts and announces what changed, so the rest of the mesh can react to a mailbox
|
// server's accounts and announces what changed, so the rest of the mesh can react to a mailbox
|
||||||
// appearing or an alias being pointed somewhere new.
|
// appearing or an alias being pointed somewhere new.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.mailu.user.created / .deleted — a mailbox appeared or was removed
|
// module.mailu.user.created / .deleted — a mailbox appeared or was removed
|
||||||
// module.mailu.alias.created / .deleted — an alias was added or removed
|
// module.mailu.alias.created / .deleted — an alias was added or removed
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-mailu",
|
"name": "@novox/module-mailu",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "mailu — mail server. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "mailu — mail server. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mailu's tools — moved here from the shared sdk (novox/hq ADR 0044), importing mailu's own client.
|
// mailu's tools — moved here from the shared sdk (novox/hq ADR 0039), importing mailu's own client.
|
||||||
// They return structured data; the mesh serves them through the sdk's tool harness. Deletions are
|
// They return structured data; the mesh serves them through the sdk's tool harness. Deletions are
|
||||||
// guarded by an explicit `confirm`, since removing a mailbox destroys its mail and cannot be undone.
|
// guarded by an explicit `confirm`, since removing a mailbox destroys its mail and cannot be undone.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The MinIO admin client — minio's own code, living in the module (novox/hq ADR 0044). Ported out
|
// The MinIO admin client — minio's own code, living in the module (novox/hq ADR 0039). Ported out
|
||||||
// of the shared hal sdk, where a change to MinIO's surface rebuilt everything; here it rebuilds only
|
// of the shared hal sdk, where a change to MinIO's surface rebuilt everything; here it rebuilds only
|
||||||
// minio. This module's tools, its provisioner and its events entrypoint import it; nothing outside
|
// minio. This module's tools, its provisioner and its events entrypoint import it; nothing outside
|
||||||
// minio does.
|
// minio does.
|
||||||
@@ -207,7 +207,7 @@ export class MinioClient {
|
|||||||
/**
|
/**
|
||||||
* Create a service account scoped to one bucket, under a given access key and secret key, and
|
* Create a service account scoped to one bucket, under a given access key and secret key, and
|
||||||
* return the pair. The secret key is the mesh's — the mesh mints one password per consumer and
|
* return the pair. The secret key is the mesh's — the mesh mints one password per consumer and
|
||||||
* hands a copy to both ends (novox/hq ADR 0053), so minio sets that as the secret rather than
|
* hands a copy to both ends (novox/hq ADR 0048), so minio sets that as the secret rather than
|
||||||
* generating one the consumer could never learn. The MinIO admin REST API encrypts this request
|
* generating one the consumer could never learn. The MinIO admin REST API encrypts this request
|
||||||
* with a key derived (Argon2) from the root secret, which node built-ins cannot reproduce — so, as
|
* with a key derived (Argon2) from the root secret, which node built-ins cannot reproduce — so, as
|
||||||
* hal did, the module drives the `mc` CLI, which the runtime image bundles.
|
* hal did, the module drives the `mc` CLI, which the runtime image bundles.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-minio",
|
"name": "@novox/module-minio",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "minio — S3-compatible object store. Its admin client, tools, provisioner and events live here (novox/hq ADR 0044).",
|
"description": "minio — S3-compatible object store. Its admin client, tools, provisioner and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// minio's provisioner — the adapter that makes minio a provider of the mesh `s3-bucket` interface
|
// minio's provisioner — the adapter that makes minio a provider of the mesh `s3-bucket` interface
|
||||||
// (the name in module.json's `provides`). The reconcile loop, the contributions file, and reading
|
// (the name in module.json's `provides`). The reconcile loop, the contributions file, and reading
|
||||||
// the mesh's minted secret are the sdk harness's; this writes only the per-service half: how minio
|
// the mesh's minted secret are the sdk harness's; this writes only the per-service half: how minio
|
||||||
// creates and removes a consumer's bucket and its scoped access key (novox/hq ADR 0044/0045/0053).
|
// creates and removes a consumer's bucket and its scoped access key (novox/hq ADR 0039/0040/0048).
|
||||||
//
|
//
|
||||||
// The `s3-bucket` interface: a consumer connects to an S3 endpoint with an access key confined to
|
// The `s3-bucket` interface: a consumer connects to an S3 endpoint with an access key confined to
|
||||||
// its own bucket. It depends on `s3-bucket`, not on minio, so any S3-compatible provider could serve
|
// its own bucket. It depends on `s3-bucket`, not on minio, so any S3-compatible provider could serve
|
||||||
// it.
|
// it.
|
||||||
//
|
//
|
||||||
// **The access key and its secret are the mesh's, not the provisioner's (ADR 0053).** The mesh
|
// **The access key and its secret are the mesh's, not the provisioner's (ADR 0048).** The mesh
|
||||||
// derives the login (the access-key id) and hands it to both ends, and mints the secret key. minio
|
// derives the login (the access-key id) and hands it to both ends, and mints the secret key. minio
|
||||||
// creates the service account under exactly that access key with exactly that secret — a credential
|
// creates the service account under exactly that access key with exactly that secret — a credential
|
||||||
// the provisioner invented is one the consumer could never present. The bucket is derived from the
|
// the provisioner invented is one the consumer could never present. The bucket is derived from the
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// minio's tools — ported here from the shared sdk (novox/hq ADR 0044), importing minio's own client.
|
// minio's tools — ported here from the shared sdk (novox/hq ADR 0039), importing minio's own client.
|
||||||
// They return structured data; the mesh serves them through the sdk's tool harness. These are the
|
// They return structured data; the mesh serves them through the sdk's tool harness. These are the
|
||||||
// read/inspect operations useful to an operator; creating storage for a consumer is the provisioner's
|
// read/inspect operations useful to an operator; creating storage for a consumer is the provisioner's
|
||||||
// job, not a tool's.
|
// job, not a tool's.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mongodb's admin client — mongodb's own code, living in the module (novox/hq ADR 0044). Both this
|
// mongodb's admin client — mongodb's own code, living in the module (novox/hq ADR 0039). Both this
|
||||||
// module's tools and its provisioner import it, and nothing outside mongodb does.
|
// module's tools and its provisioner import it, and nothing outside mongodb does.
|
||||||
//
|
//
|
||||||
// Commands run through `mongosh`, not a wire-protocol driver: the module may take NO npm dependency
|
// Commands run through `mongosh`, not a wire-protocol driver: the module may take NO npm dependency
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// mongodb's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
// mongodb's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
||||||
// ./provisioner separately). The database lifecycle events are EMITTED from the provisioner, where
|
// ./provisioner separately). The database lifecycle events are EMITTED from the provisioner, where
|
||||||
// the lifecycle actually happens (novox/hq ADR 0046/0047):
|
// the lifecycle actually happens (novox/hq ADR 0041/0042):
|
||||||
// module.mongodb.database.provisioned — a consumer's database + owning user was created
|
// module.mongodb.database.provisioned — a consumer's database + owning user was created
|
||||||
// module.mongodb.database.deprovisioned — that database was removed
|
// module.mongodb.database.deprovisioned — that database was removed
|
||||||
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-mongodb",
|
"name": "@novox/module-mongodb",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "mongodb — provides the mesh mongodb-database interface. Its client, provisioner, tools and events live here (novox/hq ADR 0044).",
|
"description": "mongodb — provides the mesh mongodb-database interface. Its client, provisioner, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// mongodb's provisioner — the adapter that makes mongodb a provider of the mesh `mongodb-database`
|
// mongodb's provisioner — the adapter that makes mongodb a provider of the mesh `mongodb-database`
|
||||||
// interface. The reconcile loop, the contributions file, and reading the mesh's minted password are
|
// 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 mongodb creates and removes a
|
// the sdk harness's; this writes only the per-service half: how mongodb creates and removes a
|
||||||
// consumer's database + owning user (novox/hq ADR 0044/0045/0053).
|
// consumer's database + owning user (novox/hq ADR 0039/0040/0048).
|
||||||
//
|
//
|
||||||
// The `mongodb-database` interface: a consumer connects to a database it alone owns, as `as` with the
|
// The `mongodb-database` interface: a consumer connects to a database it alone owns, as `as` with the
|
||||||
// password the mesh minted, authenticating against that same database.
|
// password the mesh minted, authenticating against that same database.
|
||||||
//
|
//
|
||||||
// **The user name and password are the mesh's, not the provisioner's (ADR 0053).** The mesh derives
|
// **The user name and password are the mesh's, not the provisioner's (ADR 0048).** The mesh derives
|
||||||
// the login and hands it to both ends, and mints the password. mongodb creates a user and a
|
// the login and hands it to both ends, and mints the password. mongodb creates a user and a
|
||||||
// same-named database under exactly that login — a name the consumer cannot learn is a database it
|
// same-named database under exactly that login — a name the consumer cannot learn is a database it
|
||||||
// cannot reach.
|
// cannot reach.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mongodb's tools — mongodb's own code (novox/hq ADR 0044), importing mongodb's own client. They
|
// mongodb's tools — mongodb's own code (novox/hq ADR 0039), importing mongodb's own client. They
|
||||||
// return structured data; the mesh serves them through the sdk's tool harness. Both call through
|
// return structured data; the mesh serves them through the sdk's tool harness. Both call through
|
||||||
// MongoClient.evalJs(), the module's one execution boundary (see client.ts).
|
// MongoClient.evalJs(), the module's one execution boundary (see client.ts).
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mosquitto's admin client — mosquitto's own code, living in the module (novox/hq ADR 0044). Both
|
// mosquitto's admin client — mosquitto's own code, living in the module (novox/hq ADR 0039). Both
|
||||||
// this module's tools and its provisioner import it, and nothing outside mosquitto does.
|
// this module's tools and its provisioner import it, and nothing outside mosquitto does.
|
||||||
//
|
//
|
||||||
// Client, role and ACL administration is driven through `mosquitto_ctrl dynsec`, not a hand-rolled
|
// Client, role and ACL administration is driven through `mosquitto_ctrl dynsec`, not a hand-rolled
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// mosquitto's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
// mosquitto's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
||||||
// ./provisioner separately). The topic lifecycle events are EMITTED from the provisioner, where the
|
// ./provisioner separately). The topic lifecycle events are EMITTED from the provisioner, where the
|
||||||
// lifecycle actually happens (novox/hq ADR 0046/0047):
|
// lifecycle actually happens (novox/hq ADR 0041/0042):
|
||||||
// module.mosquitto.topic.provisioned — a consumer's client + scoped role was created
|
// module.mosquitto.topic.provisioned — a consumer's client + scoped role was created
|
||||||
// module.mosquitto.topic.deprovisioned — that client was removed
|
// module.mosquitto.topic.deprovisioned — that client was removed
|
||||||
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-mosquitto",
|
"name": "@novox/module-mosquitto",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "mosquitto — provides the mesh mqtt-topic interface. Its admin client, provisioner, tools and events live here (novox/hq ADR 0044).",
|
"description": "mosquitto — provides the mesh mqtt-topic interface. Its admin client, provisioner, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// mosquitto's provisioner — the adapter that makes mosquitto a provider of the mesh `mqtt-topic`
|
// mosquitto's provisioner — the adapter that makes mosquitto a provider of the mesh `mqtt-topic`
|
||||||
// interface. The reconcile loop, the contributions file, and reading the mesh's minted password are
|
// 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 mosquitto creates and removes a
|
// the sdk harness's; this writes only the per-service half: how mosquitto creates and removes a
|
||||||
// per-consumer MQTT client (novox/hq ADR 0044/0045/0053).
|
// per-consumer MQTT client (novox/hq ADR 0039/0040/0048).
|
||||||
//
|
//
|
||||||
// The `mqtt-topic` interface: a consumer connects as `as` with the password the mesh minted, and
|
// The `mqtt-topic` interface: a consumer connects as `as` with the password the mesh minted, and
|
||||||
// publishes and subscribes under `<as>/#`, isolated from every other consumer by a Dynamic Security
|
// publishes and subscribes under `<as>/#`, isolated from every other consumer by a Dynamic Security
|
||||||
// role scoped to exactly that subtree.
|
// role scoped to exactly that subtree.
|
||||||
//
|
//
|
||||||
// **The login and password are the mesh's, not the provisioner's (ADR 0053).** The mesh derives the
|
// **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.
|
// login and hands it to both ends so they agree, and mints the password and delivers a copy to each.
|
||||||
// mosquitto creates exactly that client with exactly that password — a name or password the
|
// mosquitto creates exactly that client with exactly that password — a name or password the
|
||||||
// provisioner invented is one the consumer could never present.
|
// provisioner invented is one the consumer could never present.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mosquitto's tools — mosquitto's own code (novox/hq ADR 0044), importing mosquitto's own admin
|
// mosquitto's tools — mosquitto's own code (novox/hq ADR 0039), importing mosquitto's own admin
|
||||||
// client. They return structured data; the mesh serves them through the sdk's tool harness.
|
// client. They return structured data; the mesh serves them through the sdk's tool harness.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mssql's admin client — mssql's own code, living in the module (novox/hq ADR 0044). Both this
|
// mssql's admin client — mssql's own code, living in the module (novox/hq ADR 0039). Both this
|
||||||
// module's tools and its provisioner import it, and nothing outside mssql does.
|
// module's tools and its provisioner import it, and nothing outside mssql does.
|
||||||
//
|
//
|
||||||
// SQL is executed through `sqlcmd`, not a wire-protocol driver: the module may take NO npm
|
// SQL is executed through `sqlcmd`, not a wire-protocol driver: the module may take NO npm
|
||||||
@@ -86,7 +86,7 @@ export class MssqlClient {
|
|||||||
// `-h -1` drops the column-header rule; `-y 0`/`-Y 0` lift the display-width cap so a long
|
// `-h -1` drops the column-header rule; `-y 0`/`-Y 0` lift the display-width cap so a long
|
||||||
// JSON document is not truncated; `-W` trims trailing whitespace so the JSON chunks rejoin
|
// JSON document is not truncated; `-W` trims trailing whitespace so the JSON chunks rejoin
|
||||||
// cleanly. sqlcmd from the mssql-tools ships in the runtime container, the way `psql` ships
|
// cleanly. sqlcmd from the mssql-tools ships in the runtime container, the way `psql` ships
|
||||||
// with postgres's — the module owns its own code (ADR 0044) and shells out to it.
|
// with postgres's — the module owns its own code (ADR 0039) and shells out to it.
|
||||||
const { stdout } = await run(
|
const { stdout } = await run(
|
||||||
"sqlcmd",
|
"sqlcmd",
|
||||||
[
|
[
|
||||||
@@ -109,7 +109,7 @@ export class MssqlClient {
|
|||||||
/**
|
/**
|
||||||
* Create a login and a database it owns (mapped as a db_owner user), idempotently. The login,
|
* Create a login and a database it owns (mapped as a db_owner user), idempotently. The login,
|
||||||
* the database and the user all carry the consumer's minted name, so the consumer owns exactly
|
* the database and the user all carry the consumer's minted name, so the consumer owns exactly
|
||||||
* its own database — a name it cannot learn is a database it cannot reach (ADR 0053).
|
* its own database — a name it cannot learn is a database it cannot reach (ADR 0048).
|
||||||
*/
|
*/
|
||||||
async createDatabaseAndLogin(database: string, login: string, password: string): Promise<void> {
|
async createDatabaseAndLogin(database: string, login: string, password: string): Promise<void> {
|
||||||
const logins = await this.query(
|
const logins = await this.query(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// mssql's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
// mssql's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
||||||
// ./provisioner separately). The database lifecycle events are EMITTED from the provisioner, where
|
// ./provisioner separately). The database lifecycle events are EMITTED from the provisioner, where
|
||||||
// the lifecycle actually happens (novox/hq ADR 0046/0047):
|
// the lifecycle actually happens (novox/hq ADR 0041/0042):
|
||||||
// module.mssql.database.provisioned — a consumer's database + login/user was created
|
// module.mssql.database.provisioned — a consumer's database + login/user was created
|
||||||
// module.mssql.database.deprovisioned — that database was removed
|
// module.mssql.database.deprovisioned — that database was removed
|
||||||
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-mssql",
|
"name": "@novox/module-mssql",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "mssql — provides the mesh mssql-database interface. Its client, provisioner, tools and events live here (novox/hq ADR 0044).",
|
"description": "mssql — provides the mesh mssql-database interface. Its client, provisioner, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// mssql's provisioner — the adapter that makes mssql a provider of the mesh `mssql-database`
|
// mssql's provisioner — the adapter that makes mssql a provider of the mesh `mssql-database`
|
||||||
// interface. The reconcile loop, the contributions file, and reading the mesh's minted password
|
// 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 mssql creates and removes a
|
// are the sdk harness's; this writes only the per-service half: how mssql creates and removes a
|
||||||
// consumer's database + login/user with the mesh-minted credential (novox/hq ADR 0044/0045/0053).
|
// consumer's database + login/user with the mesh-minted credential (novox/hq ADR 0039/0040/0048).
|
||||||
//
|
//
|
||||||
// The `mssql-database` interface: a consumer connects to a database it alone owns, as `as` with
|
// The `mssql-database` interface: a consumer connects to a database it alone owns, as `as` with
|
||||||
// the password the mesh minted.
|
// the password the mesh minted.
|
||||||
//
|
//
|
||||||
// **The login name and password are the mesh's, not the provisioner's (ADR 0053).** The mesh
|
// **The login name and password are the mesh's, not the provisioner's (ADR 0048).** The mesh
|
||||||
// derives the login and hands it to both ends, and mints the password. mssql creates a login, a
|
// derives the login and hands it to both ends, and mints the password. mssql creates a login, a
|
||||||
// same-named database, and a db_owner user under exactly that login — a name the consumer cannot
|
// same-named database, and a db_owner user under exactly that login — a name the consumer cannot
|
||||||
// learn is a database it cannot reach.
|
// learn is a database it cannot reach.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// mssql's tools — mssql's own code (novox/hq ADR 0044), importing mssql's own client. They return
|
// mssql's tools — mssql's own code (novox/hq ADR 0039), importing mssql's own client. They return
|
||||||
// structured data; the mesh serves them through the sdk's tool harness. Both call through
|
// structured data; the mesh serves them through the sdk's tool harness. Both call through
|
||||||
// MssqlClient, the module's one pending execution boundary (see client.ts): the tool shapes are
|
// MssqlClient, the module's one pending execution boundary (see client.ts): the tool shapes are
|
||||||
// fixed and correct, and surface the work honestly through that boundary.
|
// fixed and correct, and surface the work honestly through that boundary.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Nextcloud's client — nextcloud's own code, living in the module (novox/hq ADR 0044). Both this
|
// Nextcloud's client — nextcloud's own code, living in the module (novox/hq ADR 0039). Both this
|
||||||
// module's tools and its events entrypoint import it, and nothing outside nextcloud does.
|
// module's tools and its events entrypoint import it, and nothing outside nextcloud does.
|
||||||
//
|
//
|
||||||
// Nextcloud is administered two ways, and this client speaks both:
|
// Nextcloud is administered two ways, and this client speaks both:
|
||||||
@@ -25,7 +25,7 @@ export interface NextcloudShare {
|
|||||||
owner: string;
|
owner: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// nextcloud's events. The tool runtime imports this once the broker is bound. It watches the user
|
// nextcloud's events. The tool runtime imports this once the broker is bound. It watches the user
|
||||||
// list and the share list and announces new arrivals.
|
// list and the share list and announces new arrivals.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.nextcloud.user.created — a user account appeared (occ user:list)
|
// module.nextcloud.user.created — a user account appeared (occ user:list)
|
||||||
// module.nextcloud.share.created — a share appeared (OCS shares)
|
// module.nextcloud.share.created — a share appeared (OCS shares)
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-nextcloud",
|
"name": "@novox/module-nextcloud",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "nextcloud — file sync and share. Its client, tools and events live here (novox/hq ADR 0044).",
|
"description": "nextcloud — file sync and share. Its client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// nextcloud's tools — importing nextcloud's own client (novox/hq ADR 0044). occ runs inside the
|
// nextcloud's tools — importing nextcloud's own client (novox/hq ADR 0039). occ runs inside the
|
||||||
// container; shares come over OCS. They return structured data; the mesh serves them through the
|
// container; shares come over OCS. They return structured data; the mesh serves them through the
|
||||||
// sdk's tool harness.
|
// sdk's tool harness.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Node-RED's admin-API client — nodered's own code, living in the module (novox/hq ADR 0044). Only
|
// Node-RED's admin-API client — nodered's own code, living in the module (novox/hq ADR 0039). Only
|
||||||
// this module's tools import it; nodered has nothing to poll, so there is no events entrypoint.
|
// this module's tools import it; nodered has nothing to poll, so there is no events entrypoint.
|
||||||
//
|
//
|
||||||
// Node-RED exposes a runtime admin API under its base URL: GET/POST /flows for the whole flow
|
// Node-RED exposes a runtime admin API under its base URL: GET/POST /flows for the whole flow
|
||||||
@@ -20,7 +20,7 @@ export interface NodeRedNodeModule {
|
|||||||
types: string[];
|
types: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-nodered",
|
"name": "@novox/module-nodered",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "nodered — flow-based automation. Its client and tools live here (novox/hq ADR 0044).",
|
"description": "nodered — flow-based automation. Its client and tools live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// nodered's tools — importing nodered's own admin-API client (novox/hq ADR 0044). They return
|
// nodered's tools — importing nodered's own admin-API client (novox/hq ADR 0039). They return
|
||||||
// structured data; the mesh serves them through the sdk's tool harness.
|
// structured data; the mesh serves them through the sdk's tool harness.
|
||||||
//
|
//
|
||||||
// The deploy tool is nodered's one event source (novox/hq ADR 0046/0047): a successful deploy
|
// The deploy tool is nodered's one event source (novox/hq ADR 0041/0042): a successful deploy
|
||||||
// emits module.nodered.flows.deployed. nodered has nothing to observe on a timer, so there is no
|
// emits module.nodered.flows.deployed. nodered has nothing to observe on a timer, so there is no
|
||||||
// separate events entrypoint — the emit rides the action that causes it. The emit is best-effort:
|
// separate events entrypoint — the emit rides the action that causes it. The emit is best-effort:
|
||||||
// if no broker is bound, the deploy still succeeds and the announcement is simply skipped.
|
// if no broker is bound, the deploy still succeeds and the announcement is simply skipped.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The NZBGet API client — nzbget's own code, living in the module (novox/hq ADR 0044). Ported from
|
// The NZBGet API client — nzbget's own code, living in the module (novox/hq ADR 0039). Ported from
|
||||||
// hal's shared nzbget tools, but self-contained: a change to NZBGet's JSON-RPC now rebuilds only
|
// hal's shared nzbget tools, but self-contained: a change to NZBGet's JSON-RPC now rebuilds only
|
||||||
// nzbget and nothing else. Both this module's tools and its events entrypoint import it, and
|
// nzbget and nothing else. Both this module's tools and its events entrypoint import it, and
|
||||||
// nothing outside nzbget does. NZBGet speaks JSON-RPC at /jsonrpc, behind HTTP Basic auth.
|
// nothing outside nzbget does. NZBGet speaks JSON-RPC at /jsonrpc, behind HTTP Basic auth.
|
||||||
@@ -41,7 +41,7 @@ export interface NzbgetHistoryItem {
|
|||||||
success: boolean;
|
success: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// nzbget's events. The tool runtime imports this once the broker is bound. It watches the download
|
// nzbget's events. The tool runtime imports this once the broker is bound. It watches the download
|
||||||
// queue and the history and turns their comings and goings into mesh events.
|
// queue and the history and turns their comings and goings into mesh events.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.nzbget.download.added — an NZB entered the queue
|
// module.nzbget.download.added — an NZB entered the queue
|
||||||
// module.nzbget.download.completed — an NZB finished successfully (left the queue, landed in
|
// module.nzbget.download.completed — an NZB finished successfully (left the queue, landed in
|
||||||
// history as SUCCESS). This exact routing key is what the
|
// history as SUCCESS). This exact routing key is what the
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-nzbget",
|
"name": "@novox/module-nzbget",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "nzbget — Usenet download client. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "nzbget — Usenet download client. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// nzbget's tools — ported from the shared hal sdk (novox/hq ADR 0044), importing nzbget's own
|
// nzbget's tools — ported from the shared hal sdk (novox/hq ADR 0039), importing nzbget's own
|
||||||
// client. They return structured data (not the pre-formatted text hal returned); the mesh serves
|
// client. They return structured data (not the pre-formatted text hal returned); the mesh serves
|
||||||
// them through the sdk's tool harness.
|
// them through the sdk's tool harness.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Ombi API client — ombi's own code, living in the module (novox/hq ADR 0044). Ombi is the
|
// The Ombi API client — ombi's own code, living in the module (novox/hq ADR 0039). Ombi is the
|
||||||
// request front-end: viewers ask for movies and shows, and an operator approves them. This client
|
// request front-end: viewers ask for movies and shows, and an operator approves them. This client
|
||||||
// talks its /api/v1 REST API (keyed by an ApiKey header); ombi's tools and events import it.
|
// talks its /api/v1 REST API (keyed by an ApiKey header); ombi's tools and events import it.
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export interface RequestCounts {
|
|||||||
available: number;
|
available: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// are worth announcing — the mesh can notify on a new request, and act on an approval (that is when
|
// are worth announcing — the mesh can notify on a new request, and act on an approval (that is when
|
||||||
// a downloader should start looking).
|
// a downloader should start looking).
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.ombi.request.created — a viewer filed a new request
|
// module.ombi.request.created — a viewer filed a new request
|
||||||
// module.ombi.request.approved — a request was approved
|
// module.ombi.request.approved — a request was approved
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-ombi",
|
"name": "@novox/module-ombi",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "ombi — media requests. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "ombi — media requests. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// ombi's tools — its own code (novox/hq ADR 0044), importing ombi's client. They return structured
|
// ombi's tools — its own code (novox/hq ADR 0039), importing ombi's client. They return structured
|
||||||
// data; the mesh serves them through the sdk's tool harness.
|
// data; the mesh serves them through the sdk's tool harness.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The photo app's API client — photos' own code, living in the module (novox/hq ADR 0044). The
|
// The photo app's API client — photos' own code, living in the module (novox/hq ADR 0039). The
|
||||||
// module packages a self-hosted photo library (immich-shaped: a REST API under `/api`, authenticated
|
// module packages a self-hosted photo library (immich-shaped: a REST API under `/api`, authenticated
|
||||||
// by an API key sent as the `x-api-key` header). The client speaks only what the tools and the
|
// by an API key sent as the `x-api-key` header). The client speaks only what the tools and the
|
||||||
// item-added event need: server version and statistics, albums, and recent assets.
|
// item-added event need: server version and statistics, albums, and recent assets.
|
||||||
@@ -26,7 +26,7 @@ export interface PhotosAsset {
|
|||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// photos' events. The tool runtime imports this once the broker is bound. It watches the library and
|
// photos' events. The tool runtime imports this once the broker is bound. It watches the library and
|
||||||
// announces newly added assets.
|
// announces newly added assets.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.photos.item.added — a new asset appeared in the library
|
// module.photos.item.added — a new asset appeared in the library
|
||||||
//
|
//
|
||||||
// New assets are found by diffing the recent-assets slice by asset id. Primed silently on the first
|
// New assets are found by diffing the recent-assets slice by asset id. Primed silently on the first
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-photos",
|
"name": "@novox/module-photos",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "photos — self-hosted photo library. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "photos — self-hosted photo library. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// photos' tools (novox/hq ADR 0044), importing photos' own client.
|
// photos' tools (novox/hq ADR 0039), importing photos' own client.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
import { PhotosClient } from "../client.js";
|
import { PhotosClient } from "../client.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Plex API client — plex's own code, living in the module (novox/hq ADR 0044). Moved out of the
|
// The Plex API client — plex's own code, living in the module (novox/hq ADR 0039). Moved out of the
|
||||||
// shared hal sdk, where a change to Plex's API rebuilt everything; here it rebuilds only plex. Both
|
// shared hal sdk, where a change to Plex's API rebuilt everything; here it rebuilds only plex. Both
|
||||||
// this module's tools and its events entrypoint import it, and nothing outside plex does.
|
// this module's tools and its events entrypoint import it, and nothing outside plex does.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// plex's events. The tool runtime imports this once the broker is bound, and it does two things:
|
// plex's events. The tool runtime imports this once the broker is bound, and it does two things:
|
||||||
// it watches the server and emits what happened, and it reacts to the mesh's media events.
|
// it watches the server and emits what happened, and it reacts to the mesh's media events.
|
||||||
//
|
//
|
||||||
// Emits (novox/hq ADR 0046/0047):
|
// Emits (novox/hq ADR 0041/0042):
|
||||||
// module.plex.playback.started / .stopped — someone began or ended watching
|
// module.plex.playback.started / .stopped — someone began or ended watching
|
||||||
// module.plex.item.added — a new item appeared in a library
|
// module.plex.item.added — a new item appeared in a library
|
||||||
// Consumes:
|
// Consumes:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-plex",
|
"name": "@novox/module-plex",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "plex — media server. Its API client, tools and events live here (novox/hq ADR 0044).",
|
"description": "plex — media server. Its API client, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// plex's tools — moved here from the shared sdk (novox/hq ADR 0044), importing plex's own client.
|
// plex's tools — moved here from the shared sdk (novox/hq ADR 0039), importing plex's own client.
|
||||||
// They return structured data; the mesh serves them through the sdk's tool harness.
|
// They return structured data; the mesh serves them through the sdk's tool harness.
|
||||||
|
|
||||||
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// The Portainer API client — portainer's own code, living in the module (novox/hq ADR 0044).
|
// The Portainer API client — portainer's own code, living in the module (novox/hq ADR 0039).
|
||||||
// portainer is tools-only: its "events" would really be the underlying containers' lifecycle,
|
// portainer is tools-only: its "events" would really be the underlying containers' lifecycle,
|
||||||
// which the host owns and emits — so this module reads Portainer's own resources (endpoints,
|
// which the host owns and emits — so this module reads Portainer's own resources (endpoints,
|
||||||
// stacks, containers) and exposes them, and stops there.
|
// stacks, containers) and exposes them, and stops there.
|
||||||
@@ -29,7 +29,7 @@ export interface PortainerContainer {
|
|||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The settings-merged config the mesh delivers (novox/hq ADR 0051): { url, apiKey, token, password, user, ... }. */
|
/** The settings-merged config the mesh delivers (novox/hq ADR 0046): { url, apiKey, token, password, user, ... }. */
|
||||||
function meshConfig(file?: string): Record<string, string> {
|
function meshConfig(file?: string): Record<string, string> {
|
||||||
if (!file) return {};
|
if (!file) return {};
|
||||||
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
try { return JSON.parse(readFileSync(file, "utf8")) as Record<string, string>; }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-portainer",
|
"name": "@novox/module-portainer",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "portainer — container management UI. Its API client and tools live here (novox/hq ADR 0044).",
|
"description": "portainer — container management UI. Its API client and tools live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// portainer's tools — its own code (novox/hq ADR 0044), importing portainer's own client. They
|
// portainer's tools — its own code (novox/hq ADR 0039), importing portainer's own client. They
|
||||||
// return structured data; the mesh serves them through the sdk's tool harness. portainer is
|
// return structured data; the mesh serves them through the sdk's tool harness. portainer is
|
||||||
// tools-only (no events entrypoint): a container starting or stopping is the host's signal to emit,
|
// tools-only (no events entrypoint): a container starting or stopping is the host's signal to emit,
|
||||||
// not Portainer's to re-announce.
|
// not Portainer's to re-announce.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// postgres's admin client — postgres's own code, living in the module (novox/hq ADR 0044). Both this
|
// postgres's admin client — postgres's own code, living in the module (novox/hq ADR 0039). Both this
|
||||||
// module's tools and its provisioner import it, and nothing outside postgres does.
|
// module's tools and its provisioner import it, and nothing outside postgres does.
|
||||||
//
|
//
|
||||||
// SQL is executed through `psql`, not a wire-protocol driver: the module may take NO npm dependency
|
// SQL is executed through `psql`, not a wire-protocol driver: the module may take NO npm dependency
|
||||||
@@ -60,7 +60,7 @@ export class PostgresClient {
|
|||||||
async query(sql: string, database = "postgres"): Promise<QueryResult> {
|
async query(sql: string, database = "postgres"): Promise<QueryResult> {
|
||||||
// Executed through `psql`, the way minio drives itself through `mc` and mailu through doveadm:
|
// Executed through `psql`, the way minio drives itself through `mc` and mailu through doveadm:
|
||||||
// node has no postgres wire client without an npm dependency, and the module owns its own code
|
// node has no postgres wire client without an npm dependency, and the module owns its own code
|
||||||
// (ADR 0044), so it shells out to the client the postgres tools ship. CSV so the rows come back
|
// (ADR 0039), so it shells out to the client the postgres tools ship. CSV so the rows come back
|
||||||
// structured; ON_ERROR_STOP so a failed statement is an error here, not a success with a warning.
|
// structured; ON_ERROR_STOP so a failed statement is an error here, not a success with a warning.
|
||||||
const { stdout } = await run(
|
const { stdout } = await run(
|
||||||
"psql",
|
"psql",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// postgres's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
// postgres's events entrypoint, loaded by the per-node tool host (the provisioner container runs
|
||||||
// ./provisioner separately). The database lifecycle events are EMITTED from the provisioner, where
|
// ./provisioner separately). The database lifecycle events are EMITTED from the provisioner, where
|
||||||
// the lifecycle actually happens (novox/hq ADR 0046/0047):
|
// the lifecycle actually happens (novox/hq ADR 0041/0042):
|
||||||
// module.postgres.database.provisioned — a consumer's database + owning role was created
|
// module.postgres.database.provisioned — a consumer's database + owning role was created
|
||||||
// module.postgres.database.deprovisioned — that database was removed
|
// module.postgres.database.deprovisioned — that database was removed
|
||||||
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@novox/module-postgres",
|
"name": "@novox/module-postgres",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "postgres — provides the mesh postgres-database interface. Its client, provisioner, tools and events live here (novox/hq ADR 0044).",
|
"description": "postgres — provides the mesh postgres-database interface. Its client, provisioner, tools and events live here (novox/hq ADR 0039).",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user