Skip to content

fix(dashboard): revalidate HTML app shell so obsidian card shows /obsidian/ not :3000#79

Merged
AlienWalker1995 merged 1 commit into
mainfrom
fix/obsidian-services-link
Jul 10, 2026
Merged

fix(dashboard): revalidate HTML app shell so obsidian card shows /obsidian/ not :3000#79
AlienWalker1995 merged 1 commit into
mainfrom
fix/obsidian-services-link

Conversation

@AlienWalker1995

Copy link
Copy Markdown
Owner

What the operator saw

The new obsidian row in the dashboard Services tab showed a raw :3000 instead of the clean /obsidian/ SSO link.

Discriminating evidence — it was NOT #78, it was browser cache

Live /api/services obsidian entry (container-to-container via caddy + X-Forwarded-Email) is structurally identical to codebase-memory-ui — the working precedent:

obsidian: { id, name:"Obsidian", port:3000, has_gpu:false, hint, ok:true, error:null }
  • No url key and the check URL is NOT echoed into the payload.
  • serviceOpenHref therefore takes the APP_PREFIX && SSO_ROUTES['obsidian'] branch → location.origin + '/obsidian/'.
  • The card renders only: name, the SSO Open link, Running/Offline, and (only when offline) a hint that says https://<host>/obsidian/:3000 appears nowhere.

So current code cannot produce :3000. The served index.html from #78 already contains obsidian: '/obsidian/' in SSO_ROUTES. The operator's browser was serving a cached pre-#78 shell (dashboard recreated ~21:25Z; the old shell had a url/no-obsidian-route and fell through to serviceUrl(host, s.port):3000).

Root cause (upstream, recurring — not a one-off)

Starlette's StaticFiles sends ETag + Last-Modified but no Cache-Control on index.html. Browsers then apply heuristic freshness and serve the stale app shell without revalidating. Every future dashboard rebuild (new SSO routes / service cards) risks the same stale-shell drift. Telling the operator to hard-refresh would be a per-incident bandaid that repeats forever.

Fix

Subclass StaticFiles to add Cache-Control: no-cache to HTML responses only. The shell stays cached but MUST revalidate its ETag every load, so new builds are picked up immediately; hashed static assets keep their long-cache behavior.

Tests (root tests/test_services_and_throughput.py)

  • test_index_html_sends_no_cache — HTML shell returns Cache-Control: no-cache and still carries an ETag.
  • test_obsidian_entry_is_sso_routed_like_codebase_memory — locks the obsidian entry to the SSO-routed shape (no url, port for the probe only, hint uses /obsidian/, never :3000), mirroring codebase-memory-ui.

Acceptance (validated live on ordo-v2, only dashboard rebuilt/recreated)

  • Served shell now carries cache-control: no-cache (ETag retained) + the obsidian: '/obsidian/' route.
  • /api/services obsidian entry clean; render branch → /obsidian/, no :3000 in any visible field.
  • /api/hardware still returns disk (1999.8 GB total) + both GPUs (GTX 1070 + RTX 5090) — env overrides persist via the manifest.
  • Dashboard healthy; obsidian / llamacpp / agent container IDs unchanged (only dashboard recreated).
  • Root ruff (dashboard/) clean; 118 passed, 4 skipped.

🤖 Generated with Claude Code

…obsidian :3000)

The operator saw the obsidian Services card showing a raw `:3000` instead of
the clean `/obsidian/` SSO link, even though PR #78 shipped correctly:
/api/services returns the obsidian entry with NO `url`/`check` echoed (only
id/name/port/hint/ok), and serviceOpenHref takes the `APP_PREFIX &&
SSO_ROUTES['obsidian']` branch -> `/obsidian/`. The card never renders `port`
as text. So current code cannot produce `:3000` — the browser was serving a
CACHED pre-#78 index.html.

Root cause: Starlette's StaticFiles sends ETag + Last-Modified but NO
Cache-Control on index.html, so browsers apply heuristic freshness and serve
the stale app shell without revalidating. Every dashboard rebuild (new SSO
routes / service cards) can therefore keep showing the old shell until a hard
refresh — a recurring drift, not a one-off. Fixing per-incident ("tell the
operator to hard-refresh") would repeat forever; the real fix is upstream.

Fix: subclass StaticFiles to add `Cache-Control: no-cache` to HTML responses
only. The shell stays cached but MUST revalidate its ETag every load, so new
builds appear immediately; hashed static assets keep long-cache behavior.

Tests: index.html sends Cache-Control: no-cache (+ retains ETag); obsidian
entry has the SSO-routed shape (no `url`, port for probe only, hint uses the
same-origin `/obsidian/` path, never `:3000`) — regression-locking the shape
against codebase-memory-ui.

Validated live on ordo-v2 (only dashboard rebuilt/recreated): served shell now
carries `cache-control: no-cache` + the obsidian SSO route; /api/services
obsidian entry unchanged & clean; /api/hardware still reports disk
(1999.8GB) + both GPUs (1070+5090); dashboard healthy; obsidian/llamacpp/agent
container IDs unchanged. Root ruff (dashboard/) + tests green (118 passed).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@AlienWalker1995
AlienWalker1995 merged commit ab0156c into main Jul 10, 2026
5 checks passed
@AlienWalker1995
AlienWalker1995 deleted the fix/obsidian-services-link branch July 10, 2026 03:12
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.

1 participant