Understanding the AgentMemory Real-Time Viewer Architecture
The AgentMemory real-time viewer is a lightweight HTTP front-end that combines a static file server, dynamic Content Security Policy injection, and a reverse proxy into a single Node.js process.
The rohitg00/agentmemory repository provides you with a real-time viewer that runs alongside the core AgentMemory service. This architecture eliminates cross-origin issues by exposing the full REST API through the same host and port as the web interface, making client-side development seamless.
Core Components of the Viewer
The viewer logic is completely isolated under src/viewer, allowing you to start or omit it without impacting the backend service. It consists of three tightly coupled subsystems.
HTTP Server Layer (src/viewer/server.ts)
The entry point creates a raw Node.js http server using createServer. This server handles three distinct responsibilities:
- Static UI delivery: Serves the viewer interface on the routes
/,/viewer, and/agentmemory/viewer - CORS handling: Applies a configurable allow-list via the
VIEWER_ALLOWED_ORIGINSenvironment variable - Request routing: Proxies all other requests to the AgentMemory REST API
By default, the viewer assumes the REST API runs on viewerPort - 2. If you start the viewer on port 3111, it automatically proxies API calls to port 3109.
HTML Rendering and Security (src/viewer/document.ts)
When you request the viewer UI, the server does not serve a raw static file. Instead, loadViewerTemplate reads viewer/index.html and injects dynamic security tokens:
- Per-request nonce: Generated via
createViewerNonceto prevent XSS attacks - Version metadata: Injected from
VERSIONconstant insrc/version.ts - CSP header: Built by
buildViewerCspto restrict script execution to resources matching the generated nonce
This ensures that even if the HTML template contains placeholders, only scripts with the correct nonce can execute during that specific request.
API Proxy Layer (src/viewer/server.ts)
The proxyToRestApi function acts as a thin reverse proxy with these specific behaviors:
- Path rewriting: Prepends
/agentmemory/...to incoming paths before forwarding - Method preservation: Forwards the original HTTP method and body for
POST,PUT,DELETE, andPATCHrequests - Header passthrough: Carries over the
Authorizationbearer token and other headers - Timeout protection: Uses an
AbortControllerwith a 10-second timeout to prevent hanging upstream calls - Response streaming: Returns the upstream status, body, and content-type while preserving CORS headers
Bootstrap and Configuration
You initialize the viewer using the startViewerServer function exported from src/viewer/server.ts. The signature accepts the AgentMemory KV store, SDK instance, and optional authentication parameters.
import { startViewerServer } from "./src/viewer/server.js";
// Start viewer on 3111, REST API expected on 3109
const viewer = startViewerServer(
3111, // viewer port
kvInstance, // AgentMemory KV store
sdkInstance, // iii-SDK instance
process.env.VIEWER_SECRET, // optional API auth secret
);
Once running, you can access the UI and proxy API calls through the same origin:
// Fetch the viewer HTML (with injected nonce)
const html = await fetch("http://localhost:3111/").then(r => r.text());
// Proxy a request to the underlying REST API
await fetch("http://localhost:3111/agentmemory/remember", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer SECRET"
},
body: JSON.stringify({ content: "Important memory" }),
});
Security Implementation Details
The security model relies on nonce-based CSP rather than complex authentication flows. The buildViewerCsp function, referenced through utilities in src/auth.js, constructs headers that tie the injected nonce to allowed script sources. This approach lets the viewer safely load dynamic scripts while mitigating injection attacks.
The optional VIEWER_SECRET parameter enables basic bearer token validation. When provided, the proxy layer validates the Authorization header before forwarding requests to the REST API.
Summary
- Single-process architecture: The viewer runs as one Node.js process alongside AgentMemory, not a separate service
- Three-layer design: HTTP server (
server.ts), document generator (document.ts), and proxy handler (proxyToRestApi) - Zero CORS configuration: By proxying API calls through the same port, you eliminate cross-origin browser restrictions
- Request-scoped security: Each page load generates a unique nonce via
createViewerNonceand enforces it throughbuildViewerCsp - Configurable ports: Default REST port is automatically calculated as
viewerPort - 2, customizable via therestPortparameter
Frequently Asked Questions
How does the real-time viewer determine the REST API port?
By default, the viewer assumes the AgentMemory REST API runs exactly two ports below the viewer port. If you start the viewer on port 3111, it proxies API requests to 3109. You can override this by passing a custom restPort argument to startViewerServer.
Is the Content Security Policy static or dynamic?
The CSP is dynamic and request-scoped. For every incoming request to the viewer routes, document.ts generates a fresh nonce via createViewerNonce and constructs a new CSP header via buildViewerCsp that only permits scripts matching that specific nonce.
Can I disable the viewer and run only the core AgentMemory service?
Yes. All viewer code lives under src/viewer and is completely isolated from core memory functions. You can omit calling startViewerServer entirely without affecting the underlying remember, recall, or forget operations.
What happens if the REST API takes longer than 10 seconds to respond?
The proxyToRestApi implementation uses an AbortController with a 10-second timeout. If the upstream AgentMemory API does not respond within this window, the proxy aborts the request and returns an error to the client, preventing the viewer from hanging indefinitely.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →