docs: initial scaffold design for Spine

This commit is contained in:
2026-06-20 15:19:57 -04:00
commit 7612e20669
@@ -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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{title}}</title>
</head>
<body>
<h1>{{message}}</h1>
</body>
</html>
```
- 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.