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,109 @@
/** Shared file/index contracts. Payloads are remote; these records contain metadata only. */
/** A share expiry never deletes a file. UTC ISO-8601 for timed retention. */
export type FileRetention = { legalHold: boolean } & (
| { mode: "keep"; expiresAt?: never }
| { mode: "until"; expiresAt: string }
);
export interface GroupFileMapping {
workspaceId: string;
groupId: string;
/** Immutable Matrix room ID, never an alias or display name. */
roomId: string;
spaceId: string;
folderId: string;
archivePolicy: "disabled" | "unencrypted-archive";
retention: FileRetention;
}
export type SharedFileOrigin =
| { kind: "matrix"; mxcUri: string }
| { kind: "bridge"; mxcUri: string; providerId: string; remoteEventId?: string }
| { kind: "upload" }
| { kind: "pad-export"; padId: string; revision: string };
export interface GroupFile {
id: string;
workspaceId: string;
groupId: string;
roomId: string;
senderId: string;
/** Null for a direct upload/export until a link is posted in the room. */
eventId: string | null;
origin: SharedFileOrigin;
name: string;
mimeType: string;
sizeBytes: number;
/** SHA-256 of verified payload bytes, lowercase hex; never an S3 ETag. */
checksum: { algorithm: "sha256"; value: string };
createdAt: string;
indexedAt: string;
file: { spaceId: string; resourceId: string; version: string };
/** Authenticated library link by default; never a persisted presigned R2 URL. */
shareLink: { url: string; access: "members" | "public"; expiresAt: string | null };
retention: FileRetention;
status: "available" | "revoked" | "expired";
}
export interface MatrixAttachmentCandidate {
roomId: string;
eventId: string;
senderId: string;
eventType: string;
msgtype: string;
/** Unknown state must fail closed, including on initial sync. */
roomEncryption: "unencrypted" | "encrypted" | "unknown";
encryptedFile: boolean;
redacted: boolean;
mxcUri: string;
}
/** Eligibility only; the adapter must also check membership/ACL, quotas and actual bytes. */
export function canArchiveAttachment(
mapping: GroupFileMapping,
event: MatrixAttachmentCandidate,
): boolean {
return mapping.archivePolicy === "unencrypted-archive"
&& mapping.roomId === event.roomId
&& event.roomEncryption === "unencrypted"
&& !event.encryptedFile
&& !event.redacted
&& event.eventType === "m.room.message"
&& ["m.file", "m.image", "m.audio", "m.video"].includes(event.msgtype)
&& /^mxc:\/\/[^/\s]+\/[^/\s?#]+$/.test(event.mxcUri);
}
/** Collision-free replay identity; DB uniqueness is scoped to workspace/room/event. */
export function attachmentArchiveKey(workspaceId: string, roomId: string, eventId: string): string {
return JSON.stringify([workspaceId, roomId, eventId]);
}
/** Future adapter boundary, not a running bot or public API. Commit before advancing sync. */
export interface HubArchivePort {
archive(mapping: GroupFileMapping, event: MatrixAttachmentCandidate): Promise<GroupFile>;
revoke(workspaceId: string, roomId: string, eventId: string): Promise<void>;
}
export interface CollaborativeDocument {
fileId: string;
mimeType: "application/vnd.oasis.opendocument.text";
editor: "wopi";
/** Authenticated launch URL; WOPI access tokens are short-lived and not indexed. */
launchUrl: string;
}
/** Etherpad/HedgeDoc-compatible link/export seam. No pad text or revisions in Hub DB. */
export interface SharedPad {
id: string;
workspaceId: string;
groupId: string;
roomId: string;
service: "etherpad-compatible" | "hedgedoc-compatible";
url: string;
payloadStore: "r2";
retention: FileRetention;
}
export interface PadExportPort {
exportRevision(pad: SharedPad, revision: string): Promise<GroupFile>;
}