Skip to content

Repository files navigation

Apricity

Apricity

A local, zero-dependency front-end for MemPalace — browse, search, write, edit, and curate your personal-memory store and knowledge graph from your browser.

License: MIT PyPI Python 3.11+ No runtime dependencies Local-first Latest release

Design

What is Apricity?

MemPalace gives your AI assistant a structured, durable memory — wings, rooms, drawers, and a knowledge graph, all stored locally. Apricity is the front-end for it. Once you have hundreds of memories, you need a way to see, search, and curate them without dropping to SQL or living inside an MCP shell. That's what Apricity is for.

It is not a memory system of its own — it reads and writes an existing MemPalace install on the same machine, through the official mempalace package. Think of it as the window onto your palace.

  • 🏠 Local-first. Binds to 127.0.0.1. No telemetry, cloud account, or memory-data upload. The optional release check can be disabled.
  • 🪶 Zero runtime dependencies. Pure-Python standard-library server + plain HTML / CSS / vanilla JS front-end. No pip install, no npm, no Docker, no build step.
  • 🛡️ Safe by construction. Every write goes through the official mempalace Python package. Every destructive action is snapshotted and recoverable. ETag concurrency control prevents lost edits.
  • Fast. Reads the SQLite/Chroma backends directly; a localStorage cache, gzip, and serve-time minification make reloads feel instant.
  • ⌨️ Keyboard-driven. ⌘K to search, E to edit, F to maximize/browse — core actions are remappable.

⚠️ Apricity expects an installed and initialised MemPalace instance on the same machine. It does not ship MemPalace itself — install it first from MemPalace/mempalace.

Contents

Features

Browse & navigate

Apricity's search bar: a rotating ring sweeps the border while a query is typed in
The search bar's always-on rotating ring — Apricity's “alive and listening” affordance. ⌘K from anywhere.
  • Search across content and metadata, filter by author, and sort by date or title (with a configurable default). ⌘K focuses it from anywhere; Enter jumps to the first match.
  • Three panes — a Rooms sidebar (wings → rooms, iOS-style drill-down), a Memories list, and a Detail view.
  • Browse mode — maximize the Memories panel into an adaptive multi-column grid; toggle with F.
  • Wiki-style links[[drawer_id|Display Title]] references in memory content become clickable jumps to the linked memory, with one-click back-navigation.
  • URL state — the current wing / room / memory / query / sort survive reloads and are linkable.

Write & edit

Apricity's inline editor: a circular formatting toolbar applying bold and italic, a Markdown heading autoformatting, and a callout block
The inline WYSIWYG editor — circular toolbar, live bold/italic, ## → heading autoformat, and callouts. No modal, no raw-Markdown round-trip.
  • Inline visual editor — a WYSIWYG editor for bold/italic, headings, lists, links, and callouts, with Markdown autoformatting and HTML→Markdown conversion on save. No modal, no raw-Markdown round-trip.
  • Edit metadata — change content, title, wing, and room, or move a memory between rooms, with content-hash ETag protection against concurrent edits.
  • Optimistic saves — edits apply instantly and revert automatically (with a notification and a retry affordance) if the server rejects them.
  • Drafts inbox — stage partial memories, edit them in place, and file them into the palace when ready.

Curate & recover

Apricity's delete-and-recover flow: a memory card slides from Memories into Recently deleted, gains a Restore button, then slides back
Nothing is ever lost — every delete is snapshotted into Recently deleted, one click from restore.
  • Delete a single memory, a whole room, or a whole wing — every delete is snapshotted to a recoverable log.
  • Recently deleted — restore a deleted memory with one click (optimistically), or purge the log.
  • Knowledge graph — view triples, add new facts, invalidate stale ones, and explore a force-directed graph visualisation.
  • Tunnels — cross-room / cross-wing links surfaced inline as room chips; create and delete them, bind them to specific memories, and inspect them on a dedicated tunnel page.

Stay in the loop

Apricity's notification bell ringing as toasts from Claude, Codex, and Gemini cascade in, each tagged created or updated
The bell rings as each model files or revises a memory — per-model avatars, created vs updated at a glance.
  • Notifications — a bell that surfaces newly created and updated memories with the originating model's avatar (Claude, Codex, …), distinguishing created from updated.
  • Recently-updated markers — memories touched recently get a relative-time marker that clears once you've viewed them.
  • Live polling — optional background refresh (15 / 30 / 60 s) that pauses when the tab is hidden, with an optional sound and LAN-wide "seen" sync so dismissing on one device clears it everywhere.

Make it yours

Apricity's Auto / Light / Dark theme switcher morphing a miniature interface between light and dark
Auto / Light / Dark — follow the system or pick a side; your choice is persisted.
  • Tools panel — a power-user sheet that surfaces MemPalace's MCP tools: knowledge-graph query + timeline, tunnels (list, create, delete, find, follow, traverse), diary (read / write), stats, and maintenance (taxonomy, duplicate check, hook settings, sync, reconnect, AAAK spec). Hidden by default; enable it in Settings. See the API reference for the underlying endpoints.
  • Settings — a redesigned sheet with sidebar navigation: Display (theme, reduce-motion, relative time, title/name polishing, panel-control reveal), Notifications, remappable keyboard Shortcuts, Trash, Backup, Account, and an About pane with an update-available check.
  • Theme — Auto / Light / Dark, overriding or following the system preference, with your choice persisted.
  • Auth — fail-closed username + password protection with PBKDF2-SHA256 hashing and HTTP-only session cookies (12-hour default, 30-day "Remember me").

Quickstart

Prerequisites

  • Python 3.11 or newer (Apricity itself uses only the standard library — no pip install required).
  • A working MemPalace install with its own venv. Apricity shells out to it for writes, so it must be importable from $MEMPALACE_PYTHON_BIN.

Run from a clone (no install step)

The whole server is one file of standard-library Python, so the simplest way to run Apricity is straight from a checkout:

git clone https://github.com/epinethrone/apricity
cd apricity
python3 server.py            # equivalent to `python -m mempalace_dashboard`

The server prints a one-time setup secret. Open http://127.0.0.1:8765, go to Settings → Account, and enter that secret to create the owner account.

Install as a command

Prefer an isolated, on-PATH install? Use pipx (or uv tool install):

pipx install apricity
apricity

Note

Published on PyPI as apricitypip install apricity or uv tool install apricity work too.

There's no build step and no third-party runtime dependencies — the package is published purely so installing it is one command. If your MemPalace install lives somewhere non-standard, copy .env.example to .env (or export the variables in your shell) and adjust the paths before launching.

First-run checklist

  1. ✅ MemPalace itself is installed and you've successfully filed at least one memory through it.
  2. ~/.mempalace/palace/chroma.sqlite3 and ~/.mempalace/knowledge_graph.sqlite3 exist (or you've pointed MEMPALACE_PALACE_DB / MEMPALACE_KG_DB at where they actually live).
  3. MEMPALACE_PYTHON_BIN points at a Python that can import mempalace.
  4. ✅ You copied the setup secret from the server log, opened Settings → Account, and created the owner account.

Configuration

All filesystem locations default to the standard MemPalace home (~/.mempalace). Override with environment variables if your installation differs (see .env.example for a starter file):

Variable Default Purpose
PORT 8765 Port Apricity listens on.
MEMPALACE_HOST 127.0.0.1 Address Apricity binds to. A non-loopback value requires configured authentication.
MEMPALACE_ALLOWED_HOSTS (unset) Comma-separated additional hostnames accepted by Host-header validation, such as a reverse-proxy hostname.
MEMPALACE_HOME ~/.mempalace Root for all of Apricity's data.
MEMPALACE_PALACE_DB <HOME>/palace/chroma.sqlite3 The Chroma SQLite backend.
MEMPALACE_KG_DB <HOME>/knowledge_graph.sqlite3 The knowledge-graph SQLite database.
MEMPALACE_INBOX <HOME>/dashboard-inbox Where drafts are staged before filing.
MEMPALACE_VERSIONS <HOME>/dashboard-versions.jsonl Append-only log of deletions (powers Recently deleted).
MEMPALACE_CREDENTIALS <HOME>/dashboard-credentials.json Username + PBKDF2 hash. Mode 0600.
MEMPALACE_SESSIONS <HOME>/dashboard-sessions.json Active session tokens. Mode 0600.
MEMPALACE_PREFERENCES <HOME>/dashboard-preferences.json Server-side UI preferences (sort, theme, notification settings).
MEMPALACE_PYTHON_BIN ~/.local/share/mempalace-venv/bin/python Python that can import mempalace.
MEMPALACE_TOKEN (unset) Optional shared-secret used by scripts via the X-Auth-Token header. Coexists with the cookie flow.
MEMPALACE_SETUP_TOKEN random per startup Optional fixed first-run enrollment secret. Prefer the generated value printed at startup.
MEMPALACE_COOKIE_SECURE false Set to true when Apricity is served over HTTPS so session cookies carry Secure.
MEMPALACE_SYNC_ROOTS (unset) Roots allowed for caller-supplied /api/sync project directories, separated by the platform path separator (: on Linux/macOS). Explicit directories are disabled when unset.
APRICITY_DISABLE_UPDATE_CHECK false Set to true to disable the cached request to the GitHub releases API.

Securing Apricity

  1. Start the server and copy the generated setup secret from its startup log.
  2. Open the UI and go to Settings → Account.
  3. Enter the setup secret, choose a username and password (≥ 8 characters), and save.

Apricity fails closed before enrollment: palace endpoints remain inaccessible, and only the setup-secret-protected enrollment request is accepted. Missing, malformed, or unreadable credential files never enable anonymous access. Credentials are stored as pbkdf2_sha256$200000$<salt>$<hash> and never leave the host.

If you forget the password, delete ~/.mempalace/dashboard-credentials.json and ~/.mempalace/dashboard-sessions.json, restart the server, and re-enroll using the new setup secret printed at startup. This does not create an anonymous-access window.

For scripted access, set MEMPALACE_TOKEN=<some-secret> in the environment and send it as the X-Auth-Token header. The token is enforced even when no credential file exists and coexists with the cookie flow.

To listen beyond loopback, first configure an owner account or MEMPALACE_TOKEN, then set MEMPALACE_HOST explicitly. Apricity refuses a non-loopback bind without one of those authentication mechanisms. Prefer an HTTPS reverse proxy or authenticated tunnel, set MEMPALACE_ALLOWED_HOSTS to its hostname, and enable MEMPALACE_COOKIE_SECURE=true when the browser reaches Apricity over HTTPS.

Keyboard shortcuts

Four actions are remappable under Settings → Shortcuts (defaults shown); ⌘K and Esc are fixed.

Shortcut Action Remappable
F Maximize / Browse mode
E Edit the selected memory
X Close / collapse the current panel or sheet
Backspace Delete the focused item (with confirmation)
⌘K / Ctrl+K Focus the search box (inserts a link inside the editor)
Esc Close any open sheet — permanent fallback for X
Enter (in search) Jump to the first matching memory

Single-key shortcuts only fire when you're not typing in a field. Full details in the wiki.

API reference

All endpoints are JSON over HTTP. Auth is by session cookie (mempalace_session) or X-Auth-Token header when MEMPALACE_TOKEN is set. Every POST requires Content-Type: application/json. Cookie-authenticated mutations also require the session's X-CSRF-Token; the bundled UI handles this automatically. Token-authenticated scripts do not need a CSRF token.

Method Path Auth Purpose
GET /health open Liveness + whether auth is active.
GET /api/session open Current auth state.
POST /api/login open {username, password, remember} — issues a session cookie.
POST /api/logout auth Clears the session.
GET /api/palace auth Full palace snapshot (wings, drawers, triples, stats).
GET /api/search?q=… auth Filtered subset of drawers + triples.
GET /api/versions auth Recently deleted snapshots (powers Recently deleted).
POST /api/memories auth File a new memory.
POST /api/memories/update auth Patch content / wing / room of an existing drawer; supports ETag.
POST /api/delete auth Delete a drawer, room, or wing ({scope, …, confirm: "DELETE"}).
GET /api/drafts auth List drafts; ?id=<draft-id> returns one with body.
POST /api/drafts auth Save a new draft.
POST /api/drafts/update auth Replace a draft in place.
POST /api/drafts/delete auth Remove a draft.
POST /api/drafts/commit auth File a draft into the palace.
POST /api/versions/restore auth Recreate a deleted memory from a snapshot.
POST /api/versions/delete auth Remove one snapshot from the log.
POST /api/versions/clear auth Wipe the snapshot log ({confirm: "CLEAR"}).
POST /api/facts auth Add a knowledge-graph triple.
POST /api/facts/invalidate auth Mark a triple as ended.
GET /api/settings auth Whether credentials are configured + username.
POST /api/settings/credentials setup/auth Create credentials with the startup setup secret; rotate them with an authenticated session.
GET /api/kg/query?entity=…&direction=…&as_of=… auth Query KG facts about an entity.
GET /api/kg/timeline?entity=… auth Chronological view of an entity's facts.
GET /api/kg/stats auth Knowledge-graph aggregate stats.
GET /api/graph/stats auth Palace graph stats.
GET /api/taxonomy auth Current wing/room taxonomy tree.
GET /api/checkpoint auth Memories-filed-away state (recent checkpoint).
GET /api/aaak-spec auth AAAK spec reference.
GET /api/diary?agent_name=…&last_n=…&wing=… auth Read agent diary entries (latest last_n, capped at 200).
POST /api/diary auth Write a diary entry ({agent_name, entry, topic?, wing?}).
GET /api/tunnels?wing=… auth List tunnels (optionally filtered by wing).
POST /api/tunnels auth Create a tunnel ({source_wing, source_room, target_wing, target_room, label?, source_drawer_id?, target_drawer_id?}).
POST /api/tunnels/delete auth Delete a tunnel by ID. Snapshotted to the versions log before deletion.
GET /api/tunnels/find?wing_a=…&wing_b=… auth Find tunnels between two wings.
GET /api/tunnels/follow?wing=…&room=… auth List tunnels reachable from a room.
GET /api/traverse?start_room=…&max_hops=… auth Graph-traverse from a starting room (max_hops capped at 5).
POST /api/check-duplicate auth Check whether content is a near-duplicate ({content, threshold?}).
GET /api/hooks auth Current hook settings (silent-save, desktop-toast).
POST /api/hooks auth Update hook settings ({silent_save?, desktop_toast?}).
POST /api/sync auth Sync from a project tree ({apply?, wing?, project_dir?}). Caller-supplied directories must resolve beneath MEMPALACE_SYNC_ROOTS. Slow — runs with a 5-minute timeout.
POST /api/reconnect auth Force-reconnect the MemPalace backend.

Example — file a memory from the command line:

curl -X POST http://127.0.0.1:8765/api/memories \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: $MEMPALACE_TOKEN" \
  -d '{"wing":"notes","room":"general","title":"hello","content":"first memory from curl"}'

Safety model

  • No raw DB writes. Every mutation routes through the official mempalace Python package — Apricity never modifies SQLite directly.
  • Snapshot before destruction. Every delete (memory, room, wing, tunnel) appends a record to the versions log so the content is recoverable. Restoring recreates the memory through MemPalace with its original content, author, and source, plus a fresh ID and filing time. Edits aren't snapshotted as history — they're protected from accidental overwrite by ETag concurrency instead.
  • ETag concurrency. The edit form sends the content hash it was opened with; the server refuses to overwrite if it has changed.
  • Confirmation values. Bulk deletes and snapshot wipes require an exact-string confirmation in the request body.
  • Fail-closed authentication. Missing or invalid credentials do not grant palace access. First-run enrollment requires a generated setup secret, and login attempts are rate-limited.
  • Browser request isolation. Host-header validation blocks DNS rebinding. Mutations require same-origin requests, JSON content, and a per-session CSRF token. A restrictive Content Security Policy limits executable content.
  • No plaintext secrets. Credentials are hashed with PBKDF2-HMAC-SHA256 (200 000 iterations, 16-byte salt). Sessions are stored server-side; cookies are HttpOnly + SameSite=Lax, with optional Secure for HTTPS deployments.
  • Constrained maintenance paths. Explicit sync directories are disabled unless their allowed roots are configured; resolved paths and symlink targets must remain within those roots.
  • Loopback by default. The server binds to 127.0.0.1. Non-loopback binding is explicit and refused without authentication. If you put it behind a reverse proxy, terminate TLS there and keep the upstream on loopback where possible.

Architecture

┌────────────────────┐   HTTP/JSON   ┌───────────────────────┐  subprocess  ┌─────────────────┐
│ Browser (vanilla   │ ────────────► │ server.py             │ ───────────► │ mempalace venv  │
│ HTML + JS, no      │ ◄──────────── │ (stdlib only,         │ ◄─────────── │ (writes only)   │
│ build step)        │   snapshots   │  ThreadingHTTPServer) │              └─────────────────┘
└────────────────────┘               │                       │  read-only
                                     │                       │ ─────────────► palace/chroma.sqlite3
                                     │                       │ ─────────────► knowledge_graph.sqlite3
                                     └───────────────────────┘
  • Reads go directly against the SQLite files for speed.
  • Writes are dispatched to a child Python that imports mempalace — keeps Apricity in lock-step with the canonical schema.
  • State that belongs to Apricity (drafts, snapshots, credentials, sessions, preferences) lives in $MEMPALACE_HOME alongside the palace data so a single backup captures everything.

The Python import package is still named mempalace_dashboard (hence python -m mempalace_dashboard and the directory layout below) — only the distribution, command, and brand are apricity.

Troubleshooting

Apricity loads but the palace is empty

The UI rendered, but Wings: 0 and the list is empty.

  • Confirm ~/.mempalace/palace/chroma.sqlite3 exists. If it doesn't, MemPalace itself hasn't been initialised on this machine.
  • If your palace lives elsewhere, set MEMPALACE_PALACE_DB and MEMPALACE_KG_DB to the correct absolute paths and restart.
  • Check the server log — Apricity reports the exact paths it tried to open.
"mempalace package not found" when writing

Reads work, but every save / delete fails with a subprocess error mentioning ModuleNotFoundError: No module named 'mempalace'.

  • MEMPALACE_PYTHON_BIN points at a Python that can't import mempalace. Run it manually:
    $MEMPALACE_PYTHON_BIN -c "import mempalace; print(mempalace.__file__)"
  • Fix the path, or install mempalace into that venv, then restart Apricity.
I forgot my password
rm ~/.mempalace/dashboard-credentials.json ~/.mempalace/dashboard-sessions.json

Restart the server, copy the newly generated setup secret from its log, and re-enroll via Settings → Account. This does not touch your memories or expose them without authentication.

Port 8765 is already in use
PORT=9000 python3 server.py
I want to reach Apricity from another machine on my LAN

The server binds to 127.0.0.1 deliberately. Prefer a reverse proxy (Caddy, nginx, Tailscale Serve) that terminates TLS and forwards to the loopback port. Configure its hostname in MEMPALACE_ALLOWED_HOSTS and set MEMPALACE_COOKIE_SECURE=true.

Direct LAN binding is an explicit opt-in: configure an owner account or MEMPALACE_TOKEN, then start with MEMPALACE_HOST=0.0.0.0. Apricity refuses this mode without authentication. Never expose the raw port to the public internet.

FAQ

Is this an official MemPalace project? No. Apricity is a community front-end that talks to the official mempalace package. The MemPalace project itself lives at MemPalace/mempalace.

Does it work offline? Yes. The server is loopback-only by default and the front-end is vendored — no CDNs or analytics. The only optional outbound request is a cached GitHub releases check; disable it with APRICITY_DISABLE_UPDATE_CHECK=true.

Do my memories leave my machine? Memory content is never uploaded. Apricity reads and writes the local palace; the optional release check sends no palace data but does contact api.github.com, and can be disabled with APRICITY_DISABLE_UPDATE_CHECK=true.

Can I run multiple instances on one host? Yes — give each one its own PORT and (if you want isolated palaces) its own MEMPALACE_HOME.

Why no pip install requirement? The whole server is one file of standard-library Python. Keeping it dependency-free means you can audit it, vendor it, or run it on a fresh box without thinking about supply chains.

Can I script against it? Yes. Set MEMPALACE_TOKEN=… and pass it as X-Auth-Token. See API reference.

Contributing

Issues, ideas, and PRs are welcome. See CONTRIBUTING.md for the development loop, code conventions, and how to file a useful bug report.

If you're adding a feature, please open an issue first to talk through the shape of it — Apricity values being small and dependency-free, so not every feature is the right fit.

Security

If you find a vulnerability, please do not open a public issue. See SECURITY.md for the disclosure process.

License

MIT © Apricity contributors.

About

Apricity — a fast, local-first web UI for MemPalace: browse, search, edit & curate your AI agent's memory, knowledge graph & vector store. Zero-dependency, self-hosted, MCP-friendly.

Topics

Resources

Contributing

Security policy

Stars

12 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages