5.8 KiB
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)
file-notify-add-watchon a TRAMP path (e.g.,/ssh:fenix@zzz:/home/jara/python/)- TRAMP's
tramp-sh-handle-file-notify-add-watchkicks in - It runs
inotifywait -mq -e create,modify,delete <path>on the remote host via SSH - Events stream back in real-time through the SSH tunnel
tramp-sh-inotifywait-process-filterparses them into standard Emacs file-notify events- Your callback fires with
(event-type file-path)— just like local inotify
Backend priority on remote (SSH)
- inotifywait (preferred) — already installed on zzz
- gio monitor (fallback) — GLib-based, more portable
- 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)
# On anka4 (professor's machine):
autossh -M 0 -N -R 2222:localhost:22 fenix@qu3v3d0.tech
Watcher modification on zzz
# 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:
autosshon 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
waterdaemon - 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
waterfor 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.