From 030ac3beebb6c3572b0f87b29f2792afb2596b28 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 30 Sep 2026 10:05:18 +0200 Subject: [PATCH] Add 9P server design doc (draft) Documents design options for: - Section hierarchy (nested vs flat) - Property granularity (per-file vs structured) - Change notification - Write semantics - Agenda/query views Needs decisions before implementation. --- doc/9P_DESIGN.md | 270 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 doc/9P_DESIGN.md diff --git a/doc/9P_DESIGN.md b/doc/9P_DESIGN.md new file mode 100644 index 0000000..93d07fb --- /dev/null +++ b/doc/9P_DESIGN.md @@ -0,0 +1,270 @@ +# Orkmode 9P Server Design + +Status: **Draft — needs decisions** + +## Goal + +A 9P server that exposes org documents so any frontend (CLI, KDE app, web) can read/write by simple file operations. No client library needed. + +## Core Questions + +### 1. Section Hierarchy + +Org sections nest: `* A` contains `** B` contains `*** C`. + +**Option A: Nested directories** +``` +sections/ +└── a/ + ├── title + ├── todo + └── children/ + └── b/ + ├── title + └── children/ + └── c/ +``` +- Pro: Reflects actual structure +- Con: Deep paths (`sections/a/children/b/children/c/title`), awkward to enumerate all + +**Option B: Flat with parent refs** +``` +sections/ +├── idx # a\t1\tTitle A\tTODO\t\t(parent empty = root) +│ # b\t2\tTitle B\t\ta (parent = a) +│ # c\t3\tTitle C\t\tb (parent = b) +├── a/ +│ ├── title +│ ├── parent # (empty) +│ └── children # b +├── b/ +│ ├── title +│ ├── parent # a +│ └── children # c +└── c/ + ├── title + ├── parent # b + └── children # (empty) +``` +- Pro: Easy to list all sections, reconstruct tree client-side +- Con: Hierarchy implicit, extra `parent`/`children` files + +**Option C: Hierarchical IDs** +``` +sections/ +├── idx # 1\tTitle A\tTODO +│ # 1.1\tTitle B +│ # 1.1.1\tTitle C +├── 1/ +│ └── title +├── 1.1/ +│ └── title +└── 1.1.1/ + └── title +``` +- Pro: ID encodes position, no extra files +- Con: IDs change when sections move, not stable for references + +**Leaning toward:** Option B (flat + parent refs). Stable IDs (from CUSTOM_ID or hash), easy enumeration. + +--- + +### 2. Property Granularity + +How to expose section attributes? + +**Option A: One file per property** +``` +sections/review-pr/ +├── title # Review PR +├── todo # TODO +├── priority # A +├── tags # work:urgent +├── properties # CUSTOM_ID=review-pr\nEFFORT=2h +├── planning # DEADLINE: <2025-01-20 Mon> +└── body # The actual content... +``` +- Pro: `echo "DONE" > todo` just works, Unix composable +- Con: Many small files, multiple reads to get full section + +**Option B: Single structured file per section** +``` +sections/review-pr # title: Review PR + # todo: TODO + # priority: A + # tags: work urgent + # deadline: 2025-01-20 + # body: + # The actual content... +``` +- Pro: One read gets everything +- Con: Parsing needed, writes replace whole thing + +**Option C: Both** +``` +sections/review-pr/ +├── data # Structured (all properties) +├── title # Just title (convenience) +├── todo # Just todo (convenience) +└── raw # Original org text for this section +``` +- Pro: Flexible — use what fits +- Con: Redundancy, sync complexity + +**Leaning toward:** Option A (one file per property). Matches Unix philosophy. Reads are cheap. + +--- + +### 3. Change Notification + +How does a GUI know when to refresh? + +**Option A: No notification (poll)** +- Client re-reads periodically or on user action +- Simple, works everywhere +- Latency for external changes + +**Option B: Blocking read (statewait pattern)** +- `cat sections/idx.wait` blocks until change, returns new content +- Client loops: read → update UI → read again +- Complex server state, client must handle reconnects + +**Option C: External mechanism** +- Server writes to a Unix socket / named pipe on change +- Or: client uses inotify on .org files directly +- Separates concerns + +**Option D: Skip for v1** +- CLI doesn't need it +- GUI can poll or watch .org files with inotify +- Add later if needed + +**Leaning toward:** Option D for now. Don't over-engineer before we have a GUI. + +--- + +### 4. Write Semantics + +How do writes flow back to .org files? + +**Option A: Direct property writes** +```bash +echo "DONE" > sections/review-pr/todo +# Server rewrites work.org with updated TODO state +``` +- Pro: Intuitive +- Con: Partial writes tricky (tags append vs replace?) + +**Option B: Command-based writes** +```bash +echo "todo DONE" > sections/review-pr/ctl +echo "tag +urgent" > sections/review-pr/ctl +echo "tag -work" > sections/review-pr/ctl +``` +- Pro: Clear semantics, atomic operations +- Con: Less intuitive than direct file writes + +**Option C: Document-level raw write** +```bash +# Edit raw org content +cat docs/work.org/raw > /tmp/edit.org +vim /tmp/edit.org +cat /tmp/edit.org > docs/work.org/raw +# Server reparses and updates index +``` +- Pro: Full control, no semantic mismatch +- Con: Client does all the work + +**Leaning toward:** Option A for simple cases (todo, priority), Option B (ctl) for complex ops (tag add/remove). Option C always available as escape hatch. + +--- + +### 5. Agenda / Query Views + +How to handle cross-document queries? + +**Option A: Computed directories** +``` +agenda/ +├── today # Sections with deadline/scheduled today +├── week # This week +├── todos # All open TODOs +└── tags/ + └── {tag} # Sections with this tag +``` +- Reads compute result on demand +- Simple, no index maintenance + +**Option B: Search file (rdwr)** +``` +echo "todo:TODO tag:work" > search +cat search +# Returns matching section IDs +``` +- More flexible queries +- Single entry point + +**Option C: Both** +- Common queries as directories +- `search` for custom queries + +**Leaning toward:** Start with Option A (fixed views). Add search later. + +--- + +## Proposed Structure (v1) + +``` +/ork/ +├── ctl # Commands: reload, quit +├── docs/ +│ ├── idx # doc\tpath\ttitle\ttodos\tdone (per line) +│ └── {doc}/ +│ ├── raw # Full .org content (r/w) +│ ├── meta # title, author, etc. (read-only) +│ └── sections/ +│ ├── idx # id\tlevel\ttitle\ttodo\tpriority\ttags\tparent +│ └── {id}/ +│ ├── title +│ ├── todo +│ ├── priority +│ ├── tags +│ ├── planning +│ ├── properties +│ ├── body +│ ├── parent +│ ├── children +│ └── ctl # Commands: todo X, tag +x, tag -x +├── agenda/ +│ ├── today +│ ├── week +│ └── todos +└── capture # Write "* TODO text" to append to inbox +``` + +## Open Questions + +1. **ID stability** — If CUSTOM_ID doesn't exist, generate from title hash? What if title changes? + +2. **Concurrency** — Multiple writers (emacs + orkmode)? Last-write-wins, or detect conflicts? + +3. **Large files** — Stream body content, or load all in memory? + +4. **Symbolic links** — Should `agenda/today` entries be symlinks to actual section dirs? + +5. **Error handling** — Write fails (invalid todo keyword) — return error how? (9P has Rerror) + +--- + +## Implementation Plan + +1. **ork-server crate** — 9P server using `nine` crate +2. **VirtFS layer** — Map namespace to org-parser operations +3. **File watcher** — Detect external .org changes, reparse +4. **CLI update** — `ork` talks to server instead of parsing directly (optional) + +## References + +- [nine crate](https://crates.io/crates/nine) — Rust 9P2000 +- [9P protocol](http://9p.io/sys/man/5/INDEX.html) — Plan 9 manual +- ~/src/ollie — Working 9P implementation (Go)