Files
spine/docs/plans/2026-06-20-initial-scaffold.md

6.2 KiB
Raw Permalink Blame History

Spine Initial Scaffold — Implementation Plan

Goal: A minimal Emacs web server that serves a templated "Hello from Emacs." page, exercising the simple-httpd + mustache.el stack end-to-end.

Architecture: Single spine.el entry point bootstraps packages, starts simple-httpd on port 8080, and registers a /hello servlet. Templates are .mustache files read from a templates/ directory relative to spine.el.

Tech Stack: Emacs Lisp, simple-httpd (MELPA), mustache (MELPA), ht (transitive).


File structure

File Responsibility
templates/hello.mustache Smoke-test HTML template with {{title}} and {{message}} placeholders
spine.el Entry point: package bootstrap, server config, template renderer, /hello handler

spine.el is the sole Elisp file. No project file, no Makefile — invocation is emacs --quick --load spine.el.


Task 1: Create the hello.mustache template

Files:

  • Create: templates/hello.mustache

  • Step 1: Create templates/ directory and hello.mustache

mkdir -p templates

Write templates/hello.mustache:

<!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>
  • Step 2: Verify template file exists

Run: cat templates/hello.mustache Expected: outputs the template content above.

  • Step 3: Commit
git add templates/hello.mustache
git commit -m "feat: add hello.mustache smoke-test template"

Task 2: Write spine.el entry point

Files:

  • Create: spine.el

  • Step 1: Write the full spine.el

Write spine.el:

;;; spine.el — personal reading tracker, served from Emacs

;; --- package bootstrap ------------------------------------------------

(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)))

(require 'simple-httpd)
(require 'mustache)

;; --- configuration ----------------------------------------------------

(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.")

;; --- template 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))

;; --- handlers ---------------------------------------------------------

(defservlet hello text/html ()
  (insert (spine-render "hello.mustache"
            (ht ("title"   "Spine")
                ("message" "Hello from Emacs.")))))

;; --- start ------------------------------------------------------------

(setq httpd-port spine-port)
(httpd-start)
(message "Spine listening on http://localhost:%d" spine-port)

(provide 'spine)
;;; spine.el ends here
  • Step 2: Verify the file has no syntax errors by loading it with Emacs in batch mode

Run: emacs --quick --batch --load spine.el Expected: no errors printed to stderr. The server starts briefly then Emacs exits (batch mode runs the file then quits). You should see something like:

Spine listening on http://localhost:8080

If a package is missing, you'll see MELPA refresh output followed by install messages — that's fine, it means the bootstrap worked.

  • Step 3: Commit
git add spine.el
git commit -m "feat: add spine.el — package bootstrap, server, hello handler"

Task 3: Smoke test — first launch (package install)

Files:

  • No changes — verification only.

Prerequisite: Delete any previously installed simple-httpd and mustache packages so we test the bootstrap path. Run:

rm -rf ~/.emacs.d/elpa/simple-httpd-* ~/.emacs.d/elpa/mustache-* ~/.emacs.d/elpa/ht-*

If you're using a custom package-user-dir, adjust the paths accordingly. If you don't want to nuke your real packages, set package-user-dir to a temp directory for this test:

mkdir -p /tmp/spine-elpa
emacs --quick --eval "(setq package-user-dir \"/tmp/spine-elpa\")" --load spine.el

(Use whichever approach fits your setup — the key is testing the fresh-install path.)

  • Step 1: Start Emacs with spine.el (fresh package state)

Run (in one terminal, Emacs stays running):

emacs --quick --load spine.el

Expected output in the *Messages* buffer:

  • MELPA archive refresh output (first-time only)

  • Package install messages for simple-httpd, mustache, ht

  • Spine listening on http://localhost:8080

  • Step 2: Verify /hello endpoint

In another terminal:

curl -s http://localhost:8080/hello

Expected: HTML output containing <h1>Hello from Emacs.</h1> and <title>Spine</title>.

  • Step 3: Stop Emacs

In the Emacs terminal, press C-x C-c to quit.


Task 4: Smoke test — second launch (no package install)

Files:

  • No changes — verification only.

  • Step 1: Start Emacs again (packages already installed)

emacs --quick --load spine.el

Expected: no MELPA refresh output, no package install messages. Only Spine listening on http://localhost:8080.

  • Step 2: Verify /hello still works
curl -s http://localhost:8080/hello

Expected: same HTML as before.

  • Step 3: Stop Emacs (C-x C-c)

Task 5: Final commit

Files:

  • No code changes — just confirming all commits are done.

  • Step 1: Verify git status is clean

git status

Expected: nothing to commit, working tree clean

If anything shows as modified, stage and commit it:

git add -A
git commit -m "chore: finalize initial scaffold"
  • Step 2: Review commit log
git log --oneline

Expected: 24 commits covering the template, spine.el, and any fixes.