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:
commit
aa5e262f3a
880 changed files with 111042 additions and 0 deletions
109
packages/core/src/shared-files.ts
Normal file
109
packages/core/src/shared-files.ts
Normal 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>;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue