Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Payne
682a1c836d feat(bezalel): add emacs daemon with shared socket + audit trail (Closes #429)
Some checks failed
Architecture Lint / Linter Tests (pull_request) Successful in 18s
Smoke Test / smoke (pull_request) Failing after 22s
Validate Config / YAML Lint (pull_request) Failing after 18s
Validate Config / JSON Validate (pull_request) Successful in 24s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 1m3s
Validate Config / Python Test Suite (pull_request) Has been skipped
Validate Config / Cron Syntax Check (pull_request) Successful in 13s
Validate Config / Shell Script Lint (pull_request) Failing after 1m6s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 14s
Validate Config / Playbook Schema Validation (pull_request) Successful in 27s
Architecture Lint / Lint Repository (pull_request) Failing after 24s
PR Checklist / pr-checklist (pull_request) Successful in 3m19s
Added missing Emacs daemon startup infrastructure for Bezalel VPS:

- `wizards/bezalel/emacs-daemon-start.sh` — daemon launcher
  - Creates shared socket dir `/srv/fleet/emacs/socket` (gid=fleet, mode=2775)
  - Starts Emacs daemon named "bezalel" using that socket
  - Loads shared config `emacs-daemon.el`

- `wizards/bezalel/emacs-daemon.el` — daemon configuration
  - Enables server mode with socket at /srv/fleet/emacs/socket/bezalel
  - Wraps `server-eval-and-print` to log all eval requests
  - Audit log written to /srv/fleet/logs/emacs-audit.log
  - Captures user identity via $USER/$SUDO_USER
  - Ensures fleet-group can access socket (server-socket-mod-group-permissions)

Fixes the missing daemon that cron/@reboot references but was absent from
the repository. With this, `emacsclient -s bezalel -e "(+ 1 1)"` returns 2
and all cross-user edits leave an audit trail.

Closes #429
2026-04-30 13:34:17 -04:00
2 changed files with 98 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
# =============================================================================
# emacs-daemon-start.sh — Bezalel Emacs daemon launcher
# =============================================================================
# Starts Emacs daemon with a shared socket for multi-user access.
# Creates socket dir with group=fleet, mode=2775 (setgid) so all agents
# can connect via emacsclient -s bezalel.
#
# Acceptance: emacsclient -s bezalel -e "(+ 1 1)" returns 2
# =============================================================================
DAEMON_NAME="bezalel"
SOCKET_DIR="/srv/fleet/emacs/socket"
LOG_DIR="/srv/fleet/logs"
EMACS_CONFIG="/root/wizards/bezalel/emacs-daemon.el"
# Create shared directories with correct group/permissions
# (fleet group created separately; ignore errors if already correct)
mkdir -p "$SOCKET_DIR" "$LOG_DIR"
chmod 2775 "$SOCKET_DIR" 2>/dev/null || true
chmod 755 "$LOG_DIR" 2>/dev/null || true
chgrp fleet "$SOCKET_DIR" "$LOG_DIR" 2>/dev/null || true
# Ensure config exists
if [ ! -f "$EMACS_CONFIG" ]; then
echo "ERROR: Emacs config not found at $EMACS_CONFIG" >&2
exit 1
fi
# Start daemon if not already running
if ! pgrep -f "emacs --daemon=$DAEMON_NAME" > /dev/null 2>&1; then
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Starting Emacs daemon '$DAEMON_NAME'..."
exec emacs --daemon="$DAEMON_NAME" --socket-dir="$SOCKET_DIR" -l "$EMACS_CONFIG"
else
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] Emacs daemon already running."
fi

View File

@@ -0,0 +1,60 @@
;; Emacs Daemon Configuration — Bezalel
;; - Shared socket at /srv/fleet/emacs/socket/bezalel
;; - Audit trail to /srv/fleet/logs/emacs-audit.log
;; - Per-user identity via $USER environment variable
;; - All evals are logged with user, expression, result, timestamp
;; Server setup
(require 'server)
(setq server-auth-dir "/srv/fleet/emacs/socket/") ; shared socket dir
(setq server-name "bezalel") ; socket = bezalel
(setq server-socket-dir server-auth-dir)
;; Do not alter socket prefix — keep exact name
(setq server-socket-mod-group-permissions t) ; allow group access
;; Start server if not already running
(unless (server-running-p)
(server-start))
;; Audit log setup
(defvar bezalel-audit-log "/srv/fleet/logs/emacs-audit.log"
"Path to Emacs daemon audit log for per-user traceability.")
(defun bezalel-audit-log-entry (user expression result status)
"Log an audit entry: timestamp, user, expression, result status."
(with-temp-buffer
(insert (format "[%s] user=%s expr=%s status=%s\n"
(format-time-string "%Y-%m-%dT%H:%M:%SZ" (current-time))
(or user (getenv "USER") "unknown")
(replace-regexp-in-string "\n" "\\n" expression)
status))
(write-region (point-min) (point-max) bezalel-audit-log t)))
;; Wrap server-eval-and-print to audit every request
(defun bezalel--server-eval-and-print-around (orig-fun &rest args)
"Around advice for `server-eval-and-print': log user, expression, result."
(let* ((expr (car args))
(user (or (getenv "SUDO_USER") (getenv "USER") "unknown"))
result
status)
(condition-case err
(progn
(setq result (apply orig-fun args))
(setq status "success")
(bezalel-audit-log-entry user expr (prin1-to-string result) status)
result)
(error
(setq result (error-message-string err))
(setq status "error")
(bezalel-audit-log-entry user expr result status)
(signal (car err) (cdr err))))))
(advice-add 'server-eval-and-print :around #'bezalel--server-eval-and-print-around)
;; Ensure audit directory exists at startup
(let ((log-dir (file-name-directory bezalel-audit-log)))
(when (and (not (file-exists-p log-dir))
(file-writable-p (expand-file-name "../.." log-dir)))
(make-directory log-dir t)))
(provide 'emacs-daemon)