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.
This commit is contained in:
parent
7e24a9cf02
commit
030ac3beeb
|
|
@ -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)
|
||||
Loading…
Reference in New Issue