Documents, spreadsheets, slides, and whiteboards — CRDT-native and real-time, shipped as a single binary, running on your own storage. No cloud account, no telemetry, no lock-in.
Quickstart · Docs · Collaboration · Architecture · Self-hosting
Diwan is a standalone, self-hostable office suite — documents, spreadsheets, slides, and whiteboards in one clean, modern web app, shipped as a single Go binary with the entire frontend embedded. There is no cloud account to create, nothing phones home, and your files live in your own storage.
Real-time co-editing is peer-to-peer and CRDT-native: there is no central document server. Peers sync directly over an end-to-end-encrypted channel, and because edits are conflict-free (CRDTs), everyone converges with no authority in the middle. What you run is what you own.
It carries the torch lit by LibreOffice and OpenOffice — the projects that proved productivity software can be free, open, and community-driven — into the browser, on a fast React frontend and a lightweight Go backend.
%%{init: {'theme':'base','themeVariables':{'fontFamily':'ui-monospace, SFMono-Regular, Menlo, monospace','primaryColor':'transparent','primaryBorderColor':'#14b8a6','primaryTextColor':'#8f969e','lineColor':'#8a8f98','nodeBorder':'#5f8f8a','edgeLabelBackground':'transparent','clusterBorder':'#3f8f86','clusterBkg':'transparent'}}}%%
flowchart LR
you["you (browser)"] -->|edits| diwan["Diwan<br/>single binary"]
peer["a collaborator"] -.->|"E2E-encrypted P2P (WebRTC)"| you
diwan --> store["your storage<br/>files · SQLite · Postgres · S3"]
diwan --> surfaces["Docs · Sheets · Slides<br/>Whiteboards · PDF signing"]
A diwan was a register of records, and a collected volume of work. This one is yours.
The mark is an iwan — the vaulted portal at the heart of Persian and Ottoman architecture, the arch you pass through to reach the room where the registers were kept. Give it a quarter turn clockwise and it becomes a D: the arch's two legs are the bars, the teal threshold is the stem.
| 📝 Documents | Rich-text editing (TipTap) — headings, tables, inline images, footnotes, task lists, links; anchored comments, suggestions (track-changes with accept/reject), version history with restore, find/replace, live outline + word count. |
| 📊 Spreadsheets | A full grid (Fortune Sheet) — formulas, number formats, conditional formatting, data validation, charts, filters, pivot tables, named ranges, freeze panes. |
| 🖼️ Slides | A from-scratch positioned-object canvas — free drag/resize/rotate of text, shapes, and images; per-element animations, themes, editable master slides, per-slide transitions, and a presenter view (notes + timer). |
| 🎨 Whiteboards | An infinite hand-drawn canvas built on the MIT Excalidraw editor — shapes, arrows, freehand, text, and images. The scene is a Yjs CRDT synced over the same E2E-encrypted P2P engine as Docs. |
| ⚡ Real-time co-editing | Always peer-to-peer, no central document server. Edits sync as CRDT updates inside an end-to-end-encrypted room; peers connect directly over WebRTC with a content-blind relay only as a hard-NAT fallback. Share by invite link — the key rides the URL fragment and never reaches any server. |
| 📄 PDF import / export & signing | Import and export .docx · .xlsx · .csv · .pptx · Markdown · HTML · PDF. View, annotate, fill AcroForm fields, and sign PDFs, including multi-party signing envelopes with a cryptographic audit trail. |
| 💾 Your own storage | Local files + SQLite by default; optional PostgreSQL (schema office) for multi-user; optional S3-compatible object store. Nothing is hosted for you unless you choose it. |
| 🔓 No lock-in | Open formats in and out, MIT-licensed, single self-contained binary. Every editor is also published as an npm library (the diwan package — diwan/docs, diwan/sheets, diwan/slides, diwan/whiteboard, diwan/pdf) so you can embed any surface in your own app. |
Diwan is documents-only by design: Docs, Sheets, Slides, Whiteboards, and PDF signing. Chat, video, and mail are deliberately not part of it.
Diwan wears a warm, light-first "Workshop" identity — an ember/terracotta accent, a paper-tinted canvas, Fraunces on the page and Hanken Grotesk in the chrome — with a full dark theme (and System) one click away.
Light & dark — every surface ships both, light-first:
![]() |
![]() |
Regenerate anytime with
npm run screenshots— it boots the app with seeded demo data (no real backend or credentials needed) and captures every surface in light and dark at retina. See docs/SCREENSHOTS.md.
Diwan runs by itself — no account, no cloud, no external service required.
docker run -d \
--name diwan \
-p 8080:8080 \
-v diwan-data:/srv/data \
ghcr.io/vul-os/diwan:latestOpen http://localhost:8080.
Prerequisites: Go 1.25+ and Node.js 18+ with npm.
git clone https://github.com/vul-os/diwan.git
cd diwan
# Install deps and build the frontend + single binary
npm install
npm run build
# Run — single-user, local storage, no auth, no cloud
./diwanOpen http://localhost:8080. Data lives in ./data and ./uploads — that's the
whole app, in one file. The Go module and the binary it builds are named
diwan; the npm package and the product are Diwan.
To require login (still fully standalone):
# config.yaml → auth.enabled: true
export DIWAN_JWT_SECRET="$(openssl rand -hex 32)"
./diwannpm run dev:web # Vite dev server (:5173) + Go API (:8080), live reloadOpen http://localhost:5173.
Diwan also installs as a one-click app on a Vulos OS box (DEPLOY_MODE=os),
where it runs behind the box gateway with scoped storage and single sign-on. It
is the same binary — the app store just wires identity and storage for you.
Self-hosting it yourself is always the default path, and never second-class.
%%{init: {'theme':'base','themeVariables':{'fontFamily':'ui-monospace, SFMono-Regular, Menlo, monospace','primaryColor':'transparent','primaryBorderColor':'#14b8a6','primaryTextColor':'#8f969e','lineColor':'#8a8f98','nodeBorder':'#5f8f8a','edgeLabelBackground':'transparent','clusterBorder':'#3f8f86','clusterBkg':'transparent'}}}%%
flowchart TD
FE["React + Vite + Tailwind frontend<br/>Docs · Sheets · Slides · Whiteboards · PDF signing"]
BE["Go backend (Gin)<br/>handlers · userauth · signing · storage · obs"]
Seam["backend/seam — Identity · Entitlements · Usage<br/>standalone defaults (local, unlimited, no-op)"]
Adapter["backend/integration<br/>(optional identity/entitlements adapter, opt-in)"]
FE -->|"embedded into the binary"| BE
BE --> Seam
Seam -->|optional| Adapter
Diwan is a single Go binary with the whole frontend embedded — one file to
deploy. With zero configuration it runs as a single-user, local-storage app on
your own machine. Everything that could tie it to an external service lives
behind a small set of Go interfaces in backend/seam; the standalone build
wires local, unlimited, no-op defaults and never imports the optional adapter.
Real-time collaboration is CRDT-based and always peer-to-peer: Docs and
whiteboards sync as Yjs updates, Sheets and Slides use LWW/tree CRDTs, all inside
an end-to-end-encrypted room whose key never reaches any server. The only server
role is content-blind peer discovery — it learns that peers share a random
room id, never any content — and that role can be filled either by a Vulos OS /
Ephor host in front of Diwan, or by pointing this deployment's own
config.yaml (collab.rendezvous_url / VULOS_RENDEZVOUS_URL) at any
self-hosted vulos-relayd, so a bare standalone binary gets real P2P collab
with no Vulos OS or account at all. In that mode the browser talks to the relayd
directly — Diwan's own server is not in the discovery path and sees nothing
of it. See
docs/COLLABORATION.md §3 and
docs/ARCHITECTURE.md.
Full documentation lives in docs/.
| Document | Description |
|---|---|
| Getting started | Run it locally, in Docker, or in production |
| Self-hosting | Run fully standalone; the optional identity/entitlements seam |
| Collaboration | How real-time P2P CRDT editing works, end to end |
| Architecture | Component map and design decisions |
| Configuration | Env vars, config.yaml, storage, OTEL reference |
| API | REST API — files, versions, collab, signing |
| Admin guide | Deploy, back up, and operate the server |
| Deploy | Docker, single-box co-location, static CDN |
| Testing | Vitest unit + RTL/MSW integration, Playwright E2E |
| Roadmap · Changelog | Plans and version history |
npm run dev:web # Vite (:5173) + Go API (:8080)
npm test # unit + RTL/MSW integration tests (Vitest)
npm run test:e2e # browser E2E (Playwright, backend mocked)
npm run test:e2e:p2p # REAL P2P: a live vulos-relayd + two standalone servers
npm run build # frontend dist/ + Go binary
npm run screenshots # regenerate the docs/screenshots gallery
go build ./... && go test ./... && go vet ./...Frozen invariants: pure Go (no CGO), JSX only (never
.tsx), no third-party SSO or payment lock-in in the standalone build. See CONTRIBUTING.md.
Diwan centralises HTML sanitisation in one audited DOMPurify policy, treats CRDT ingress as fail-closed, gates read-only collaborators cryptographically, and enforces per-file ACLs when multi-user auth is on. Found a vulnerability? Please report it privately — see SECURITY.md, THREAT-MODEL.md, and SECURITY-TESTING.md. Do not open public issues for security reports.
Pull requests are welcome — bug fixes, signing robustness, accessibility, tests, and docs especially. For major changes, open an issue first. See CONTRIBUTING.md for setup, code style, and the frozen invariants. No CLA required.
MIT OR Apache-2.0 — © VulOS. Diwan is a VulOS project; source and issues at github.com/vul-os/diwan.
![]()
vulos — open by design







