# WBO
WBO is an online collaborative whiteboard that allows many users to draw simultaneously on a large virtual board.
The board is updated in real time for all connected users, and its state is always persisted. It can be used for many different purposes, including art, entertainment, design, teaching.
A demonstration server is available at [wbo.ophir.dev](https://wbo.ophir.dev)
## Screenshots
## Running your own instance of WBO
If you have your own web server, and want to run a private instance of WBO on it, you can. It should be very easy to get it running on your own server.
### Running the code in a container (safer)
If you use the [docker](https://www.docker.com/) containerization service, you can easily run WBO as a container.
An official docker image for WBO is hosted on dockerhub as [`lovasoa/wbo`](https://hub.docker.com/r/lovasoa/wbo): [](https://hub.docker.com/repository/docker/lovasoa/wbo).
You can run the following bash command to launch WBO on port 5001, while persisting the boards outside of docker:
```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
```
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`.
### Running the code without a container
Alternatively, you can run the code with [node.js](https://nodejs.org/) directly, without docker.
First, download the sources:
```
git clone https://github.com/lovasoa/whitebophir.git
cd whitebophir
```
Then [install node.js](https://nodejs.org/en/download/) (v22 or superior)
if you don't have it already, then install WBO's dependencies:
```
npm install --production
```
Finally, you can start the server:
```
PORT=5001 npm start
```
This will run WBO directly on your machine, on port 5001, without any isolation from the other services. You can also use an invokation like
```
PORT=5001 HOST=127.0.0.1 npm start
```
to make whitebophir only listen on the loopback device. This is useful if you want to put whitebophir behind a reverse proxy.
### 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](https://github.com/lovasoa/whitebophir/wiki/Setup-behind-Reverse-Proxies).
## Translations
WBO is available in multiple languages. The translations are stored in [`server/http/translations.json`](./server/http/translations.json).
If you feel like contributing to this collaborative project, you can [translate WBO into your own language](https://github.com/lovasoa/whitebophir/wiki/How-to-translate-WBO-into-your-own-language).
## Authentication
WBO supports authentication using [Json Web Tokens](https://jwt.io/introduction). Pass the token as a `token` query parameter, for example `http://myboard.com/boards/test?token={token}`.
The `AUTH_SECRET_KEY` variable in [`configuration.mjs`](./server/configuration.mjs) should be filled with the secret key for the JWT.
### Board Capabilities
WBO evaluates board access as three capabilities:
- `canOpen`: the user may load or connect to the board.
- `canEdit`: the user may send normal board changes.
- `canClear`: the user may use the Clear tool, which wipes all content from the board.
JWT role strings can define these capabilities. They are declared in the JWT payload:
```json
{
"iat": 1516239022,
"exp": 1516298489,
"roles": ["editor"]
}
```
### Board Visibility / Access
If `AUTH_SECRET_KEY` is not set, any valid board URL has `canOpen`.
If `AUTH_SECRET_KEY` is set, `canOpen` requires a valid token. You can restrict which board names a token may open by adding `:` to a claim:
```json
{
"roles": ["editor:board-a", "moderator:board-b", "reader:board-c"]
}
```
- `reader:` grants `canOpen` for that board.
- `editor:` grants `canOpen` for that board and is edit-capable on read-only boards.
- `moderator:` grants `canOpen` for that board, is edit-capable on read-only boards, and grants `canClear`.
For example, `http://myboard.com/boards/mySecretBoardName?token={token}` with:
```json
{
"iat": 1516239022,
"exp": 1516298489,
"roles": ["moderator:mySecretBoardName"]
}
```
If a token contains any board-scoped claims, it can only open the boards named in those claims.
Phase 1 of the capability refactor does not add new permission types, board owners, administrators, sharing controls, or permission management UI. Existing JWT claim syntax remains unchanged.
### Board Editability / Read-Only
Board visibility and board editability are separate.
- On a read-only board, only `editor` and `moderator` claims still grant `canEdit`.
- On instances without JWT authentication, a read-only board does not grant `canEdit` because there is no authenticated edit-capable claim.
- Without JWT authentication, `canClear` is never granted.
- With JWT authentication, only `moderator` claims grant `canClear`.
Read-only state is stored on the persisted board SVG root as `data-wbo-readonly`:
```xml