From 7612e206693056e0d19fe457e7bdd9ffb6d5d434 Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Sat, 20 Jun 2026 15:19:57 -0400 Subject: [PATCH] docs: initial scaffold design for Spine --- .../2026-06-20-initial-scaffold-design.md | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 docs/specs/2026-06-20-initial-scaffold-design.md diff --git a/docs/specs/2026-06-20-initial-scaffold-design.md b/docs/specs/2026-06-20-initial-scaffold-design.md new file mode 100644 index 0000000..a067d88 --- /dev/null +++ b/docs/specs/2026-06-20-initial-scaffold-design.md @@ -0,0 +1,145 @@ +# Spine — initial scaffold + +A minimal working Emacs web server that serves templated HTML. This is the +first concrete slice of [spine-spec.md](../spine-spec.md), resolving the open +"glue language + web framework" decision: Emacs owns the HTTP socket and serves +the UI directly. + +## Architecture decision + +**Emacs end-to-end.** No external web process, no `emacsclient` IPC for reads. +The app is a single Emacs instance running `simple-httpd`. This keeps the build +simple (one language, two packages) and is adequate for Spine's complexity level. + +## Packages + +| Package | Version | Source | Purpose | +|---------|---------|--------|---------| +| `simple-httpd` | MELPA latest | MELPA | HTTP server, `defservlet` routing | +| `mustache` | MELPA latest | MELPA | Mustache template rendering | +| `ht` | — | transitive via `mustache` | Hash table for template context | + +## Project structure + +``` +spine/ +├── spine.el # entry point: bootstrap, server start, handlers +├── templates/ +│ └── hello.mustache # smoke-test template +├── spine-spec.md # design brief (existing) +├── spine-mockup-a-agenda.html +├── spine-mockup-b-journal.html +└── README.md +``` + +- `spine.el` is the sole Elisp file for now. Split later when a seam earns it. +- Templates live in `templates/` as `.mustache` files. +- Mockups stay as reference artifacts; they are not served. + +## Package bootstrap + +`spine.el` uses `package.el` (built-in). On first run it installs missing +packages from MELPA; subsequent starts skip the install step. + +``` +(require 'package) +(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) +(package-initialize) +(dolist (pkg '(simple-httpd mustache)) + (unless (package-installed-p pkg) + (package-refresh-contents) + (package-install pkg))) +``` + +`package-refresh-contents` runs only when a package is missing — not on every +start. + +## Server startup + +``` +(defvar spine-port 8080 + "Port for the Spine HTTP server.") + +(defvar spine-template-dir + (expand-file-name "templates/" (file-name-directory load-file-name)) + "Directory containing .mustache templates.") + +(setq httpd-port spine-port) +(httpd-start) +(message "Spine listening on http://localhost:%d" spine-port) +``` + +- Port defaults to 8080, overridable by setting `spine-port` before loading. +- `spine-template-dir` resolves relative to `spine.el`'s directory so the app + works regardless of `default-directory`. + +## Template rendering + +A single helper wraps Mustache rendering: + +``` +(defun spine-render (name context) + "Render templates/NAME.mustache with CONTEXT (an ht hash table). +Returns the rendered string." + (mustache-render + (with-temp-buffer + (insert-file-contents + (expand-file-name name spine-template-dir)) + (buffer-string)) + context)) +``` + +- Reads the template file from disk on every request. No caching — correct and + simple; add caching if profiling shows a need. +- `context` is an `ht` hash table whose keys map to `{{key}}` placeholders. + +## Hello World handler + +``` +(defservlet hello text/html () + (insert (spine-render "hello.mustache" + (ht ("title" "Spine") + ("message" "Hello from Emacs."))))) +``` + +`templates/hello.mustache`: + +```html + + + + + + {{title}} + + +

{{message}}

+ + +``` + +- Accessible at `http://localhost:8080/hello`. +- The root `/` is unbound and will show `simple-httpd`'s default directory + listing — acceptable for a smoke test. + +## Invocation + +```bash +emacs --quick --load spine.el +``` + +`--quick` (aliases: `-Q`) skips init files for a clean, repeatable start. + +## Acceptance + +- [ ] `emacs --quick --load spine.el` starts without errors. +- [ ] Browsing `http://localhost:8080/hello` shows "Hello from Emacs." +- [ ] Templates load relative to `spine.el`'s directory, not `default-directory`. +- [ ] Second invocation skips package install (packages already present). + +## Out of scope + +- Book data, Org file integration, any handler beyond `/hello`. +- Static file serving (CSS, JS, icons). +- Emacs daemon mode, process management, systemd units. +- Tests — the scaffold is small enough to smoke-test manually.