Skip to content

fix: dashboard 404s on Linux due to Pulse vs PULSE path casing#1175

Merged
danielmiessler merged 1 commit into
danielmiessler:mainfrom
StarksLabs:fix/observability-path-casing-linux
May 20, 2026
Merged

fix: dashboard 404s on Linux due to Pulse vs PULSE path casing#1175
danielmiessler merged 1 commit into
danielmiessler:mainfrom
StarksLabs:fix/observability-path-casing-linux

Conversation

@StarksLabs

Copy link
Copy Markdown
Contributor

Summary

Releases/v5.0.0/.claude/PAI/PULSE/Observability/observability.ts hardcodes three filesystem paths using "Pulse" (TitleCase). The canonical install directory in v5.0.0 is "PULSE" (uppercase) — confirmed by the install.sh template and the Releases/v5.0.0/.claude/PAI/PULSE/ directory in this repo.

On macOS HFS+/APFS (case-insensitive default), Pulse and PULSE resolve to the same directory — bug is invisible. On Linux ext4/btrfs (case-sensitive), the dashboard directory is never found, every static-file lookup returns 404, and localhost:31337/ appears completely broken even though the HTTP server is bound and serving fine.

This is a Linux-portability regression — the daemon starts cleanly, the cron loop runs, voice notifications work — but the Life Dashboard is dark.

Reproduction (Linux ext4 / btrfs)

# Fresh PAI 5.0.0 install on Linux
cd ~/.claude/PAI/PULSE && bun run pulse.ts &

# Pulse starts and binds to port:
#   {"level":"info","msg":"HTTP server listening","port":31337}

curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:31337/
# HTTP 404
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:31337/agents
# HTTP 404
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:31337/index.html
# HTTP 404

Voice notify (POST /notify) works fine — only the dashboard's static-file serving is dead.

Root cause: serveStaticFile() calls getDashboardDir() which returns ~/.claude/PAI/Pulse/Observability/out (lowercase 'P'). That path doesn't exist on case-sensitive filesystems — the actual directory is ~/.claude/PAI/PULSE/Observability/out. So existsSafe() returns false and every page returns 404.

Fix

Three string changes — "Pulse""PULSE" — in observability.ts:

Line Context
69 DEFAULT_DASHBOARD_DIR constant
152 getDashboardDir() relative-path resolver
1650 handleUserIndexApi() user-index.json lookup

These are the only path-construction sites using "Pulse". The other "Pulse" references in the file (around lines 851/853/859/861) are string literals describing the daemon by name in tooling descriptions — those are correct and remain unchanged.

Test (Linux)

After patch:

$ curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:31337/
HTTP 200

$ curl -s http://localhost:31337/ | grep -oE '<title>[^<]*</title>'
<title>PAI Observatory</title>

$ for p in /agents /telos /health /work /security; do
    printf "%-15s " "$p"
    curl -s -o /dev/null -w "HTTP %{http_code}\n" "http://localhost:31337${p}"
  done
/agents          HTTP 200
/telos           HTTP 200
/health          HTTP 200
/work            HTTP 200
/security        HTTP 200

Risk

Very low.

  • Linux: previously broken → now works (intended fix).
  • macOS: behavior unchanged — case-insensitive HFS+/APFS already resolved "Pulse" to the canonical "PULSE" directory, so the same paths resolved fine before and after the change.
  • No semantic changes — only path string casing. No public API touched. No config schema changes.

…ility

Three hardcoded paths in the observability module used 'Pulse'
(TitleCase) while the canonical install directory is 'PULSE' (uppercase).
On macOS HFS+/APFS (case-insensitive default), 'Pulse' and 'PULSE'
resolve to the same directory, so the bug is invisible. On Linux
ext4/btrfs (case-sensitive), the dashboard directory is never found,
every static-file lookup returns 404, and the Pulse server appears
broken while the HTTP server is actually serving fine.

Symptom: localhost:31337 returns 404 for / and every dashboard page on
Linux; macOS users see no issue.

Affected paths:
- DEFAULT_DASHBOARD_DIR (line 69)
- getDashboardDir() relative-path resolver (line 152)
- handleUserIndexApi() user-index.json lookup (line 1650)

Behavior on macOS unchanged — case-insensitive FS already resolved
'Pulse' to the canonical 'PULSE' directory.

Signed-off-by: Gerald Starks <gerald.starks@icloud.com>
@danielmiessler danielmiessler merged commit e2ae840 into danielmiessler:main May 20, 2026
larsboes added a commit to larsboes/PAI that referenced this pull request Jun 10, 2026
…, PULSE casing

Selective integration of upstream danielmiessler/PAI (9fb9c862fde1bb):
- security: port execSync→execFileSync in tab-setter.ts (danielmiessler#1046), incl. the
  fork's extra cmux call sites + the kitten|jq pipe → execFileSync+JSON.parse,
  and replace `command -v` with `which`. Adds KITTY_LISTEN_ON socket validation.
- security: remove Midjourney/Discord integration from Art/Media skills (6 files)
  + strip stale "Midjourney" trigger keyword from Media SKILL.md descriptions.
- fix(PULSE): "Pulse"→"PULSE" directory casing across PULSE module — real bug on
  Linux/WSL2 case-sensitive FS (live dir is PULSE; refs were "Pulse") (danielmiessler#1259/danielmiessler#1175).
- bump .pai-fork/last-synced.ref → 2fde1bb.

Skipped (verified N/A to fork): plansDirectory danielmiessler#672 (no such key), case-colliding
danielmiessler#621 (no pai-observability-server), wiki Algorithm danielmiessler#1273 (fork casing consistent),
PAI-Install danielmiessler#1267 (fork doesn't deploy PAI-Install).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants