OQL (Org Query Language)
OQL is the WASM kernel at the centre of the workbooks runtime. It is a Rust
library compiled to wasm32-wasi, wrapped as a WIT component, and embedded in the
runtime binary. Every parse, render, tangle, and lint operation goes through OQL.
The desktop app also embeds it — local workbook rendering requires no server.
What OQL is
OQL is not a query language in the database sense. The name reflects its purpose: it is the query interface over an Org file. You ask it to parse a workbook into headlines, extract a build plan, render to HTML, or validate structure. The answers come back as structured data or HTML — no side effects, no I/O.
The Rust source is in runtime/kernel/. The compiled output is oql.wasm. The
Elixir wrapper is runtime/host/oql.ex.
Exported functions
OQL exports five functions, all accessible via Workbooks.OQL.*:
parseheadlines
Workbooks.OQL.parse_headlines(org :: String.t()) :: [map()]
Returns every heading in the org source as a list of maps:
[
{
"level": 1,
"title": "My Workbook",
"tags": [],
"properties": {"WORKBOOK_VERSION": "1"},
"position": {"start": 0, "end": 45}
},
{
"level": 2,
"title": "Compute",
"tags": [],
"properties": {"in": "x", "out": "result"},
"position": {"start": 46, "end": 120}
}
]
This is used by wb query, toolkit discovery, and any agent tool that needs to
inspect workbook structure without rendering it.
tangleplan
Workbooks.OQL.tangle_plan(org :: String.t()) :: map()
Returns the build plan: worlds, components, edges, imports, exports. This is the DAG that drives WASM compilation and execution. See The Build Plan for the complete schema.
render
Workbooks.OQL.render(org :: String.t()) :: String.t()
Renders the org source to an HTML fragment (not a full page — headings, prose,
code blocks, tables, lists). The caller wraps it in a page shell
(PublicWeb.static_page/2 or PublicWeb.site_page/5).
The orgize Rust parser is used for rendering. Org elements map to semantic HTML:
headings → <h2> / <h3>, source blocks → <pre><code>, tables →
<table>, inline code → <code>, links → <a>.
Note: #+begin_example blocks that contain nested Org markers should use
comma-escaping (prefix each inner marker with ,) to prevent the parser from
closing the block early. Alternatively, use #+begin_src org for literal Org
examples.
lint
Workbooks.OQL.lint(org :: String.t()) :: [map()]
Returns a list of issue maps. Currently a stub returning [] — full static
analysis (undeclared deps, type mismatches, unreachable components) is a planned
addition.
validate
Workbooks.OQL.validate(org :: String.t()) :: :ok | {:error, [String.t()]}
Checks that the org source is a valid workbook: has a :WORKBOOK_VERSION:
property, no structural errors. Returns :ok or an error tuple with a list of
human-readable issues.
checkupgrade
Workbooks.OQL.check_upgrade(org :: String.t()) :: map()
Returns upgrade advice for a workbook at an older WORKBOOK_VERSION. Returns a
map with needs_upgrade: false if the workbook is current.
The Rust kernel
The kernel is in runtime/kernel/src/lib.rs. Key types:
| Type | Role |
Headline | A parsed org heading with level, title, tags, properties, children |
Src | A source block with language, content, and header args |
Args | Parsed :in:, :out:, :deps:, :sig: properties |
Comp | A component: heading ID + args |
Sig | An explicit WIT function signature from :sig: |
World | A compilable unit: source + language |
The orgize crate does the actual Org parsing. The kernel walks the orgize AST to collect headings and source blocks, then builds the plan from the collected data.
WIT interface
OQL is exposed as a WIT component. The WIT world is in
runtime/kernel/wit/world.wit. The five exported functions map to the five
Workbooks.OQL.* calls. The Wasmex NIF calls the WIT exports directly; no
serialization layer between Elixir and the WASM boundary.
Where oql.wasm comes from
At mix deps.compile time, scripts/provision-tools.sh ensures javy,
wasm-tools, and wasi-adapter are on PATH, then mix compile runs the Rust
build (cargo build --target wasm32-wasi), wraps the output with wasm-tools
component new, and embeds the result via a Mix task into the Wasmex NIF. The
final oql.wasm lives at priv/oql.wasm and is baked into the escript/release.