Download
Desktop client v0.3.0

Your team's hub. On your own servers.

Chat, threads, video calls, shared documents and an AI assistant — one application your team signs into with your own identity provider, running on hardware you control. No seats to buy, no tenant somewhere else.

2.6 MB · one file, no installer. Other platforms

One morning in a channel

A bug reported, diagnosed, and written up — without leaving the app

Everything below is one continuous conversation: the report, the fix found in a thread, the assistant auditing the codebase with its own tools, the call that settled it, and the release note it produced.

# release-2-4 — Northwind Platform Demo

# release-2-4

Cutting 2.4.0 — staging sign-off before Thursday

9

Marta Kowalczyk Platform

Staging is on 2.4.0-rc3. Uploads over about 40 MB fail at 100% — the progress bar completes, then the document never appears in the folder.

2 2 people reacted: looking into it

Kenji Tanaka QA

Same here. Nothing in the backend log at all, which is the odd part.

Layla Haddad Support

Power BI — التقارير الكبيرة تفشل منذ أمس.

Translation: Power BI — large reports have been failing since yesterday.

Daniel Okoro Backend

Found it. client_max_body_size is still 32m on the staging proxy. nginx truncates the POST, DRF sees a short body and returns 400 before our view ever runs — so the failure is in the proxy log, not ours. Production is 512m.

Sofia Ferrara

Reproduced with a 47 MB PDF. Same 400, same silence in the app log.

Daniel Okoro

Patching staging now. It only ever affected staging.

Marta Kowalczyk

Let's pin the value in the deploy compose as well, so the two environments can't drift apart again.

Marta Kowalczyk Platform

@assistant where else is an upload limit configured?

Assistant AI

  1. search_messages client_max_body_size 4 matches across 2 channels
  2. read_document deploy/nginx.conf 2 server blocks
  3. read_document backend/settings.py 1 match

Three places set an upload ceiling. deploy/nginx.conf caps the request body — 32m in the staging block, 512m in production, which is the difference you hit. DATA_UPLOAD_MAX_MEMORY_SIZE in backend/settings.py only decides when Django spools to a temp file, so it will not reject a large upload. The SDK also refuses anything over 500 MB client-side before it starts. Only the nginx value differs between environments.

deploy/nginx.conf#infra · 4 Feb

Release 2.4 — upload limits

Started by Daniel · 6 min · Screen share · staging nginx config

Marta Kowalczyk Platform

Agreed on the call: one limit, one place, pinned in the deploy compose. Written up in the release note below.

Seen by all 9 members

is typing…

Playing 0 of 0

What the morning produced

The conversation ends as something people can read

Documents, pages and announcements are first-class objects in the same hub, not links to a different product. This is the release note that came out of the thread above, as a reader sees it.

Platform › Release notes Demo
v2.4.0 #release-2.4#infrastructure

Upload limits now live in one place

A 32 MB request-body cap left on the staging proxy was silently truncating large uploads. The value is now pinned in the deploy compose file, identical across environments.

What was happening

nginx cut the request body at 32 MB on staging. The browser finished sending, so the progress bar reached 100%, but Django received a short body and rejected it with a 400 before the upload view ran. Nothing reached the application log, which is why the failure looked like it came from nowhere.

What changed

The limit moved out of the per-host nginx config and into the deploy compose file, where both environments read the same value. Staging and production are now both 512m. The two other ceilings — Django spooling to disk, and the 500 MB client-side guard in the SDK — were already environment-independent and are unchanged.

What to do

Nothing, if you use the hosted upgrade path. If you run your own compose file, copy the CONTENTHUB_MAX_UPLOAD entry from the sample and rebuild the proxy container.

Calls & screen sharing

Video that never leaves your network

Calls run through a mediasoup SFU and a coturn TURN server that you deploy alongside everything else. Media is relayed by your own containers, so a call between two people in your office does not transit anyone else's cloud.

  • Group video and audio, with screen sharing per monitor or per window
  • A device check before you join, so the wrong microphone is caught early
  • Calls survive navigation — moving to another channel shrinks the call into a draggable mini-player instead of dropping it
  • Presence flips to in-call automatically, so nobody pings you mid-meeting
  • A short quality rating after each call, kept with the call record
Mixed scripts

A message written in Arabic stays written in Arabic

Teams do not all write in one script, and the awkward case is not a whole message in another language — it is a sentence that mixes them. Both panels below hold the identical string. Only the direction rule differs.

dir="auto" — the browser default

Power BI — التقارير الكبيرة تفشل منذ أمس.

Resolves from the first strong character — the P of “Power” — and lays the whole sentence out left-to-right. The Arabic clause ends up on the wrong side of the product name, and the full stop on the wrong end of the sentence.

Majority script — what ContentHub does

Power BI — التقارير الكبيرة تفشل منذ أمس.

Counts strong characters — 25 right-to-left against 7 left-to-right — and lays the line out the way it was written, reading from the right, with the Latin product name correctly embedded inside it.

Where the rule applies

Direction is decided per block, not per message, so a single reply can hold an English paragraph, an Arabic one and a table without any of them fighting each other.

  • Every paragraph, heading, list and table cell re-detects its own direction
  • Code and command output are pinned left-to-right, so a snippet never mirrors inside a right-to-left reply
  • Quoted replies and forwarded messages keep the direction of what they quote
  • Single newlines stay real line breaks, so a numbered list written in Arabic does not collapse into one run-on paragraph

To be clear about what this is not: the interface itself is English. This is about the text your team writes, which is where mixed scripts actually show up.

The rest of it

Everything a team hub needs, in one deployment

One application, one database, one login. Nothing below is a paid add-on or a third-party service you have to sign up for separately.

  • Search that finds the sentence

    A Meilisearch index over every message you have access to — typo-tolerant, ranked, and scoped to your own channels and conversations.

  • Notifications people keep switched on

    Web push in the browser and email digests for what you missed, both handled by a background worker. The desktop client draws its own notification cards and honours Windows Focus Assist.

  • Documents, pages and lists

    Folders and documents with revision history, editable pages, announcements, and structured lists you can build from a template — all in the same hub as the conversation about them.

  • An assistant that can actually look things up

    It calls tools mid-answer — searching messages, reading a document, fetching a page through your own self-hosted metasearch — and shows each step. Attachments are read too: PDF, Word and text for any model, images for the ones that can see.

  • Your identity provider, not ours

    Sign-in over OpenID Connect, so accounts and group membership stay in the directory you already run. Token auth is there for anything scripted.

  • Presence and read state that stay honest

    Typing indicators, online and away presence, and per-message read receipts arrive over the same WebSocket as the messages, backed by Redis.

Self-hosting

One compose file, and nothing that phones home

Every part of ContentHub runs on your own hardware — including the parts most self-hosted chat tools hand off to a third party, like call routing and search.

Your infrastructure

Clients

Desktop client

Electron

Browser

React + Vite

Edge

Reverse proxy

nginx · TLS

Application

Django + Channels

ASGI · HTTP + WebSocket

Background worker

Celery + beat

State & search

PostgreSQL

messages, docs, users

Redis

presence · sockets · queue

Meilisearch

full-text index

Media

mediasoup SFU

call routing

coturn

TURN / NAT traversal

Signs in against your OpenID Connect provider — accounts never leave your directory.

What you need

  • A host with Docker and Docker Compose
  • A TLS certificate — the browser will not grant camera or microphone access without one
  • A UDP port range open for call media, plus TURN on 3478 and 5349
  • An OpenID Connect provider if you want single sign-on; it ships wired to Authentik

No SaaS in the critical path

Search, presence, push notifications, call signalling and media relay are all services you deploy. Nothing phones home, and there is no vendor who can read your messages — because there is no vendor.

Download

Get the desktop client

A real application rather than a browser tab, and a small one: it renders through the WebView2 runtime Windows already ships with instead of carrying its own copy of Chromium, so the whole download is 2.6 MB. It runs as a single instance, keeps a tray icon, and needs no installer.

  • Windows

    Windows 10 & 11 · 64-bit

    One 2.6 MB .exe — no installer, no admin rights. Uses the WebView2 runtime already on Windows 10 and 11.

    Download
  • macOS

    Apple silicon & Intel

    Already builds in CI. Unsigned and un-notarised, so not published yet.

  • Linux

    AppImage · x86-64

    Already builds in CI, awaiting a tagged release.

  • Android

    Phone & tablet

    Planned. Web push and the responsive layout already work in a mobile browser.

  • iOS

    iPhone & iPad

    Planned. Installs as a home-screen web app in the meantime.

Version 0.3.0 — an early release, still on a 0.x version for a reason. It connects to a hub address set when the build is made; choosing your own hub from the app is on the way, along with unread badges and notification cards. Releases on GitHub.

Builds are not code-signed yet, so Windows will show a SmartScreen prompt the first time — choose More info, then Run anyway.

No client for your platform yet? The hub is a full web application — every feature on this page works in a browser, and installs to your home screen.

The download keeps one fixed filename, with its MD5 published beside it at /download/ContentHub-Native.exe.md5 — check what you downloaded, or poll it to spot a new build.