# WBO (whitebophir): a self-hosted collaborative whiteboard you can run in one Docker command

> WBO is an AGPL-3.0 collaborative whiteboard written in JavaScript. It persists board state, speaks JWT for access control, and installs either from an official Docker image or from source with Node 22 or newer.

**lovasoa/whitebophir** — Online collaborative Whiteboard that is simple, free, easy to use and  to deploy

- Repository: https://github.com/lovasoa/whitebophir
- Website: https://wbo.ophir.dev
- Stars: 2,661 · Forks: 486
- Language: JavaScript
- License: AGPL-3.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/lovasoa-whitebophir

## What WBO is for, and who ends up running it

WBO is an online collaborative whiteboard where many users draw on the same large virtual board at once. The README lists art, entertainment, design and teaching as intended uses, and the screenshots show the same surface used for a math lesson, an architecture diagram and freehand drawing. The interesting part is not the drawing canvas; it is the deployment story. The project ships a Docker image on Docker Hub as lovasoa/wbo and a Node entry point, so the audience is anyone who wants the board on infrastructure they control rather than on a vendor's domain.

That audience is narrower than "everyone who wants a whiteboard". If you are happy with a hosted board and a link, the demonstration server at wbo.ophir.dev already covers it. WBO matters when the board URL itself is the secret, when board files must stay on your disk, or when you need to put the board behind your own reverse proxy at a path like /wbo.

## How board state, sockets and JWT capabilities fit together

The runtime is a Node server (server/server.mjs, started by the start script) that serves static assets with serve-static and keeps clients in sync over socket.io. Updates are broadcast to every connected user, and the README states the board state is always persisted. Persistence lands in a directory the container expects at /opt/app/server-data, which is why the documented Docker command mounts a host folder there.

Access control is layered on top of that. WBO evaluates board access as three capabilities: canOpen to load or connect, canEdit to send normal board changes, and canClear to use the Clear tool. With AUTH_SECRET_KEY unset, any valid board URL has canOpen. With it set, canOpen requires a valid token, and a token carrying any board-scoped claim can only open the boards named in those claims. A claim like moderator:mySecretBoardName grants canOpen for that board, makes the token edit-capable on read-only boards, and grants canClear. Read-only state is not a separate database row: it is stored on the persisted SVG root as data-wbo-readonly, and legacy .json board files using __wbo_meta__.readonly are migrated into that SVG form when loaded.

## Installing WBO with Docker and opening your first board

The README's container path is three commands. The mkdir creates a host directory for board files, chown makes it writable by the UID the image runs as, and docker run publishes container port 80 on host port 5001 while mounting the directory at the path the server writes to.

```bash
mkdir wbo-boards # Create a directory that will contain your whiteboards
chown -R 1000:1000 wbo-boards # Make this directory accessible to WBO
docker run -it --publish 5001:80 --volume "$(pwd)/wbo-boards:/opt/app/server-data" lovasoa/wbo:latest # run wbo
```

After the container starts, the README says you can access WBO at http://localhost:5001. Board content should appear as files inside wbo-boards on the host, which is the check that the volume mount took effect.

## Running from source, binding to loopback, and serving under a subfolder

Without a container the sequence is clone, install dependencies, start. The production install skips dev dependencies, and the start script accepts PORT and HOST from the environment.

```bash
git clone https://github.com/lovasoa/whitebophir.git
cd whitebophir
npm install --production
PORT=5001 npm start
```

Setting HOST=127.0.0.1, as in the README's PORT=5001 HOST=127.0.0.1 npm start example, makes the server listen only on the loopback device, which the README describes as the way to put whitebophir behind a reverse proxy. Node 22 or newer is required by the engines field in package.json.

If the board must live at a path rather than the root, the README points to the wiki page on reverse proxies and names the variable that fixes generated links: set WBO_BASE_PATH=/wbo so redirects and canonical URLs point at the external subfolder. This is a proxy-side change, not a server-side rewrite; WBO still serves its content at / internally.

## The permission model is a phase 1 refactor, and it shows

The README is unusually direct here: phase 1 of the capability refactor does not add new permission types, board owners, administrators, sharing controls, or permission management UI. Everything is expressed as JWT claims, and the claim syntax is unchanged. There is no user table, no invitation flow and no screen where an owner grants access. If your requirement is "each teacher owns their boards and can share them with a class", WBO does not model that; you either issue tokens yourself or you fall back to URL secrecy.

The read-only path has a sharp edge worth reading twice. On a read-only board, only editor and moderator claims grant canEdit. On an instance without JWT authentication, a read-only board grants no canEdit at all, because there is no authenticated edit-capable claim, and canClear is never granted. So read-only plus no auth means nobody can edit, including you. That is coherent, but it surprises people who expect a read-only toggle to be a soft lock.

There is also an operational detail the README calls out: the official Docker image does not force an IP source and defaults to WBO_IP_SOURCE=remoteAddress. Behind a trusted proxy or CDN you must set it explicitly (for example X-Forwarded-For, Forwarded or CF-Connecting-IP) or every client looks like the proxy.

## Where WBO is the wrong tool, and what to compare it against

WBO stores boards as files and treats the URL as the primary access boundary. That is a poor fit for organisations that need audit trails, per-user accounts, or a permission screen an administrator can click through. It is also a poor fit if you want the whiteboard embedded inside a video call product, or if you need a managed SLA; self-hosting is the whole point, and the README's own quickest path is a demonstration server, not a hosted plan.

A real alternative in the same category is Excalidraw, which is also a browser whiteboard with a permissive open source core and a hosted service. The difference in approach is the persistence and access model. Excalidraw centres on a scene you export or share as a link to a room, with the drawing document as the unit of work. WBO centres on a named board that lives at a server path, is updated live for all connected users, and is persisted on the server, with JWT claims deciding who may open, edit or clear it. If you want a diagram file you own and move between tools, the first model fits better. If you want a long-lived room on your own domain where the board is the address, WBO fits better.

## Licence, upgrade cost and what the repository asks of you

WBO is licensed AGPL-3.0-or-later, stated in both the repository licence field and package.json. The practical consequence for a self-hosted deployment is the network copyleft: if you modify WBO and let users interact with it over a network, the AGPL's source-availability obligation is the thing your legal team will want to read, and that is a question for them, not for this article. Running the unmodified official image is the simplest position.

Upgrade cost is low but not zero. Dependencies include socket.io 4, handlebars, jsonwebtoken and a set of OpenTelemetry packages for logs, metrics and traces, so the image and the npm dependency tree move together. The Dockerfile pins node:24-alpine while package.json requires Node 22 or newer, which means a source deployment on an older runtime is unsupported. The server-data volume is the state you must back up before any upgrade; board files live there, and the read-only flag now lives inside the SVG root, so a restore from an old backup will be migrated on load rather than rejected. The README does not document a rollback procedure, so keep a copy of the volume before you pull a new tag.

## Conclusion

Adopt WBO if you want a whiteboard you control, on your own domain, with board state persisted to disk and optional JWT-gated access. Do not adopt it if you need accounts, an admin UI, per-board ownership or a managed service; the README states that phase 1 of the capability refactor adds no board owners, administrators, sharing controls or permission management UI. Before rolling it out, verify the AUTH_SECRET_KEY behaviour on your instance, confirm the WBO_IP_SOURCE value matches your proxy or CDN, and check that the volume you mount at /opt/app/server-data is writable by UID 1000.

## FAQ

### Is WBO free?

Yes. WBO is licensed AGPL-3.0-or-later and the README describes it as simple, free and easy to deploy. You can run the official lovasoa/wbo Docker image or the source on your own server.

### How can I create a whiteboard with WBO?

Start the server (the README shows a Docker command publishing port 5001, or PORT=5001 npm start from source) and then open a board URL such as the anonymous board on the demonstration server. Board state is persisted to the server-data directory.

### Does WBO have an equivalent to Google's whiteboard?

WBO is a collaborative whiteboard where many users draw simultaneously and the board is updated in real time for everyone connected, which is the same category of tool. The difference is deployment: WBO is designed to be self-hosted, and its access control is JWT-based rather than tied to a Google account.

### What is the best whiteboard software?

The README does not rank WBO against other products or make a best-in-class claim. It describes WBO as an online collaborative whiteboard that is simple, free, easy to use and easy to deploy, and points to a demonstration server at wbo.ophir.dev so you can judge the drawing surface yourself.

## Sources

- [Issues](https://github.com/lovasoa/whitebophir/issues)
- [License: AGPL-3.0](https://github.com/lovasoa/whitebophir/blob/master/LICENSE)
- [lovasoa/whitebophir on GitHub](https://github.com/lovasoa/whitebophir)
- [Project website](https://wbo.ophir.dev)
- [README](https://github.com/lovasoa/whitebophir/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lovasoa-whitebophir
