Context
Follow-up to #10727 (auth proxy-identity) + #10723 (embedding provider observability). Surfaced during cross-family review of PR #10768 by @neo-opus-4-7 (review comment).
The Memory Core healthcheck currently surfaces three observability blocks: database.topology (#10127), identity (#10176), and (post-merge of PR #10767) providers.embedding (#10723). These let operators verify topology + identity + embedding-provider configuration empirically without inspecting logs or re-running config through node -e.
The auth dimension is currently NOT observable in healthcheck. With #10727 introducing auth.trustProxyIdentity (proxy-identity injection path), operators have two distinct auth paths (OIDC vs proxy-header) plus a single-tenant fallthrough — and no surfaced way to verify which path is active at boot.
The Problem
A misconfiguration like NEO_AUTH_TRUST_PROXY_IDENTITY=true set without a fronting proxy actually being deployed is undetectable from outside the MC server until requests start failing in non-obvious ways. Symmetric observability (already established for embedding-provider in #10723) closes this gap for the auth dimension.
The Architectural Reality
ai/mcp/server/memory-core/services/HealthService.mjs — currently exposes buildIdentityBlock (#10176), buildTopologyBlock (#10127), and buildEmbeddingProviderBlock (#10723, in PR #10767). All are module-scope pure projections with isolated unit-test coverage.
aiConfig.auth.{host, port, realm, issuerUrl, clientId, clientSecret, trustProxyIdentity} — the auth-config fields the new block reads
Server.mjs#buildRequestContext — propagates source: 'oidc' | 'proxy-header' into RequestContext; the healthcheck block reports the configured path-availability, not per-request observed source-tag (that's separate post-runtime observability)
The Fix
Add buildAuthProviderBlock(cfg) module-scope pure projection function to HealthService.mjs, wired into the healthcheck payload as providers.auth. Reports:
"providers": {
"embedding": { "...as today..." },
"auth": {
"configured": "oidc" | "proxy-header" | "unconfigured",
"oidc": {
"issuerUrl": "...",
"host": "...",
"configured": true | false
},
"proxyHeader": {
"trusted": true | false,
"headersChecked": ["x-preferred-username", "x-auth-request-preferred-username"]
}
}
}
Field semantics:
configured: which path is primary — 'oidc' if aiConfig.auth.host and aiConfig.auth.issuerUrl are populated; 'proxy-header' if those are unset AND trustProxyIdentity=true; 'unconfigured' otherwise (single-tenant fallthrough — local dev only).
oidc: detailed OIDC config visibility (does NOT expose clientSecret)
proxyHeader: trust state + which headers are read
Acceptance Criteria
Contract Ledger (T3)
Per parent epic #10721 AC2 (Contract Completeness Gate). Matrix added 2026-05-06 in response to @neo-gpt's hand-back (suggested row encoded verbatim with minor editorial adjustments).
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
providers.auth healthcheck payload |
#10770, #10721 AC2 + AC6, #10727 / PR #10768 auth substrate, PR #10785 401 gate, SharedDeployment.md ## Authentication section |
Report configured auth posture as 'oidc', 'proxy-header', or 'unconfigured'. Expose non-secret OIDC readiness fields (host, issuerUrl, realm, configured: bool) and proxy-header trust + canonical header list (x-preferred-username, x-auth-request-preferred-username). OIDC wins when both OIDC + proxy identity are configured (matches Server.mjs#buildRequestContext runtime precedence). |
Never expose clientSecret (verified by JSON.stringify probe in tests). 'unconfigured' reports explicit local-dev / single-tenant fallthrough. Missing proxy headers in 'proxy-header' mode remain a runtime 401 (PR #10785), not a healthcheck failure — healthcheck shows configured posture, runtime gate fires when prerequisite absent. Partial OIDC (e.g. host without issuerUrl) projects to 'unconfigured'. |
learn/agentos/SharedDeployment.md ## Healthcheck Verification subsection extended with providers.auth JSON sample + auth diagnostic-fields explanation. MemoryCore.md healthcheck response shape unchanged (extension is additive). |
test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs covering 6 cases: OIDC-only, proxy-only, both-configured (OIDC wins), unconfigured fallthrough, clientSecret non-leak guard (JSON.stringify probe + property check), partial OIDC fallthrough. Targeted run: npx playwright test test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs --reporter=line. Live healthcheck verification via the healthcheck MCP tool against a configured local Keycloak (operator-side L3, out of unit-test scope). |
Out of Scope
- Per-request source-tag audit (separate post-runtime observability concern)
- Healthcheck-driven auth-misconfiguration auto-recovery (operator-territory)
- Full OIDC-issuer-reachability check (network probe; out of scope for static config projection)
Related
- Adjacent shipped: PR #10767 (
providers.embedding precedent), PR #10768 (auth proxy-identity injection — the substrate this observability covers), PR #10769 (auth docs), PR #10785 (401 gate)
- Pattern precedents: #10176 (
buildIdentityBlock), #10127 (buildTopologyBlock), #10723 (buildEmbeddingProviderBlock), #10724 (buildSummaryProviderBlock)
- Parent epic: #10721 (Shared deployment MVP completeness gaps) — this is post-trial-readiness polish, not blocking
- Sibling parallel-track: #10773 (
providers.neoEmbedding SQLite-side symmetric block — same pattern applied to the SQLite-side embedding selector)
Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7
Retrieval Hint: query_raw_memories(query="healthcheck providers auth observability OIDC trustProxyIdentity proxy-header symmetric embedding 10727 10723 10768")
Context
Follow-up to #10727 (auth proxy-identity) + #10723 (embedding provider observability). Surfaced during cross-family review of PR #10768 by @neo-opus-4-7 (review comment).
The Memory Core healthcheck currently surfaces three observability blocks:
database.topology(#10127),identity(#10176), and (post-merge of PR #10767)providers.embedding(#10723). These let operators verify topology + identity + embedding-provider configuration empirically without inspecting logs or re-running config throughnode -e.The auth dimension is currently NOT observable in healthcheck. With #10727 introducing
auth.trustProxyIdentity(proxy-identity injection path), operators have two distinct auth paths (OIDC vs proxy-header) plus a single-tenant fallthrough — and no surfaced way to verify which path is active at boot.The Problem
A misconfiguration like
NEO_AUTH_TRUST_PROXY_IDENTITY=trueset without a fronting proxy actually being deployed is undetectable from outside the MC server until requests start failing in non-obvious ways. Symmetric observability (already established for embedding-provider in #10723) closes this gap for the auth dimension.The Architectural Reality
ai/mcp/server/memory-core/services/HealthService.mjs— currently exposesbuildIdentityBlock(#10176),buildTopologyBlock(#10127), andbuildEmbeddingProviderBlock(#10723, in PR #10767). All are module-scope pure projections with isolated unit-test coverage.aiConfig.auth.{host, port, realm, issuerUrl, clientId, clientSecret, trustProxyIdentity}— the auth-config fields the new block readsServer.mjs#buildRequestContext— propagatessource: 'oidc' | 'proxy-header'into RequestContext; the healthcheck block reports the configured path-availability, not per-request observed source-tag (that's separate post-runtime observability)The Fix
Add
buildAuthProviderBlock(cfg)module-scope pure projection function toHealthService.mjs, wired into the healthcheck payload asproviders.auth. Reports:"providers": { "embedding": { "...as today..." }, "auth": { "configured": "oidc" | "proxy-header" | "unconfigured", "oidc": { "issuerUrl": "...", "host": "...", "configured": true | false }, "proxyHeader": { "trusted": true | false, "headersChecked": ["x-preferred-username", "x-auth-request-preferred-username"] } } }Field semantics:
configured: which path is primary —'oidc'ifaiConfig.auth.hostandaiConfig.auth.issuerUrlare populated;'proxy-header'if those are unset ANDtrustProxyIdentity=true;'unconfigured'otherwise (single-tenant fallthrough — local dev only).oidc: detailed OIDC config visibility (does NOT exposeclientSecret)proxyHeader: trust state + which headers are readAcceptance Criteria
buildAuthProviderBlock(cfg)added toHealthService.mjsas module-scope pure projectionproviders.authblock wired into healthcheck payload (alongsideproviders.embedding)clientSecretvalue leaks into the healthcheck output (security-relevant — secret should not be in observability surface)learn/agentos/SharedDeployment.mdupdated to document the new observability surface (similar shape to the providers.embedding documentation in PR #10767)Contract Ledger (T3)
Per parent epic #10721 AC2 (Contract Completeness Gate). Matrix added 2026-05-06 in response to @neo-gpt's hand-back (suggested row encoded verbatim with minor editorial adjustments).
providers.authhealthcheck payloadSharedDeployment.md## Authenticationsection'oidc','proxy-header', or'unconfigured'. Expose non-secret OIDC readiness fields (host,issuerUrl,realm,configured: bool) and proxy-header trust + canonical header list (x-preferred-username,x-auth-request-preferred-username). OIDC wins when both OIDC + proxy identity are configured (matchesServer.mjs#buildRequestContextruntime precedence).clientSecret(verified by JSON.stringify probe in tests).'unconfigured'reports explicit local-dev / single-tenant fallthrough. Missing proxy headers in'proxy-header'mode remain a runtime 401 (PR #10785), not a healthcheck failure — healthcheck shows configured posture, runtime gate fires when prerequisite absent. Partial OIDC (e.g.hostwithoutissuerUrl) projects to'unconfigured'.learn/agentos/SharedDeployment.md## Healthcheck Verificationsubsection extended withproviders.authJSON sample + auth diagnostic-fields explanation.MemoryCore.mdhealthcheck response shape unchanged (extension is additive).test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjscovering 6 cases: OIDC-only, proxy-only, both-configured (OIDC wins), unconfigured fallthrough,clientSecretnon-leak guard (JSON.stringify probe + property check), partial OIDC fallthrough. Targeted run:npx playwright test test/playwright/unit/ai/mcp/server/memory-core/services/HealthService.spec.mjs --reporter=line. Live healthcheck verification via thehealthcheckMCP tool against a configured local Keycloak (operator-side L3, out of unit-test scope).Out of Scope
Related
providers.embeddingprecedent), PR #10768 (auth proxy-identity injection — the substrate this observability covers), PR #10769 (auth docs), PR #10785 (401 gate)buildIdentityBlock), #10127 (buildTopologyBlock), #10723 (buildEmbeddingProviderBlock), #10724 (buildSummaryProviderBlock)providers.neoEmbeddingSQLite-side symmetric block — same pattern applied to the SQLite-side embedding selector)Origin Session ID: 23b9cbcd-4938-4a46-b21a-0d48dd12e7e7
Retrieval Hint:
query_raw_memories(query="healthcheck providers auth observability OIDC trustProxyIdentity proxy-header symmetric embedding 10727 10723 10768")