/* RFC-0565: Kernel module registering DHT workspace commands. All commands are workspace-scoped and not cacheable (they depend on external network state and workshop-local files). Commands are added incrementally as they are implemented. Do not implement command handlers — those live in their own files. Do not implement DHT routing — that lives in node.ts. Module registers commands only — DHT logic lives in the sibling handler modules. All commands are workspace-scoped — no app context is required. RFC-0565: initial implementation — dht module with dht.node.init command. RFC-1097: step 6 — compass.migrate codemod run Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. RFC-1173: declare mutatesState on all kernel commands — collectDeclarationDiagnostics emits error-severity MUTATES-STATE-DECLARED, command.manifest.validate is the blocking consumer in packages.check, sweep declares the flag on every command definition (factories hardcode false for read-only check specs) RFC-1173: fixup: flip mutatesState to true on 19 commands that declare writes/generates — the codemod's name-suffix rules misclassified leitstand.dev-deploy, leitstand.certify, coverage.report and peers as read-only; declared writes imply mutation */ import type { ModuleExport } from "@warpgogol/werkstatt-shared/kernel"; export async function createDhtModule(): Promise { const { runDhtNodeInit } = await import("./init.ts"); const { runDhtLookup } = await import("./lookup.ts"); const { runDhtRegister } = await import("./register.ts"); const { runDhtCapacityPublish } = await import("./capacity.ts"); const { runDhtPlacement } = await import("./placement.ts"); const { runDhtStatus } = await import("./status.ts"); return { name: "dht", version: "0.1.0", declarations: [], commands: [ { name: "dht.node.init", coverage: "operator" as const, mutatesState: true, modulePath: "packages/werkstatt-engine/src/kernel/dht/dht-module.ts", generates: [], description: "RFC-0565: initialize DHT configuration. Creates werkstatt.dht.json with " + "bind address, bootstrap nodes, replication factor, and timeout parameters. " + "Use --bind to specify the bind address. Use --bootstrap " + "to specify a bootstrap node (can be repeated). Use --json for machine-readable output.", scope: "workspace", cacheable: false, flags: { bind: { kind: "string", description: "Bind address (host:port)." }, bootstrap: { kind: "string[]", description: "Bootstrap node (host:port, repeatable).", }, }, reads: ["werkstatt.identity.json"], writes: ["werkstatt.dht.json"], execute: runDhtNodeInit, }, { name: "dht.lookup", coverage: "operator" as const, mutatesState: true, modulePath: "packages/werkstatt-engine/src/kernel/dht/dht-module.ts", generates: [], description: "RFC-0565: resolve a site id to its DHT entry. Queries the DHT (or local " + "cache if fresh) and validates the entry signature. Routes around dead " + "workshops detected by SWIM. Use --site-id to specify the site. " + "Use --force to bypass cache. Use --json for machine-readable output.", scope: "workspace", cacheable: false, flags: { "site-id": { kind: "string", required: true, description: "Site id to resolve." }, force: { kind: "boolean", description: "Bypass the local cache." }, }, reads: [ "werkstatt.dht.json", "werkstatt.dht.cache.json", "werkstatt.identity.json", "werkstatt.swim.json", "werkstatt.genome.log", ], writes: ["werkstatt.dht.cache.json"], execute: runDhtLookup, }, { name: "dht.register", coverage: "operator" as const, mutatesState: true, modulePath: "packages/werkstatt-engine/src/kernel/dht/dht-module.ts", generates: [], description: "RFC-0565: publish a local registry entry to the DHT. Signs the entry with " + "the operator's Ed25519 keypair. Uses LWW on lastUpdated for conflict resolution. " + "Required flags: --site-id , --owner , --endpoint . " + "Optional: --mirrors (can be repeated). Use --json for output.", scope: "workspace", cacheable: false, flags: { "site-id": { kind: "string", required: true, description: "Site id to register." }, owner: { kind: "string", required: true, description: "Owner identity (did:web identifier).", }, endpoint: { kind: "string", required: true, description: "Workshop endpoint (host:port).", }, mirrors: { kind: "string[]", description: "Additional mirror endpoints (host:port, repeatable).", }, }, reads: ["werkstatt.dht.json", "werkstatt.identity.json"], writes: ["werkstatt.dht.cache.json"], execute: runDhtRegister, }, { name: "dht.capacity.publish", coverage: "operator" as const, coverageNote: "DHT capacity publication", mutatesState: true, modulePath: "packages/werkstatt-engine/src/kernel/dht/dht-module.ts", generates: [], description: "RFC-0565: publish workshop capacity to the DHT for placement decisions. " + "Signs the capacity entry with the operator's Ed25519 keypair. " + "Required: --workshop-id , --available-slots . " + "Optional: --endpoint , --storage-limit-mb , --bandwidth-limit-mbps . Use --json for output.", scope: "workspace", cacheable: false, flags: { "workshop-id": { kind: "string", required: true, description: "Workshop id publishing capacity.", }, "available-slots": { kind: "string", required: true, description: "Number of available placement slots.", }, endpoint: { kind: "string", description: "Workshop endpoint (host:port, default 0.0.0.0:0).", }, "storage-limit-mb": { kind: "string", description: "Storage limit in MB." }, "bandwidth-limit-mbps": { kind: "string", description: "Bandwidth limit in Mbps.", }, }, reads: ["werkstatt.dht.json", "werkstatt.identity.json"], writes: [], execute: runDhtCapacityPublish, }, { name: "dht.placement", coverage: "operator" as const, mutatesState: false, modulePath: "packages/werkstatt-engine/src/kernel/dht/dht-module.ts", generates: [], description: "RFC-0565: determine the best workshop for placing a site by querying DHT " + "capacity entries. Uses least-loaded strategy by default. " + "Required: --site-id , --workshops (or repeated). " + "Optional: --strategy . Use --json for output.", scope: "workspace", cacheable: false, flags: { "site-id": { kind: "string", required: true, description: "Site id to place." }, strategy: { kind: "string", description: "Placement strategy (least-loaded|nearest|owner-preference).", }, workshops: { kind: "string[]", required: true, description: "Candidate workshop ids (comma-separated or repeated).", }, }, reads: ["werkstatt.dht.json", "werkstatt.identity.json", "werkstatt.swim.json"], writes: [], execute: runDhtPlacement, }, { name: "dht.status", coverage: "operator" as const, mutatesState: false, modulePath: "packages/werkstatt-engine/src/kernel/dht/dht-module.ts", generates: [], description: "RFC-0565: report local DHT node status including config, cache entries, " + "identity bootstrap state, and SWIM configuration. Local-only query — no network I/O. " + "Use --json for machine-readable output.", scope: "workspace", cacheable: false, flags: {}, reads: [ "werkstatt.dht.json", "werkstatt.dht.cache.json", "werkstatt.identity.json", "werkstatt.swim.json", ], writes: [], execute: runDhtStatus, }, ], pipelines: [], }; }