Profile
Back to NewsBack
GitHub Trending 6 min
Reader Mode
lovasoa/whitebophir: Online collaborative Whiteboard that is simple, free, easy to use and  to deploy

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

8 hours ago

WBO

WBO is an online collaborative whiteboard that allows many users to draw simultaneously on a large virtual board. Connected users see edits in real time; the server saves boards as SVG files. Use it for drawing, design, teaching, or collaborative diagrams.

Try wbo.ophir.dev. For contributors, see the short architecture guide and engineering policies.

Screenshots

The anonymous board collaborative diagram editing Screenshot of WBO's user interface: architecture
teaching math on WBO wbo teaching drawing art kawai cats on WBO

Running your own instance of WBO

Docker

The Docker image is available as lovasoa/wbo.

You can run the following bash command to launch WBO on port 5001, while persisting the boards outside of docker:

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

You can then access WBO at http://localhost:5001.

The official Docker image does not force an IP source. By default the application uses WBO_IP_SOURCE=remoteAddress. If you run the container behind a trusted proxy or CDN, set -e WBO_IP_SOURCE=... explicitly, for example X-Forwarded-For, Forwarded, or CF-Connecting-IP.

Node.js

Install Node.js 22 or later, then:

git clone https://github.com/lovasoa/whitebophir.git
cd whitebophir
npm ci --omit=dev
PORT=5001 npm start

Set HOST=127.0.0.1 to listen only on loopback when using a reverse proxy. For development, use npm ci to include test and lint dependencies.

Running WBO on a subfolder

By default, WBO launches its own web server and serves all of its content at the root of the server (on /). If you want to make the server accessible with a different path like https://your.domain.com/wbo/ you have to setup a reverse proxy. Set WBO_BASE_PATH=/wbo so generated links, redirects, and canonical URLs point at the external subfolder. See instructions on our Wiki about how to setup a reverse proxy for WBO.

Translations

WBO is available in multiple languages. The translations are stored in server/http/translations.json. If you feel like contributing to this collaborative project, you can translate WBO into your own language.

Authentication

Set AUTH_SECRET_KEY to require a signed JSON Web Token. Pass it in the token query parameter, such as /boards/team?token=.... With JWT authentication disabled, anyone with a valid board URL can open it and edit writable boards.

Tokens may contain global roles or board-scoped roles:

{
  "roles": ["editor:team", "moderator:planning", "reader:archive"]
}

When any role names a board, the token can open only the named boards. Use standard JWT expiry claims to limit token lifetime.

| Access | Writable board | Read-only board | Clear and moderation | | --- | --- | --- | --- | | Public visitor, JWT disabled | Edit | View | No | | Board-scoped reader | Edit | View | No | | Editor | Edit | Edit | No | | Moderator | Edit | Edit | Yes |

A reader role only enforces viewing on a board that is itself read-only. Permission resolution is defined in board_capabilities.mjs.

Moderators

WBO_BOARD_MODERATORS grants moderator access through the user's wbo-user-secret-v1 cookie, including on instances without JWT authentication. Configure space-separated groups in the form team:, using the actual cookie values. Treat these values as credentials.

Permanent moderators can grant temporary moderator access from the users menu. Temporary grants and bans are local to the board and process and disappear on restart. Temporary moderators cannot delegate their role. Ordinary user reports disconnect the reporter and target without creating a ban; moderator actions can warn, ban, and revoke bans.

Read-only boards

Stop the server before editing stored board files so an in-memory save cannot overwrite the change. Back up board-.svg in WBO_HISTORY_DIR, set data-wbo-readonly="true" on its root , then restart the server. Set it to false to make the board writable again. Editors and moderators can still edit read-only boards. Legacy JSON boards are migrated to SVG when loaded.

Configuration

When you start a WBO server, it loads its configuration from several environment variables. You can see a list of these variables in configuration.mjs. Common settings:

  • WBO_HISTORY_DIR : configures the directory where the boards are saved. Defaults to ./server-data/.
  • WBO_BASE_PATH : optional external URL path prefix, such as /wbo, for deployments mounted under a reverse-proxy subfolder.
  • WBO_HTML_HEAD_SNIPPET_PATH : optional path to an HTML snippet inserted raw before on rendered HTML pages. This is useful for adding user analytics scripts or similar trusted snippets. The file is read once at server startup; relative paths resolve from the server working directory.
  • WBO_MAX_EMIT_COUNT : the general per-IP socket write limit profile. Use compact entries such as :250/5s anonymous:125/5s. Increase this if you want smoother drawings, at the expense of making denial-of-service bursts cheaper for clients. The default is :250/5s.
  • WBO_MAX_CONSTRUCTIVE_ACTIONS_PER_IP : the constructive per-IP write limit profile. Use compact entries such as *:40/10s anonymous:20/10s.
  • WBO_MAX_DESTRUCTIVE_ACTIONS_PER_IP : the destructive per-IP write limit profile. Use compact entries such as *:190/60s anonymous:95/60s.
  • WBO_IP_SOURCE : which request attribute to trust for client IP based limits and logs. Supports remoteAddress, X-Forwarded-For, Forwarded, or a custom header such as CF-Connecting-IP. The default is remoteAddress.
  • AUTH_SECRET_KEY : If you would like to authenticate your boards using jwt, this declares the secret key.

Monitoring

Server logs go to stdout/stderr. Set LOG_LEVEL to debug, info, warn, or error. Metrics, logs, and traces support OpenTelemetry OTLP export:

OTEL_SERVICE_NAME=whitebophir-server \
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 \
npm start

Standard OTEL_* settings control resource attributes, per-signal endpoints, headers, and trace sampling. Without an exporter endpoint, WBO does not attempt remote export. Traces default to 5% sampling; for a debugging session, set OTEL_TRACES_SAMPLER=parentbased_traceidratio and OTEL_TRACES_SAMPLER_ARG=1.0.

Storage and recovery

Back up WBO_HISTORY_DIR. Accepted edits are saved asynchronously, so a crash can lose changes since the last completed save. Run one server process per history directory; independent writers are not supported.

Unreadable SVG files are quarantined before recovery is attempted. The .svg.bak file is temporary save staging, not a durable backup. See the architecture guide for persistence and replay boundaries.

SVG preview

Open /preview/ to download a standalone SVG preview, for example the anonymous board preview.

Chat with me