-
-
Notifications
You must be signed in to change notification settings - Fork 482
Server configuration examples
WBO reads server configuration from environment variables when the server starts. Restart the container after changing these values.
The complete source of truth is server/configuration.mjs. The examples below cover common deployment requests.
The examples use the official Docker image, lovasoa/wbo:latest, expose WBO on host port 5001, and persist board data in ./wbo-boards on the host.
Set WBO_DEFAULT_BOARD to a board name:
mkdir -p wbo-boards
chown -R 1000:1000 wbo-boards
docker run \
--publish 5001:80 \
--volume "$(pwd)/wbo-boards:/opt/app/server-data" \
--env WBO_DEFAULT_BOARD=anonymous \
lovasoa/wbo:latestWhen this variable is set, requests to / redirect to /boards/<board>. The board name is normalized the same way user-entered board names are normalized, so uppercase letters become lowercase and unsupported characters become dashes.
Set WBO_HTML_HEAD_SNIPPET_PATH to a file containing the exact HTML you want WBO to inject before </head>:
mkdir -p wbo-boards wbo-config
chown -R 1000:1000 wbo-boards
docker run \
--publish 5001:80 \
--volume "$(pwd)/wbo-boards:/opt/app/server-data" \
--volume "$(pwd)/wbo-config/head-snippet.html:/opt/app/config/head-snippet.html:ro" \
--env WBO_HTML_HEAD_SNIPPET_PATH=/opt/app/config/head-snippet.html \
lovasoa/wbo:latestThe snippet is inserted into rendered HTML pages, including the landing page and board pages. The file is read once at startup. WBO_HTML_HEAD_SNIPPET_PATH must point to the path inside the container, not the host path.
Only use a file controlled by the server administrator. The snippet is inserted as raw HTML, so it can run scripts or change page behavior.
Create ./wbo-config/head-snippet.html on the Docker host with your own measurement ID:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag("js", new Date());
gtag("config", "G-XXXXXXXXXX");
</script>Then start WBO with:
mkdir -p wbo-boards
chown -R 1000:1000 wbo-boards
docker run \
--publish 5001:80 \
--volume "$(pwd)/wbo-boards:/opt/app/server-data" \
--volume "$(pwd)/wbo-config/head-snippet.html:/opt/app/config/head-snippet.html:ro" \
--env WBO_HTML_HEAD_SNIPPET_PATH=/opt/app/config/head-snippet.html \
lovasoa/wbo:latestSelf-hosted deployments should make sure this is acceptable for their privacy policy and local legal requirements.
The same head snippet can add links or metadata:
<link rel="me" href="https://example.com/legal-notice">This is intended for deployment-owned additions. It is not a theme engine and it does not replace WBO templates.
If you also want to serve a custom stylesheet from WBO itself, mount it into the container's web root and link to it from the snippet:
<link rel="stylesheet" href="/custom-wbo.css">mkdir -p wbo-boards wbo-config
chown -R 1000:1000 wbo-boards
docker run \
--publish 5001:80 \
--volume "$(pwd)/wbo-boards:/opt/app/server-data" \
--volume "$(pwd)/wbo-config/head-snippet.html:/opt/app/config/head-snippet.html:ro" \
--volume "$(pwd)/wbo-config/custom-wbo.css:/opt/app/client-data/custom-wbo.css:ro" \
--env WBO_HTML_HEAD_SNIPPET_PATH=/opt/app/config/head-snippet.html \
lovasoa/wbo:latestSet AUTH_SECRET_KEY to require JWTs for opening boards:
mkdir -p wbo-boards
chown -R 1000:1000 wbo-boards
docker run \
--publish 5001:80 \
--volume "$(pwd)/wbo-boards:/opt/app/server-data" \
--env AUTH_SECRET_KEY="replace-with-a-long-random-wbo-signing-secret" \
lovasoa/wbo:latestClients pass a token with ?token=.... Token roles can grant:
-
reader: can open the board but cannot edit. -
editor: can open and edit. -
moderator: can open, edit, and clear.
Roles can be scoped to a board name, for example reader:class-a or moderator:class-a.
WBO verifies these tokens with the shared AUTH_SECRET_KEY and expects a roles claim that is an array of strings.
A common Keycloak setup is:
- Create a Keycloak realm, for example
school. - Create an application client, for example
course-app, using the normal Authorization Code + PKCE flow for your web application. - Create client or realm roles such as
wbo-reader,wbo-editor, andwbo-moderator. - Assign those roles to users or groups in Keycloak.
- In your application backend, validate the Keycloak login/session, map the user's Keycloak role to a WBO role, and sign a short-lived WBO token with the same secret configured as WBO's
AUTH_SECRET_KEY.
Do not put AUTH_SECRET_KEY in browser code. The browser should only receive the short-lived WBO token created by your backend.
Keycloak usually signs its own access tokens with realm keys such as RS256. WBO's current AUTH_SECRET_KEY flow verifies a shared-secret token, so the most portable integration is to validate Keycloak in your app and then mint a WBO-specific token.
Very short backend example:
import jwt from "jsonwebtoken";
app.get("/wbo-token", requireKeycloakUser, (req, res) => {
const board = String(req.query.board || "class-a");
const keycloakRoles = req.user.roles || [];
const role = keycloakRoles.includes("wbo-moderator")
? "moderator"
: keycloakRoles.includes("wbo-editor")
? "editor"
: "reader";
const token = jwt.sign(
{ roles: [`${role}:${board}`] },
process.env.WBO_AUTH_SECRET,
{ expiresIn: "15m" },
);
res.json({ token });
});process.env.WBO_AUTH_SECRET in this embedding application must match the AUTH_SECRET_KEY passed to the WBO container.
Very short iframe example:
<iframe id="wbo" title="Whiteboard" style="width: 100%; height: 600px; border: 0"></iframe>
<script type="module">
const board = "class-a";
const response = await fetch(`/wbo-token?board=${encodeURIComponent(board)}`);
const { token } = await response.json();
const iframe = document.getElementById("wbo");
iframe.src = `https://wbo.example.com/boards/${encodeURIComponent(board)}?token=${encodeURIComponent(token)}`;
</script>See the repository README for the current JWT payload rules.