---
url: 'https://sema-lang.com/docs/notebook.md'
---

# Notebook

Sema includes a Jupyter-inspired notebook interface for interactive development. Write code in cells, evaluate them individually or all at once, and see results inline — all in the browser.

## Quick Start

```bash
# Create a new notebook
sema notebook new my-notebook.sema-nb

# Open in the browser
sema notebook serve my-notebook.sema-nb
```

This starts a local server at `http://localhost:8888` with the notebook UI.

## Cell Types

### Code Cells

Code cells contain Sema expressions. Evaluate with **Shift+Enter** (run and advance) or **Cmd/Ctrl+Enter** (run and stay).

Cells share a persistent environment — definitions in earlier cells are visible in later ones:

```sema
;; Cell 1
(define greet (fn (name) (format "Hello, ~a!" name)))

;; Cell 2 — can use greet from Cell 1
(greet "Sema")  ;=> "Hello, Sema!"
```

Output from `println`, `display`, and `print` is captured and shown in the cell output area.

### Markdown Cells

Markdown cells render formatted text for documentation, section headers, and notes. Supports headings, bold, italic, inline code, code blocks, and lists.

Click rendered markdown to edit. Press **Shift+Enter** to re-render.

## Keyboard Shortcuts

| Shortcut         | Action                        |
| ---------------- | ----------------------------- |
| `Shift+Enter`    | Run cell and advance to next  |
| `Cmd/Ctrl+Enter` | Run cell and stay focused     |
| `Cmd/Ctrl+S`     | Save notebook                 |
| `Tab`            | Insert 2 spaces               |
| `Escape`         | Deselect cell                 |

## Toolbar

| Button          | Action                                      |
| --------------- | ------------------------------------------- |
| **+ Code**      | Add a new code cell at the end              |
| **+ Markdown**  | Add a new markdown cell at the end          |
| **Run All**     | Evaluate all code cells in order            |
| **Undo**        | Undo the last cell evaluation (restores environment) |
| **Save**        | Save the notebook to disk                   |
| **Reset**       | Clear all outputs and reset the environment |

You can also insert cells between existing ones by hovering between cells and clicking the **+** button that appears.

## Undo

After evaluating a cell, click **Undo** (or the inline "Undo cell" button on error outputs) to roll back:

* The cell's outputs are restored to their previous state
* The interpreter environment is rolled back to before the evaluation
* Downstream stale markers are reverted

This is useful when a cell modifies global state unexpectedly.

## File Format

Notebooks are saved as `.sema-nb` files in JSON format:

```json
{
  "version": 1,
  "metadata": {
    "title": "My Notebook",
    "created": "2026-01-01T00:00:00Z",
    "modified": "2026-01-01T12:00:00Z",
    "sema_version": "1.14.2"
  },
  "cells": [
    {
      "id": "c12345678",
      "type": "code",
      "source": "(+ 1 2)",
      "outputs": []
    }
  ]
}
```

### Cell `id` format

Cell IDs are short stable strings of the form `"c"` followed by 8 lowercase
hexadecimal characters (e.g. `"c4a3f2b1"`). They are generated by hashing a
fresh UUIDv4 to its first 8 hex digits and prefixing with `c`. Treat them as
opaque tokens — they're stable for the lifetime of a cell and used by the
REST API to address cells.

### `stale` flag

Code cells carry an optional `stale: bool` field. When an upstream cell is
re-evaluated, all downstream code cells with existing outputs are flipped to
`stale: true`. The field is omitted from serialized JSON when `false`. A
stale cell still displays its previous outputs, but the UI indicates that
those outputs may not reflect the current state of the environment until the
cell is re-evaluated.

### Fully-evaluated cell example

When a code cell has been evaluated, its `outputs` array contains one or
more `CellOutput` objects. Here is a cell with every field populated:

```json
{
  "id": "c4a3f2b1",
  "type": "code",
  "source": "(+ 1 2)",
  "outputs": [
    {
      "type": "value",
      "display": "3",
      "sema_value": "3",
      "timestamp": "2026-01-01T12:34:56.789Z",
      "cost_usd": 0.0,
      "requires_reeval": false,
      "duration_ms": 12
    }
  ]
}
```

Field reference (`CellOutput`):

* `type` — output discriminator (see table below).
* `display` — human-readable string shown in the UI.
* `sema_value` — round-trippable S-expression form of the value (omitted when not applicable, e.g. for `error` / `stdout` outputs).
* `timestamp` — RFC 3339 UTC timestamp of when the output was produced.
* `cost_usd` — estimated LLM cost, for outputs from LLM calls; omitted otherwise.
* `requires_reeval` — `true` for opaque values (lambdas, native fns, macros, streams, thunks) that cannot be round-tripped through `sema_value` and must be re-evaluated on notebook reload.
* `duration_ms` — wall-clock evaluation time of the cell, in milliseconds.

### `OutputType` discriminator

The `type` field on `CellOutput` is one of:

| Value     | Meaning                                                                          |
| --------- | -------------------------------------------------------------------------------- |
| `value`   | The cell returned a normal Sema value (the common case for successful evals).    |
| `error`   | Evaluation produced a `SemaError`; `display` holds the formatted error message.  |
| `stdout`  | Captured `println` / `display` / `print` output produced during cell evaluation. |

A single evaluated code cell may have both a `stdout` output (first) and a
`value` or `error` output (second), in that order.

### Version compatibility

The current notebook format version is `1`. Tools reading `.sema-nb` files
should treat `version` as a major version number and refuse to load files
where `MAJOR != 1` rather than silently mis-parsing them. Forward-compatible
field additions (e.g. new optional fields on `CellOutput`) do not bump
`version`; breaking changes to existing fields will.

## Headless Execution

Run all cells without starting the browser UI:

```bash
sema notebook run my-notebook.sema-nb
```

This evaluates all code cells in order, printing stdout to the terminal. Useful for CI validation or batch processing.

Run specific cells by index (1-based):

```bash
sema notebook run my-notebook.sema-nb --cells 1,3,5
```

## Export

Export a notebook to Markdown:

```bash
# To stdout
sema notebook export my-notebook.sema-nb

# To file
sema notebook export my-notebook.sema-nb -o output.md
```

The export includes code blocks with output, markdown sections, and error messages.

## REST API

The notebook server exposes a JSON HTTP API on the same port as the browser UI. Everything the UI does goes through these endpoints — they're stable enough to script against from external tools.

### Notebook & cells

| Method | Path                       | Description                                        |
| ------ | -------------------------- | -------------------------------------------------- |
| GET    | `/api/notebook`            | Return the full notebook (cells + metadata)        |
| POST   | `/api/cells`               | Create a new cell                                  |
| GET    | `/api/cells/{id}`          | Fetch a single rendered cell                       |
| POST   | `/api/cells/{id}`          | Update a cell's source or type                     |
| DELETE | `/api/cells/{id}`          | Delete a cell                                      |
| POST   | `/api/cells/{id}/eval`     | Evaluate a single cell                             |
| POST   | `/api/cells/reorder`       | Reorder cells by id                                |
| POST   | `/api/eval-all`            | Evaluate all cells (optionally with edited source) |
| GET    | `/api/env`                 | Inspect the current shared cell environment       |
| POST   | `/api/reset`               | Reset the evaluation environment                   |
| POST   | `/api/undo`                | Undo the last cell evaluation                      |
| POST   | `/api/save`                | Save the notebook to disk                          |

Create cell request:

```json
{ "type": "code", "source": "(+ 1 2)", "after": "<cell-id?>" }
```

Update cell request:

```json
{ "source": "(+ 1 2)", "type": "code" }
```

Reorder request:

```json
{ "cell_ids": ["id-1", "id-2", "id-3"] }
```

Eval-all request (optional — pass currently-edited sources without saving first):

```json
{ "sources": [["cell-id", "(println \"hi\")"]] }
```

### VFS

The notebook server exposes a small virtual filesystem so the browser UI can read and write files alongside the notebook.

| Method | Path         | Description                                  |
| ------ | ------------ | -------------------------------------------- |
| GET    | `/vfs/read`  | Read a file: `?path=foo.txt` → text body     |
| POST   | `/vfs/write` | Write a file (JSON body, see below)          |
| GET    | `/vfs/list`  | List a directory: `?path=.` → JSON entries   |

Write request:

```json
{ "path": "notes.txt", "content": "hello" }
```

List response (`FileEntry[]`):

```json
[{ "name": "notes.txt", "is_dir": false, "size": 5 }]
```

::: warning VFS scope
VFS endpoints are sandboxed to the **parent directory of the notebook file**. When `sema notebook serve` is started **without** a `--notebook` path, the VFS root falls back to the current working directory (`$PWD`). The server prints a warning at startup in that case — prefer passing a notebook path if you don't want the whole `$PWD` to be reachable.
:::

## Security

The notebook server is a **trusted-local** developer tool. Cells run arbitrary Sema code — including file and network access — with the full privileges of the user who started the server, and the server has **no authentication or authorization layer**.

For this reason the server binds to the loopback interface (`127.0.0.1`) by default, so it is only reachable from the local machine. You can override the bind address with `--host`, but binding to a non-loopback address such as `0.0.0.0` exposes an unauthenticated remote code-execution endpoint to the network. If you need remote access, putting it behind a firewall, VPN, or an authenticating reverse proxy is **the operator's responsibility**.

## CLI Reference

See [`sema notebook`](/docs/cli#sema-notebook) in the CLI reference for all flags and options.
