Commit graph

30 commits

Author SHA1 Message Date
11f75e945e revert: matrix-sso to the JWT broker flow; tuwunel migrated to [global] profile
tuwunel's login/get_token demands device-bound UIA completed with
org.matrix.login.jwt — the AS-impersonation shortcut cannot mint login
tokens. The [global] profile migration activates [global.jwt] (register_user
+ JWT UIA), so the original flow works: JWT login (auto-registers
@hub_<user>) -> get_token with JWT UIA -> logout broker device.
2026-10-13 16:13:51 +00:00
4a0d398a76 fix: Matrix SSO via dedicated hub appservice (AS register + get_token impersonation)
The live tuwunel does not offer m.login.jwt (the jwt.* flat config keys were
inert), so the JWT broker flow could never work. Pivot: appservice.hub owns
the exclusive hub_* namespace on the homeserver; POST /matrix/session now
registers @hub_<username> with the as_token and mints a single-use
m.login.token via login/get_token (?user_id= impersonation). Optional
MATRIX_SSO_HS_TOKEN authenticates inbound appservice transactions, acked by
the new MatrixAppserviceController sink. Response shape unchanged.
2026-10-13 15:29:16 +00:00
fe5f2403d6 feat: in-app Chat (Matrix JWT SSO) + Drive (embedded oCIS)
- api: matrix-sso module (tuwunel jwt -> login/get_token), POST /matrix/session, GET /matrix/identity, drive controller
- web: /chat full-height Element view, /files Library+Drive tabs, embedded-app frame component
- edge: /hub-sso.html handshake route (origin-pinned CSP), X-Frame-Options strip on files
- oCIS: csp.yaml frame-ancestors app.occupymedia.cc
- server config still required: tuwunel [global.jwt], MATRIX_SSO_JWT_SECRET, oCIS restart
2026-10-13 14:57:07 +00:00
8cc1c3b634 Login: drop AuthProvider dependency so /login exports statically 2026-10-13 13:18:23 +00:00
22f9e12141 Login: bare sign-in page at /login; Telegram bridge connection guide 2026-10-13 09:49:57 +00:00
d313ce0bec Onboarding: wide hero banner (mark left, system name right) on black 2026-10-13 08:23:52 +00:00
27fd68c8df Landing: Enter -> login door, /login deep-link (signin mode), /signup register; onboarding scroll fix 2026-10-13 08:13:33 +00:00
f98fca45d0 oCIS upload: drain to buffer + explicit content-length (reva rejects chunked PUTs) 2026-10-13 05:47:19 +00:00
c918fb984e group-files test: expect the hub-groups root MKCOL before the per-group folder 2026-10-13 05:37:15 +00:00
dec505b83e oCIS: create the hub-groups root before the per-group folder (409 on missing parent) 2026-10-13 05:34:10 +00:00
cbbe713dea 032: accept bare room IDs (tuwunel 1.9.4 mints and serves them without :server) 2026-10-12 23:10:59 +00:00
fd09967851 Provisioner: retry onboarding posts (fresh-room forward-extremities race), degrade non-fatally 2026-10-12 22:57:00 +00:00
27a7b72984 Provisioner: normalize bare room IDs to full !id:server form (tuwunel returns localpart) 2026-10-12 22:47:48 +00:00
e3a6e9da97 Share with collective: fix union narrowing in open-room result handling 2026-10-12 22:35:32 +00:00
df54cb9b9e Provisioner: never list the room creator in power-level users (spec rejects it) 2026-10-12 21:46:55 +00:00
b984d6842f Files: mobile upload button, no-collective empty state; nav chat/approvals icons 2026-10-12 18:02:56 +00:00
3f61d11072 /signup: stable account-creation door (redirects to register-mode onboarding) 2026-10-12 16:32:05 +00:00
2a3a1d79a1 P1 flow fix: register makes a solo user by default, collective creation is an opt-in flag, post-login menu for collective-less accounts 2026-10-12 16:19:08 +00:00
2665f43cc4 Sidebar: full icon set (new chat + approvals marks), Files/Chat elevated, settings wheel 2026-10-12 16:12:10 +00:00
6161ac4740 Manual: full participation guide (media/accounts/decisions/safety) + Enter-to-door + provisioner env passthrough 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
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
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
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
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
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
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