Skip to content

Viewer UI shows empty state when fronted by a reverse proxy on standard ports (80/443) — JS hardcodes :3113 #299

@flamerged

Description

@flamerged

Summary

When the viewer is fronted by a reverse proxy on the standard HTTP/HTTPS port (80/443), the dashboard renders but every panel shows the empty "first run" state ("0 sessions", "No sessions yet…", "0 memories", etc.) — even though the underlying KV has data and the proxied REST endpoints return that data correctly.

Root cause is in the viewer's port-detection logic: it hardcodes '3113' as the fallback REST port, so every browser-side fetch goes to <host>:3113 instead of the same origin the page was served from.

Our deployment context

We run agentmemory as a host-level systemd service on a small-production server. The viewer is intentionally loopback-bound (127.0.0.1:3113), and we expose it through our standard reverse-proxy convention:

  • A tiny socat bridge container forwards traffic from a non-loopback port (0.0.0.0:13113) to the host's 127.0.0.1:3113, since the proxy lives in a Docker bridge network that can't reach the host's loopback directly.
  • Nginx Proxy Manager terminates http://agentmemory.<internal-hostname>/ on port 80 and forwards to 172.18.0.1:13113 → socat → viewer.

This is the normal "self-hosted service behind a reverse proxy on a private network" pattern — it's how every other service on this host is reached. We expected it to "just work" with agentmemory too.

Reproduction

  1. Run agentmemory on a host. The viewer listens on 127.0.0.1:3113.
  2. Put any reverse proxy in front (nginx, Caddy, Traefik, NPM) that terminates on port 80 (or 443) and forwards /127.0.0.1:3113.
  3. Visit http://<proxy-hostname>/ in a browser.

Expected

The dashboard populates from the same origin's /agentmemory/* endpoints (which are already proxied alongside the HTML).

Actual

  • The viewer HTML loads cleanly.
  • The dashboard shows "First run → magical moment in 10 seconds / Seed sample data + prove semantic recall works".
  • Every counter is 0. "Recent Sessions: No sessions yet."
  • Browser dev-tools Network tab shows every /agentmemory/* request hitting <proxy-hostname>:3113 — a port that isn't reachable from outside the host (loopback-bound).

Meanwhile, curl http://<proxy-hostname>/agentmemory/sessions (no explicit port, same path the JS should hit) returns the populated session list correctly. The proxy chain is fine; only the JS URL construction is wrong.

Root cause

src/viewer/index.html (around lines 927–930 in v0.9.6):

var viewerPort = params.get('port') || window.location.port || '3113';
var REST = window.location.protocol + '//' + window.location.hostname + ':' + viewerPort;

When the page is served on port 80 (or 443), window.location.port is the empty string '', the fallback kicks in to the literal '3113', and the constructed REST URL is http://<host>:3113 — a port not exposed through the proxy.

The WS_URL / wsPort construction a few lines below (String(parseInt(viewerPort) - 1)) has the same issue, though it's only relevant when live updates are turned on.

Workaround (for anyone hitting this before a fix lands)

Append ?port=80 (or ?port=443 for HTTPS) to the URL:

http://<proxy-hostname>/?port=80

That makes params.get('port') win in the precedence chain, and every fetch goes to the same origin the page was served from. Works, but requires either remembering the query string or having the proxy auto-redirect / to /?port=80.

Suggested direction

When params.get('port') is absent and window.location.port is empty, construct the REST base from window.location.origin (i.e. don't append an explicit port) rather than falling back to the literal '3113'. Same logic for the WebSocket URL.

This would make the viewer "just work" behind any reverse proxy on standard ports, which is by far the most common self-hosted deployment shape.

Happy to provide more detail on the deployment or test a candidate fix on our side. Not opening a PR yet — wanted to flag the design question first in case the upstream preference is a different approach (env var, explicit base-URL config, etc.).


Plugin version: @agentmemory/agentmemory@0.9.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions