HTTP + WebSocket API¶
At a glance¶
condash runs a local axum server bound to 127.0.0.1:<port>. The main condash binary wraps it in a Tauri window; condash-serve runs the same server headless for browsers and automation. All routes are local-only — there is no auth layer, and condash never binds to a non-loopback address.
Groups:
| Area | Routes | Purpose |
|---|---|---|
| Dashboard shell | /, /favicon.*, /fragment |
Page HTML, favicons, partial re-renders |
| Change polling | /check-updates, /search-history |
Fingerprints + global search |
| Notes | /note, /note-raw, /note/* |
Read, edit, rename, create, upload |
| Assets | /download, /asset, /file |
Streaming bytes for PDFs, images, arbitrary files |
| Mutations | /toggle, /add-step, /edit-step, /remove-step, /reorder-all, /set-priority |
README edits |
| Openers | /open, /open-doc, /open-folder, /open-external |
Launch external processes |
| Meta / clipboard | /config, /clipboard, /recent-screenshot |
Config r/w, Qt clipboard, screenshot-paste lookup |
| Vendored assets | /vendor/pdfjs/…, /vendor/xterm/… |
pdf.js + xterm.js bundles |
| Terminal | WS /ws/term |
Interactive PTY |
For mutation semantics (what each route writes), see Mutation model.
Dashboard shell¶
| Method | Path | Returns |
|---|---|---|
| GET | / |
Full dashboard HTML. Re-parses the conception tree on every call. |
| GET | /favicon.svg, /favicon.ico |
Bundled SVG app icon |
| GET | /fragment?id=<id> |
HTML subtree for one card or one knowledge directory |
/fragment ids:
| Shape | Returns |
|---|---|
projects/<priority>/<slug> |
One project card. |
knowledge/<path>.md |
One knowledge card. |
knowledge/<path> (dir) |
Knowledge directory subtree. |
knowledge (root) |
404 — client falls back to full-page reload. |
| Anything else | 404. |
Change polling¶
| Method | Path | Purpose |
|---|---|---|
| GET | /check-updates |
Cheap full-tree fingerprint — client polls every 5 s |
| GET | /search-history?q=<query> |
Ranked search across README bodies, notes, filenames |
/check-updates response shape:
{
"fingerprint": "0123456789abcdef",
"git_fingerprint": "fedcba9876543210",
"nodes": {
"projects": "…",
"projects/now": "…",
"projects/now/2026-04-18-helio-benchmark-harness": "…",
"knowledge/topics/playwright-sandbox.md": "…"
}
}
fingerprint is the 16-hex MD5 of the whole-tree repr; a change at any level flips it. nodes is a flat map that lets the client decide which subtree changed and re-fetch just that — preventing full-page flicker on a single step toggle. See internals for how the hashes are computed.
/search-history returns a list of per-item hits ranked by condash-state::search::search_items. Empty q returns [].
Notes¶
All paths are relative to conception_path.
| Method | Path | Body / Query | Response |
|---|---|---|---|
| GET | /note?path=<rel> |
– | HTML render of a Markdown / text / PDF / image note |
| GET | /note-raw?path=<rel> |
– | {path, content, mtime, kind} for the edit view |
| POST | /note |
{path, content, expected_mtime?} |
{ok, mtime} or 409 {ok: false, reason} on mtime drift |
| POST | /note/rename |
{path, new_stem} |
{ok, path, mtime} |
| POST | /note/create |
{item_readme, filename, subdir?} |
{ok, path, mtime} |
| POST | /note/mkdir |
{item_readme, subpath} |
{ok, rel_dir, subdir_key} or 409 {reason: "exists"} |
| POST | /note/upload |
multipart/form-data with item_readme, optional subdir, file parts |
{ok, stored: [...], rejected: [...]} |
Upload size cap: 50 MB per file. Collisions auto-suffix (2), (3)…
See mutations for the filename regexes and sandbox rules.
Asset streaming¶
| Method | Path | Purpose |
|---|---|---|
| GET | /download/{rel} |
PDF download with Content-Disposition: inline. Rejects non-PDF paths. |
| GET | /asset/{rel} |
Image assets embedded in Markdown previews. 5-minute public cache. |
| GET | /file/{rel} |
Any file under the conception tree — used by the in-modal PDF + image viewer. 60 s private cache. |
All three re-validate the path against conception-tree regexes on every call. 403 on escape.
Mutations¶
All operate on an item's README.md by line number. See mutations for the effect on the file.
| Method | Path | Body |
|---|---|---|
| POST | /toggle |
{file, line} — cycles [ ]→[x]→[~]→[-]→[ ] |
| POST | /add-step |
{file, text, section?} |
| POST | /edit-step |
{file, line, text} |
| POST | /remove-step |
{file, line} |
| POST | /reorder-all |
{file, order: [line, line, …]} |
| POST | /set-priority |
{file, priority} — one of now/soon/later/backlog/review/done |
All return {ok: true, …} on success or {error: "<message>"} with 400 on validation failure.
Openers¶
These launch external processes. No filesystem writes — but they do mean "condash runs a shell command", so the sandbox regexes matter.
| Method | Path | Body | What runs |
|---|---|---|---|
| POST | /open |
{path, tool} |
cfg.open_with[tool].commands chain. path must resolve under workspace_path or worktrees_path. |
| POST | /open-doc |
{path} |
cfg.pdf_viewer chain for .pdf, OS default for everything else. path under conception_path. |
| POST | /open-folder |
{path} |
OS default file manager. path must match projects/YYYY-MM/YYYY-MM-DD-slug/. |
| POST | /open-external |
{url} |
User's default browser. URL must be http(s)://…. |
Meta, clipboard, config¶
| Method | Path | Purpose |
|---|---|---|
| GET | /config |
Full runtime config as JSON (merged TOML + YAML) |
| POST | /config |
Save the config. Returns {ok, restart_required: [...], config} |
| GET | /clipboard |
System clipboard text. Tries Qt QClipboard, then wl-paste / xclip / xsel. |
| POST | /clipboard |
Set the system clipboard. Body is the raw text. |
| GET | /recent-screenshot |
{path, dir, reason?} — path of the newest image file in terminal.screenshot_dir |
GET /config returns a flat JSON matching the dashboard's gear-modal form. yaml_source / preferences_yaml_source show where the YAML fields currently come from (useful for debugging per-tree vs per-machine overrides).
/clipboard works in both native and browser mode: the Qt QClipboard path is taken when native=true; otherwise the subprocess fallbacks handle Wayland / X11.
/recent-screenshot powers the screenshot-paste shortcut. reason is one of directory does not exist, configured path is not a directory, permission denied, no image files found. The client pastes path into the active terminal tab without appending a newline.
Vendored assets¶
| Method | Path | Purpose |
|---|---|---|
| GET | /vendor/pdfjs/{rel} |
Mozilla PDF.js (worker, cmaps, fonts, wasm, iccs). 24-hour cache. |
| GET | /vendor/xterm/{rel} |
xterm.js library + CSS + addon-fit. 24-hour cache. |
Both routes reject .. and null bytes; files outside the bundled directory 403.
Why vendored: QtWebEngine ships with PdfViewerEnabled=false, so the in-modal viewer can't rely on the webview's built-in PDF renderer. And a CDN fetch for xterm.js breaks offline / air-gapped installs. See internals.
Terminal WebSocket¶
| Method | Path | Purpose |
|---|---|---|
| WS | /ws/term |
Interactive PTY session (Linux + macOS only) |
Query parameters:
| Param | Meaning |
|---|---|
session_id=<id> |
Reattach to an existing PTY session. If the id is unknown, the server sends {type: "session-expired"} and closes. |
cwd=<path> |
Start the new shell in this directory. Must resolve under workspace_path / worktrees_path. Silently ignored otherwise. |
launcher=1 |
Exec terminal.launcher_command instead of a login shell. |
Frames, server → client:
| Type | Shape |
|---|---|
| Binary | Raw bytes from the PTY — append to the xterm buffer verbatim. |
Text JSON {type: "info", session_id, shell, cwd} |
First frame after attach. |
Text JSON {type: "exit"} |
Shell exited. The server closes the socket immediately after. |
Text JSON {type: "session-expired", session_id} |
Requested session is gone. Drop it from localStorage. |
Text JSON {type: "error", message} |
Unsupported platform (Windows) or other fatal refusal. |
Frames, client → server:
| Shape | Meaning |
|---|---|
| Binary | Raw input to the PTY. |
Text JSON {type: "resize", cols, rows} |
TIOCSWINSZ relay. |
The PTY survives the WebSocket: a page refresh detaches cleanly and the buffer (256 KiB ring) replays on the next attach. See guide: using the embedded terminal for the end-user surface.
Auth, CORS, bind address¶
- Server binds to
127.0.0.1only. Non-loopback addresses are never used. - No auth layer. The sandbox is "only localhost traffic can reach the server".
- No CORS headers — the dashboard lives on the same origin.
- No multi-user mode; condash is single-user by design.
If you want to drive condash from a second tool, run condash-serve with a pinned CONDASH_PORT — it prints the bound URL on startup. Without CONDASH_PORT set, the port is picked from 11111–12111 at launch.