Commit graph

29 commits

Author SHA1 Message Date
6161ac4740 Manual: full participation guide (media/accounts/decisions/safety) + Enter-to-door + provisioner env passthrough 2026-10-12 15:49:25 +00:00
07d2df8c53 Docs: cloud hosting requirements + migration runbook (measured sizing) 2026-10-12 15:49:25 +00:00
4a7e78b28d Collectives P1: participation landing (ES/EN), group tree, room provisioning APIs
- 030: groups.parent_id tree + kind discriminator + group_rooms mapping
- /collectives module: create/tree/join APIs, MatrixProvisioner (open room
  public+federated, closed room E2EE-at-creation, onboarding room), NullProvisioner
  fail-soft when ARCHIVE_PROVISIONER_* unset
- web root becomes the public participation manual (ES-first + EN toggle,
  three paths: join by invite, start collective, solo account)
- participation manual content in core
2026-10-12 13:53:40 +00:00
c31762b82d connector-facebook: drop ambiguous Response annotations (type-hoisting collision) 2026-10-12 13:53:40 +00:00
ac969f05c9 Chat client hosted at chat.occupymedia.cc: Element + API on one host, hub welcome page 2026-10-12 13:16:45 +00:00
39dcf7a8b8 Edge: chat root redirects to branded client, serve matrix well-known; onboarding hero full-bleed on black 2026-10-12 12:55:05 +00:00
699b9412c1 Phase B: encrypted archive export bundles (age), power-level-gated export command, encryption-state linter 2026-10-12 12:46:56 +00:00
a7ad1f059f Design: open room default-on with opt-out at collective creation 2026-10-12 11:26:33 +00:00
92f318de7c Design: account tiers, contribution policy disclosure, Telegram open-room access 2026-10-12 10:28:43 +00:00
2ee5c55de1 Design: collectives architecture (participation landing, room matrix, subcollectives, diffusion) 2026-10-12 09:42:16 +00:00
e6a78a0805 Hub nav: Chat tab (branded self-hosted Element at element.occupymedia.cc) 2026-10-12 09:13:37 +00:00
7ea0b1e137 Phase two: archive origin facet, redaction-aware reads, share revocation on redaction
- group-files: origin filter facet; GET /files/:id returns 410 for unavailable
  rows after the ACL recheck; fix latent group_members.status filter and unbound
  workspace parameter that would 500 listFiles
- archive-bot: markRedacted revokes live group_file_shares rows in-transaction
- new live-DB integration suite for the archive index surface
2026-10-11 21:44:23 +00:00
7cea73dc0d Edge: tailscale0 relay exemption, CT320 transit hop, files/office/intake host parity 2026-10-11 21:36:20 +00:00
83e34aa343 Switch chat CT to tuwunel 1.9.4 with R2 media provider 2026-10-11 21:09:51 +00:00
b1b2a07b3d Pass FILES_OCID_* WebDAV credentials into the api container 2026-10-11 16:25:48 +00:00
2cb77ae6b4 Harden archive bot for production; exempt container egress in edge firewall
- archive_sync_state keyed by room per migration 029 (drop synthetic id cursor)
- putStaging buffers the stream and sends content-length (oCIS rejects chunked)
- mimeClass passthrough from sniffed payload
- edge-firewall: RETURN docker-bridge ingress before the CF-range catch-all so
  container egress (Matrix/R2/platform) survives the 80/443 source filter
- files scaffold: keep the ocdav data-gateway hop on-loopback
2026-10-11 16:12:43 +00:00
23bd4dd5ad Exclude connector-sdk tests from the build typecheck
The docker build (fresh install, container @types resolution) fails on
http.test.ts's enhanced-Response mock (bytes/textStream) — tests are vitest
territory, never part of the dist build.
2026-10-11 11:21:53 +00:00
4bf90ffe25 Complete files UI rewiring to the deployed api surface; expose mimeClass
page.tsx now calls the real backend: GET /files with groupId/name/mimeClass/
cursor/limit (origin filter dropped — the api has no origin param), shares
tracked as ShareCreated (revoke by id), retention mapped to the orthogonal
legalHold model, downloads by item.name, samples reshaped to the wire
contract.

GroupFile gains mimeClass (the indexed coarse class the row icon and the
filter depend on): core contract, service row mapper, parseGroupFile guard,
test fixture, UI types — one vocabulary end to end. parseGroupFile's roomId
becomes null-tolerant to match direct-upload rows (mapping input stays
strict).

Verified: core build clean, api typecheck 0 errors + group-files 22/22,
web typecheck 0 errors.
2026-10-11 11:08:08 +00:00
e7e703c105 Integrate group files: nullable room for uploads, archive class, claim idempotency, upload route
Wires the three parallel group-cloud tracks to one surface and fixes the
seams between them:

- chat_media_archive.room_id becomes nullable (direct web uploads have no
  Matrix room yet — the contract's null-event pattern extends to the room);
  core GroupFile.roomId and ArchiveEntryInput.roomId follow.
- mime_class gains 'archive' (zip) across the 029 CHECK, validation
  MIME_CLASSES and the bot classifier — one vocabulary everywhere.
- registerArchiveEntry claim keys are null-safe for room-less uploads.
- New: POST /files/groups/:groupId/upload — FileInterceptor (20 MiB) streams
  through magic-byte sniffing, library-safe naming, oCIS upload and the
  metadata index, behind group-membership ACL (Owner/Admin bypass).
- safeFileName/sniffMime promoted to packages/core (file-names.ts,
  file-mime.ts) so the bot and the api share one implementation; the bot's
  local modules become re-export shims; @types/node added to core.

Verified: api typecheck clean, group-files 22/22, bot 74/74, web typecheck
clean; 029 re-applied clean on a fresh scratch DB (room_id nullable,
archive class, claim_key unique all confirmed in the catalog).
2026-10-11 10:09:24 +00:00
688a2ce996 Enable basic-auth WebDAV on the oCIS proxy (machine access for api/bot)
The group-files api and the archive bot authenticate to oCIS WebDAV with
basic credentials (admin until a dedicated service user exists). The proxy
is LAN-published only; no WAN forward exists.
2026-10-11 08:49:27 +00:00
777c67f7a3 Expand video-AI architecture doc to the full research report
281-line version with the person-identity DDL sketch, per-model license
audit table, VRAM/cost budget, GDPR Art. 9 analysis, and all primary-source
citations — replaces the 53-line executive summary.
2026-10-11 08:29:30 +00:00
d58db63fe6 Add group cloud: files index api, web UI, archive sync bot
The Telegram-like group file contract (packages/core shared-files.ts) becomes
a working subsystem, built in three parallel tracks:

API (NestJS): migration 029 group_files (chat_media_archive metadata-only
index with partial replay-unique + RLS, group_file_shares with sha256 token
persistence, archive_sync_state/outbox durable-claim seam incl. a unique
claim_key index), group-files module (list/search with cursor pagination,
direct upload ≤20 MiB streamed+hashed to oCIS WebDAV, download with ACL +
retention rechecks, share links with expiry, retention modes incl.
legalHold) — RBAC group-membership authz + audit chain events.

Web: Files section per group — group picker, drag-drop upload (20 MiB cap),
search + MIME/origin filters, cursor pagination, download via authenticated
blob fetch, share dialog with expiry + shown-once token, admin retention
dialog; bilingual EN/ES (wiki i18n pattern); nav sidebar/palette/bottom-nav
wiring; MIME classes aligned to the DB CHECK (document/other).

Archive bot (apps/archive-bot): plain sync-loop Matrix bot mirroring
attachments from explicitly-mapped unencrypted archive rooms — raw /sync
with durable since-cursor, outbox-claimed ingestion (hash-verified stream,
no temp files), magic-byte MIME sniffing, safe-name normalization, WebDAV
upload, indexed + replied with link, redaction→unavailable; health endpoint;
Dockerfile. Tests 74/74, typecheck clean across api/web/bot.
2026-10-11 08:21:11 +00:00
eb1b60f67d Add video-AI architecture research (transcription, diarization, faces, GDPR)
Research: use PeerTube
native transcription (core since 6.2, whisper-ctranslate2, remote runners on
GPU nodes; large-v3-turbo MIT via model_path); pyannote community-1
diarization (CC-BY-4.0, clean); WhisperX fusion.

Face recognition license audit: NO accurate open FR model has clean weights
(InsightFace/SCRFD non-commercial, yolo-face GPL, AdaFace unstated) —
posture is detection+tracking+blur by default; identity matching opt-in,
human-review-gated, collective decision + DPIA required (Art. 9 biometric
data; automated FR over non-consenting third parties effectively unlawful).

DB DDL sketch: persons/person_face_embeddings/video_face_detections/
transcript_segments/video_speaker_person/identity_audit with review gating,
visibility tiers, erasure workflow.
2026-10-11 07:55:01 +00:00
6cdffa60b2 Track the files scaffold; sync deployed oCIS compose requirements
infra/occupymedia/files/ was gitignored in phase one ('nothing deployed');
the oCIS deployment is now live, and the storage doc + DEPLOY.md link to its
runbook. Track the secret-free scaffold (compose.yml, csp.yaml, README,
tuwunel example, .env.example); keep only .env + secrets/ ignored.

compose.yml carries the deployment-proven fixes: ocis runs as user 1000 with
OCIS_CONFIG_DIR=/etc/ocis and HOME=/var/lib/ocis (source-built images do not
inherit the official image's conventions), and 9200/9980 publish on the LAN
for the CT150 tunnel connector.
2026-10-11 07:51:59 +00:00
fc5f94400f Add content-search architecture research (group cloud indexing)
Research: Tika full-text
via 3 oCIS env vars (phase 1); bge-m3 on TEI + pgvector halfvec/HNSW in the
existing Postgres with Postgres FTS + RRF hybrid (phase 2); Docling tier for
scanned PDFs; NATS event subscription (UploadReady et al) + nightly PROPFIND
reconciliation for change propagation. jina-v3 excluded (non-commercial).
Bilingual EN+ES eval set flagged as mandatory before freezing models.
2026-10-11 06:25:19 +00:00
43035a94e8 Sync storage ownership doc with provisioned R2 reality
The hub-* buckets were provisioned 2026-10-08 (files-blobs, matrix-media,
media-library, pads); the doc's occupymedia-files-blobs name never existed.
hub-files-blobs is now live as the oCIS S3NG bucket (occ-files CT 320,
deployed 2026-10-10) with a bucket-scoped S3 key.
2026-10-11 04:19:28 +00:00
161f06f1e9 Ship edge-activate.sh: workstation-side activation + pool verification
One-shot watcher for the workstation: probes ping.occupymedia.cc through a
reachable edge IP, flips the four hostnames after the port-forward verifies,
checks the public path for 6 minutes and auto-reverts to TUNNEL if the new
pool assignment is broken too. Retires itself if the tunnel heals first.
README updated to match.
2026-10-10 21:26:54 +00:00
9365b561b2 Add edge stack: second ingress path + dual-path DNS failover
Cloudflare's tunnel edge-pool assignment can blackhole the site for whole
regions (2026-10-10: tunnel hostnames served from an unreachable anycast
range for hours). This adds an independent path and the automation that
keeps occupymedia.cc reachable when either path is sick:

- nginx edge mirroring the tunnel ingress (app/api/tube/chat/ping), LE
  wildcard via dns-01, twice-daily renewal
- DOCKER-USER firewall: 80/443 restricted to Cloudflare ranges (CF mode)
  or OPEN for grey-cloud mode via firewall.mode
- edge-watcher (60s systemd timer): dynamic public-IP sync, dual-path
  health checks, DNS failover TUNNEL<->ORIGIN/GREY with hysteresis,
  gated by EDGE_ENABLED
- ping.occupymedia.cc sacrificial probe host for activation
- deployment runbook in README.md; credential files shipped as .example
2026-10-10 21:22:15 +00:00
aa5e262f3a 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/.
2026-10-10 11:56:42 +00:00