diff --git a/FINDINGS.md b/FINDINGS.md new file mode 100644 index 0000000..2cdc2e4 --- /dev/null +++ b/FINDINGS.md @@ -0,0 +1,162 @@ +# Key Findings: Emacs + napi Integration Research + +> Brainstorm session 2026-02-27 — distilled from TASKS.org last TODO + +--- + +## 1. Three Candidate Packages Evaluated + +| Package | Verdict | Why | +|:--------|:--------|:----| +| **emacs-jabber** (XMPP client) | Skip | Overkill for notifications. Full chat client overhead. Already have jabber.el configured for `jla@librebits.snikket.chat` but bot sends to `jla@librebits.info` (different JID). Would need account bridging. | +| **json-rpc-server.el + Porthole** | Skip | Over-engineered. Requires HTTP port exposure from zzz → local machine, firewall rules, session auth. Powerful but unnecessary when simpler options exist. | +| **Built-in file-notify (TRAMP)** | **Winner** | Real-time, zero dependencies, pure Emacs. Runs `inotifywait` on remote host via SSH. No new packages needed. | + +--- + +## 2. TRAMP + file-notify: The Key Discovery + +The GNU Emacs manual states: *"It is also possible to watch filesystems on remote machines"* + +### How it works (confirmed via Emacs 30.1 source analysis) + +1. `file-notify-add-watch` on a TRAMP path (e.g., `/ssh:fenix@zzz:/home/jara/python/`) +2. TRAMP's `tramp-sh-handle-file-notify-add-watch` kicks in +3. It runs `inotifywait -mq -e create,modify,delete ` **on the remote host** via SSH +4. Events stream back in real-time through the SSH tunnel +5. `tramp-sh-inotifywait-process-filter` parses them into standard Emacs file-notify events +6. Your callback fires with `(event-type file-path)` — just like local inotify + +### Backend priority on remote (SSH) + +1. **inotifywait** (preferred) — already installed on zzz +2. **gio monitor** (fallback) — GLib-based, more portable +3. Error if neither found + +### NOT polling + +This is a persistent SSH process streaming events. Latency = network RTT only (~ms for zzz). + +--- + +## 3. Existing Emacs Infrastructure (Already Installed) + +| Component | Status | Where | +|:----------|:-------|:------| +| **jabber.el** | Configured | `~/.emacs.d/init.el` line ~815 (`jla@librebits.snikket.chat`) | +| **jsonrpc.el** (v1.0.27) | Installed (LSP/eglot) | Built-in | +| **file-notify** | Built-in | Emacs 30.1 core | +| **notifications** (D-Bus) | Built-in | `(require 'notifications)` | +| **Two Emacs daemons** | Running | `server` + `water` (UI lives in `water`) | +| **TRAMP** | Built-in | SSH ControlMaster reuses connections | + +--- + +## 4. Current Notification Flow (What Already Works) + +``` +Student uploads via SFTP → zzz:/home/USER/python/ + │ + ▼ + inotifywait (watcher script) + │ + ▼ + python-upload-watcher.sh (v7) + - Batches by folder (10s silence window) + - Maps username → full name + - Detects re-entregas (delete+recreate <120s) + - Filters noise (Thumbs.db, __pycache__, etc.) + │ + ▼ + xmpp-notify.py (slixmpp) + zzz@librebits.info → jla@librebits.info + │ + ▼ + Fénix's XMPP client (phone/desktop) +``` + +The Emacs integration adds a **parallel path** — doesn't replace XMPP. + +--- + +## 5. Alternative Approach: emacsclient via SSH (Fallback Plan) + +If TRAMP file-notify proves unstable with 40+ watches, the fallback is: + +### Reverse SSH tunnel (anka4 → zzz) + +```bash +# On anka4 (professor's machine): +autossh -M 0 -N -R 2222:localhost:22 fenix@qu3v3d0.tech +``` + +### Watcher modification on zzz + +```bash +# Added to watcher scripts after existing notify_xmpp call: +notify_emacs() { + ssh -p 2222 -o ConnectTimeout=3 -o BatchMode=yes fenix@localhost \ + emacsclient --socket-name=water --eval \ + "(napi-notify \"$group\" \"$user\" \"$files\" \"$(date +%H:%M)\")" \ + 2>/dev/null & +} +``` + +### Security hardening (optional) + +Restrict the reverse-tunnel SSH key to emacsclient-only via `authorized_keys`: +``` +command="/home/fenix/.local/bin/napi-emacs-gateway.sh",no-port-forwarding ssh-ed25519 AAAA... +``` + +This approach requires: +- `autossh` on anka4 +- New SSH key pair (zzz → anka4) +- systemd unit for the tunnel +- Modifications to both watcher scripts on zzz + +**More moving parts than TRAMP**, which is why TRAMP is the primary plan. + +--- + +## 6. The `emacs --daemon=napi` Idea + +Fénix asked: *"Am I crazy saying that ~/napi could be running remotely, as long as a `emacs --daemon=napi` which I could hook into an `emacsclient`?"* + +**Answer: Not crazy at all.** A dedicated Emacs daemon for napi: + +- Isolates the 40 TRAMP watches from the main `water` daemon +- Can crash/restart independently without affecting the UI +- Light footprint (no packages loaded beyond TRAMP + napi.el) +- Managed via systemd (`emacs-napi.service`) +- Forwards notifications to `water` for display +- Queryable: `emacsclient --socket-name=napi --eval '(napi-status)'` + +--- + +## 7. Practical Limits + +| Concern | Reality | +|:--------|:--------| +| **40 inotifywait processes on zzz** | Kernel limit is ~65536 watches. 40 is trivial. | +| **40 SSH channels** | TRAMP uses ControlMaster — 1 actual connection, multiplexed. | +| **Memory on zzz** | Each inotifywait: ~1MB RSS. 40 = ~40MB. Fine. | +| **file-notify on dirs (not recursive)** | Each student has 1 upload dir. No recursion needed. | +| **SSH drops** | TRAMP sends `stopped` event. Elisp handler auto-restores watch. | + +--- + +## 8. Packages NOT Needed + +| Package | Why Skip | +|:--------|:---------| +| **Porthole** | HTTP RPC is overkill when TRAMP does it natively | +| **emacs-jabber** | XMPP notifications already work outside Emacs | +| **filenotify-recursive** | Student dirs are flat (no subdirs to recurse) | +| **autossh** | Not needed if TRAMP approach works | + +--- + +## 9. Next Step + +Implement `PLAN.md` — create `napi.el`, `napi-init.el`, systemd unit, and test.