Tactical Media Broadcast Hub — initial release

Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
This commit is contained in:
glass beadian 2026-10-10 11:56:42 +00:00
commit aa5e262f3a
880 changed files with 111042 additions and 0 deletions

View file

@ -0,0 +1,26 @@
import { Controller, Get, Res, Inject } from "@nestjs/common";
import type { Response } from "express";
import { HealthService, type ComponentHealth } from "./health.service";
interface ReadinessBody {
status: "ready" | "degraded";
components: Record<string, ComponentHealth>;
}
@Controller()
export class HealthController {
constructor(@Inject(HealthService) private readonly health: HealthService) {}
@Get("healthz")
liveness(@Res() res: Response): void {
res.status(200).json({ status: "alive" });
}
@Get("readyz")
async readiness(@Res() res: Response): Promise<void> {
const components = await this.health.components();
const ready = Object.values(components).every((c) => c.status === "up");
const body: ReadinessBody = { status: ready ? "ready" : "degraded", components };
res.status(ready ? 200 : 503).json(body);
}
}

View file

@ -0,0 +1,2 @@
export { Injectable } from "@nestjs/common";
export { logger } from "../telemetry/logger";

View file

@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";
import { HealthController } from "./health.controller";
import { HealthService } from "./health.service";
@Module({
providers: [HealthService],
controllers: [HealthController],
exports: [HealthService],
})
export class HealthModule {}

View file

@ -0,0 +1,50 @@
import { assertPublishingSchema } from "@tactical-media-broadcast-hub/publishing-execution";
import { pool } from "../db/client.js";
import * as net from "net";
import { Injectable } from "@nestjs/common";
export type ComponentStatus = "up" | "down";
export interface ComponentHealth {
status: ComponentStatus;
}
interface ServiceEndpoint {
host: string;
port: number;
}
const serviceEnv: Record<string, ServiceEndpoint> = {
postgres: { host: process.env.POSTGRES_HOST ?? "localhost", port: Number(process.env.POSTGRES_PORT ?? 5433) },
redis: { host: process.env.REDIS_HOST ?? "localhost", port: Number(process.env.REDIS_PORT ?? 6379) },
temporal: { host: process.env.TEMPORAL_HOST ?? "localhost", port: Number(process.env.TEMPORAL_PORT ?? 7233) },
};
function probeTcp(host: string, port: number, timeoutMs: number): Promise<boolean> {
return new Promise((resolve) => {
const socket = new net.Socket();
const done = (ok: boolean): void => {
socket.destroy();
resolve(ok);
};
socket.setTimeout(timeoutMs);
socket.once("connect", () => done(true));
socket.once("timeout", () => done(false));
socket.once("error", () => done(false));
socket.connect(port, host);
});
}
@Injectable()
export class HealthService {
async components(): Promise<Record<string, ComponentHealth>> {
const entries = await Promise.all(
Object.entries(serviceEnv).map(async ([name, { host, port }]) => {
const up = await probeTcp(host, port, 1500);
return [name, { status: up ? "up" : "down" } as ComponentHealth] as const;
}),
);
const schema = await assertPublishingSchema(pool).then(() => "up" as const).catch(() => "down" as const);
return { ...Object.fromEntries(entries), publishing_schema: { status: schema } };
}
}