Workbooks Documentation

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_TARGET is a recognised provider

  • Required 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.