Skip to content

Dashboard sends no auth header — every data page 401s once OM_API_KEY is set #195

Description

@ViolinKaine

Summary (Co-Authored by Claude code)

The dashboard never sends an API key/auth header on any of its data requests to the
backend. Once OM_API_KEY is set — which in practice is required, since the server
rejects protected endpoints entirely when no key is configured at all (separate
issue, filing that too) — every single data-fetching page in the dashboard breaks:
Memories, Timeline/Activity, Decay, dashboard stats, etc. all show generic
"failed to fetch ..." errors, because the backend correctly 401s requests with no
credentials.

Root Cause

dashboard/lib/api.ts:

export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || '/api/openmemory'

export const getHeaders = (): { 'Content-Type': string } => {
    return {
        'Content-Type': 'application/json',
    }
}

getHeaders() is used by every page (memories/page.tsx, timeline/page.tsx,
decay/page.tsx, etc.) as the headers object for fetch() calls to the backend.
It never includes an Authorization or x-api-key header.

docker-compose.yml defines a NEXT_PUBLIC_API_KEY build arg and env var for the
dashboard service — presumably intended for exactly this purpose — but it's dead:

$ grep -rn "NEXT_PUBLIC_API_KEY" dashboard/ --include="*.ts" --include="*.tsx"
(no output — not referenced anywhere in dashboard source)

So there's a build arg wired all the way through docker-compose.yml
Dockerfile → the Next.js build, that gets baked into the client bundle... and then
is never actually read by any client code. getHeaders() should be sending it but
doesn't.

Separately: when NEXT_PUBLIC_API_URL isn't set, API_BASE_URL falls back to
/api/openmemory, which routes through dashboard/app/api/openmemory/[...path]/route.ts.
That proxy route reads a different env var (OPENMEMORY_API_URL, default
http://127.0.0.1:9432 — a port that matches neither the documented default
(8080) nor anything else in the repo) and does correctly attach the key server-side
via OPENMEMORY_API_KEY/OM_API_KEY — but docker-compose.yml never sets
OPENMEMORY_API_URL for the dashboard service either, so that path is also broken
out of the box unless a user manually adds it.

Reproducer

  1. Set a real OM_API_KEY (as recommended/required — see linked auth issue).
  2. docker compose --profile ui up per the repo's own compose file.
  3. Open dashboard → Memories (or Timeline, or the main dashboard stats view).
  4. Browser network tab: request to /memory/all (or wherever NEXT_PUBLIC_API_URL
    points) has no Authorization/x-api-key header.
  5. Backend responds 401 {"error":"authentication_required","message":"API key required"}.
  6. UI shows a generic "failed to fetch memories" / "failed to fetch activity" error
    with no indication it's an auth problem.

Suggested Fix

  • Make getHeaders() actually read and send NEXT_PUBLIC_API_KEY (e.g. as
    Authorization: Bearer ... or x-api-key, matching whatever the backend expects).
  • Wire OPENMEMORY_API_URL (pointing at the openmemory service's internal address,
    e.g. http://openmemory:8080 in the compose network) into docker-compose.yml for
    the dashboard service, so the /api/openmemory proxy path works without manual
    config too.
  • Fix the proxy route's fallback default (127.0.0.1:9432) to match the documented
    default port (8080), or remove the fallback and fail loudly instead of silently
    pointing somewhere wrong.

Happy to open a PR if a preferred direction (client-side header vs. server-side
proxy-only) is confirmed — right now there are two half-wired paths and neither
works unedited.

Related docs mismatch

.env.example says of OM_API_KEY: "Leave empty to disable authentication
(development only)". In practice, leaving it empty doesn't disable auth — the server
returns {"error":"auth_not_configured","message":"Server has no OM_API_KEY configured. Protected endpoints are unavailable."} on protected endpoints instead,
which is a different failure mode than "auth disabled." A real key is required
either way, so the docs comment is misleading as written.

Environment

  • openmemory-js / dashboard, current main
  • Docker deployment via the repo's own docker-compose.yml --profile ui
  • Confirmed via direct curl against the backend (with vs. without the key) that
    the backend's auth behavior itself is correct — this is purely a dashboard-side
    gap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions