wb publish
wb publish renders a .org workbook to a self-contained HTML page and ships it
to a hosting provider. It is deliberately lightweight — no server required, no
BEAM app started. The OQL kernel is embedded in the wb binary; rendering
happens in-process.
The full flow is three commands:
wb publish init # scaffold publish.org
wb publish validate # coherence-check, no network
wb publish apply my.org # render + ship → prints live URL
publish.org
publish.org is the declarative config for a published workbook. It uses the
same :PROPERTIES: drawer pattern as deployment config:
* publish :publish:
:PROPERTIES:
:PUBLISH_TARGET: cloudflare-pages
:PUBLISH_PROJECT: my-pages-project
:PUBLISH_DOMAIN: my-project.pages.dev
:PUBLISH_TITLE: My Workbook
:END:
Secrets (API keys, account IDs) do not go in publish.org. Use env vars. See
publish.org Properties for the full property reference.
wb publish init
Writes a publish.org template in the current directory:
wb publish init # writes ./publish.org
wb publish init --force # overwrite existing
The scaffolded file includes comments explaining each property and a note that secrets should come from env vars.
wb publish validate
Parses publish.org and checks coherence without rendering or deploying:
wb publish validate # uses ./publish.org
wb publish validate my.pub.org # explicit config file
Checks:
PUBLISH_TARGETis a recognised providerRequired properties for the target are present
Property values don't look like secrets (warning if they do)
Exits 0 on success, 1 on errors. Output is human-readable; add --json for
machine-readable output.
wb publish apply
Renders the workbook and ships it:
wb publish apply # uses ./workbook.org + ./publish.org
wb publish apply my.org # explicit workbook
wb publish apply my.org my.pub.org # explicit workbook + config
wb publish apply --json # machine-readable output
The render path is: OQL kernel reads the .org source → produces HTML →
PublicWeb.static_page/2 wraps it in the page shell (Geist font, dark/light CSS,
full doc typography) → HTML written to PUBLISH_OUTPUT (default:
.publish_out/index.html) → provider receives it.
The HTML artifact is self-contained: all CSS is inlined, no JS runtime, no call-home. Fonts are loaded from Google Fonts CDN (one network request on first view).
wb publish site
wb publish site <dir> builds a multi-page site from a directory containing a
site.org manifest:
wb publish site web/docs # renders all pages → dist/, then deploys
site.org format:
#+TITLE: My Documentation
#+PUBLISH_TARGET: cloudflare-pages
#+PUBLISH_PROJECT: my-docs-project
#+PUBLISH_DOMAIN: docs.example.com
* Introduction
- [[index.org][Overview]]
* Getting Started
- [[getting-started/installation.org][Installation]]
- [[getting-started/quick-start.org][Quick Start]]
Each [[file.org][Title]] item is rendered with a shared sidebar. The output is a
flat dist/ tree: dist/index.html,
dist/getting-started/installation.html, etc. The sidebar marks the active page
via a small inline script that compares each link's data-url to the current
page path.
Exit codes and --json
All wb publish verbs are non-interactive and carry exit codes: 0 on success, 1
on any failure. Add --json to any verb to get structured output:
wb publish apply --json | jq .
# → {"ok": true, "message": "deployed → https://...", "url": "https://..."}
This makes wb publish composable with agent tools and CI scripts.