Add native host management and role-based operator authentication

Introduce an outbound Python host agent, Debian installer, durable remote
operations, immutable game-file libraries, host-aware deployments and ports,
and occupancy-safe campaigns and draining. Native hosts do not require Komodo.

Add administrator, operator and viewer accounts, protected management routes,
secure sessions, password recovery, audit history and separate telemetry tokens.
Document first-admin setup, reporter migration and live-host acceptance checks.

Validation: 1,343 TypeScript tests and 39 agent tests passed, along with type
checking, the production build, compiled SSR/API smoke tests and administrator
recovery. Real Debian/Docker T5/T6 acceptance remains unrun.
This commit is contained in:
2026-10-03 11:42:36 -04:00
parent a2e2311aa9
commit e5c0fda1ed
127 changed files with 8485 additions and 657 deletions
+39
View File
@@ -0,0 +1,39 @@
import type Database from 'better-sqlite3';
const initialized = new WeakSet<Database.Database>();
export function ensureGameFileSchema(handle: Database.Database): void {
if (initialized.has(handle)) return;
handle.exec(`
CREATE TABLE IF NOT EXISTS game_file_libraries (
id TEXT PRIMARY KEY, name TEXT NOT NULL COLLATE NOCASE UNIQUE,
game TEXT NOT NULL CHECK(game IN ('t5','t6')), description TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS game_file_versions (
id TEXT PRIMARY KEY, library_id TEXT NOT NULL REFERENCES game_file_libraries(id),
version TEXT NOT NULL, description TEXT NOT NULL, dlc TEXT NOT NULL,
state TEXT NOT NULL CHECK(state IN ('importing','ready','failed')),
size_bytes INTEGER NOT NULL DEFAULT 0, manifest TEXT NOT NULL DEFAULT '[]',
manifest_sha256 TEXT, created_at TEXT NOT NULL, published_at TEXT,
storage_location TEXT NOT NULL, source_host_id TEXT NOT NULL, source_volume TEXT NOT NULL,
error TEXT, operation_id TEXT, UNIQUE(library_id, version)
);
CREATE TABLE IF NOT EXISTS host_library_caches (
host_id TEXT NOT NULL, version_id TEXT NOT NULL REFERENCES game_file_versions(id),
status TEXT NOT NULL CHECK(status IN ('missing','downloading','verifying','ready','failed','outdated')),
bytes_downloaded INTEGER NOT NULL DEFAULT 0, bytes_total INTEGER NOT NULL DEFAULT 0,
manifest_sha256 TEXT, error TEXT, updated_at TEXT NOT NULL, operation_id TEXT,
PRIMARY KEY(host_id, version_id)
);
CREATE TABLE IF NOT EXISTS game_file_entries (
version_id TEXT NOT NULL REFERENCES game_file_versions(id), path TEXT NOT NULL,
size INTEGER NOT NULL, sha256 TEXT NOT NULL, PRIMARY KEY(version_id,path)
);
INSERT OR IGNORE INTO game_file_entries(version_id,path,size,sha256)
SELECT v.id,json_extract(f.value,'$.path'),json_extract(f.value,'$.size'),json_extract(f.value,'$.sha256')
FROM game_file_versions v,json_each(v.manifest) f;
CREATE TRIGGER IF NOT EXISTS game_file_versions_immutable BEFORE UPDATE ON game_file_versions
WHEN OLD.state = 'ready' BEGIN SELECT RAISE(ABORT, 'Published game-file versions are immutable'); END;
`);
initialized.add(handle);
}