code execution

your notes,
they compute.

No Jupyter. No Electron. No Notion blocks. A fenced code block inside a markdown bullet runs in five languages, drops the result back into the file, and stays diff-able forever.

I
act one

the idea is
embarrassingly small.

You're outlining a problem. You want to verify a calculation. Today: tab to a REPL, paste, run, tab back, paste the result. Maybe annotate. Two days later you can't tell which number was hand-typed and which one was computed.

outl removes the tabbing. The code lives in your note. The result lives in your note. The note is the notebook.

A fenced code block isn't syntax highlighting. It's an invitation.
II
act two

press on this.

~/notes/launch-day.md ▷ run
title:: launch day math
how many commits this week?
```python
from datetime import date
commits = [42, 17, 31, 9, 28, 14, 22]
total = sum(commits)
avg = total / len(commits)
print(f"{total} commits · avg {avg:.1f}/day")
```
idle
› result:
cached · hash: a91f2c3 auto-run:: on
the result is a markdown blockquote. open the file in vim — it's still markdown.

The button on the right is the same key as gx in the TUI. Or set auto-run:: on on the block and it runs every time the page opens — cache-aware, so unchanged source returns the cached result in <1ms.

III
act three

five languages,
one trick.

Each language is an outl-exec adapter — about 80 lines of Rust. The runtime registry is plugin-shaped: a new language ships as a new crate.

Py Python RustPython
```python
nums = [1, 1, 2, 3, 5, 8, 13]
print(sum(nums) / len(nums))
```
› result: 4.714285714285714
Scientific snippets, quick stats, list comprehensions — without spinning up Jupyter.
() Lisp Steel · Scheme R5RS-ish
```lisp
(define (sq x) (* x x))
(map sq '(1 2 3 4 5))
```
› result: '(1 4 9 16 25)
Tiny DSLs, symbolic computation, transformation pipelines — homoiconic notes.
JS JavaScript Boa · ES2015+
```js
const days = (a, b) =>
  (b - a) / 864e5;
days(new Date('2026-01-01'), new Date())
```
› result: 146
Web math, date arithmetic, regex play — exactly what you'd open the browser console for.
Lua Lua mlua 5.4 · vendored
```lua
local t = {1, 2, 3, 4, 5}
local sum = 0
for _, v in ipairs(t) do sum = sum + v end
return sum
```
› result: 15
Embedded scripting feel. Fast. Familiar if you've ever touched Neovim or Redis.
Rs Rust rustc → wasm32-wasip1 → wasmtime
```rust
fn main() {
    let v: Vec<i32> = (1..=10).collect();
    println!("{}", v.iter().sum::<i32>());
}
```
› result: 55
Compiled and cached as wasm in ~/.cache/outl/runtimes/. ~20× faster re-runs.
IV
act four

auto-run
and the hash cache.

Set auto-run:: on on a block. Every time the page opens, the block evaluates — but only if its source changed.

first open
page loaded
exec python · sha:a91f2c3
result rendered · 18ms
cached a91f2c3 → "194 commits · avg 27.7/day"
second open · source unchanged
page loaded
cache hit · sha:a91f2c3
result rendered · 0ms
· no runtime spinup, no work

The hash is stamped into the result subblock as source-hash::. If you edit the source, the hash changes, the cache misses, the block re-runs. Deterministic and observable.

V
act five

the sandbox
is paranoid.

Every block runs in wasmtime with WASI cut down to the bone. If a random gist on the internet runs in your outl, it can't reach anything that matters.

no preopens

the runtime can't see your filesystem. The .md file calling it doesn't even exist from inside the sandbox.

no env

no environment variables, no $HOME, no shell. The block runs in a vacuum.

no sockets

no network. No http requests, no DNS, no databases. If you want net, lift the source out and run it in a real shell.

fuel cap

the runtime instruction counter is capped. An infinite loop bails out cleanly instead of pegging your CPU.

epoch timeout

wall-clock timeout via wasmtime epoch interruption. A frozen runtime gets aborted.

in-memory io

stdin/stdout/stderr are captured in memory. Whatever you print is the result block; no log files, no side effects.

Pasting a stranger's snippet into a regular REPL is a leap of faith. Pasting it into outl is a contained experiment.
VI
act six

vs the usual
suspects.

feature outl Jupyter Notion Dataview Logseq Observable
markdown is the source of truth .ipynb (JSON) ✕ cloud via .md via .md (with id::) .ocnb (JSON)
multi-language native 5 langs via kernels JS only limited JS only
result lands as markdown ✓ > result: output cell cell rendered limited cell
source-hash cache ✓ SHA-256 manual memoized limited live
WASM sandbox by default ✕ host ✕ cloud host JS host ✕ host
works offline, no account limited
open source MIT BSD MIT plugin AGPL ✕ partial
where outl wins
  • +markdown is yours forever — the source format your editor already speaks
  • +five languages, no kernel install
  • +sandboxed by default, not as opt-in
  • +native binary, no Electron tax
where outl loses (today)
  • no rich output yet — matplotlib plots, dataframes, images are stdout-only
  • no shared kernel state across blocks — each block is its own ephemeral runtime
  • no third-party Python packages (RustPython subset; no numpy yet)
  • no interactive widgets — the result is text

run code,
keep markdown.

One binary. Five runtimes. Sandbox by default.