Skip to content
Rebuilding

Mnemo is being rebuilt, so downloads are paused until the new app ships. We are porting from Avalonia to React and Photino and redrawing every screen, so the screenshots here are a mix of the old app and the new one.

Follow along
Browse docs

The local API

A loopback-only server, a per-launch token, and one event stream.

The web UI and the backend are one app on one machine, and the API is built to make sure that stays true. There is no remote server, no CORS, and no way to reach the API from another device by design.

Loopback only, token always

Kestrel binds to 127.0.0.1 exclusively: an ephemeral port in production, a fixed one (47210) in dev. Every API request except the health check must carry a bearer token minted fresh at each launch; a Host-header guard additionally rejects DNS-rebinding tricks. The result is that even other software on the same machine cannot casually talk to your Mnemo.

How the SPA gets the token differs by mode, and the SPA code never has to care:

  • Production: the Host serves the built SPA itself and templates the token into index.html as it goes out.
  • Dev: the Host writes { port, token } to mnemo-web/.dev/api.json, and the Vite proxy injects the Authorization header on every proxied request.

The event stream

Server-to-client push is one SSE channel, GET /api/events. The Host publishes typed events (toasts, shutdown, and more as the port grows) and the SPA maps each type to state in a single dispatch switch. Chat streaming is separate: each assistant turn streams its own SSE response with deltas, tool calls, and status events.

Shutdown is a handshake

Closing the window does not kill the process mid-save. The first close request is held, a shutdown event goes out over the stream, and the SPA gets a short grace period to flush pending saves and reply that it is ready. Pressing close a second time overrides the wait.

Adding an endpoint

Endpoints live with their feature under Mnemo.Host (for example Mnemo.Host/Flashcards), as minimal-API endpoints mapped feature by feature over services resolved from DI. The pattern to copy is any existing feature folder: contracts in, service call, DTO out, nothing clever in the route handler itself.