{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/memory-web",
  "slug": "products/memory-web",
  "kind": "capabilities",
  "bucket": "product",
  "title": "@leumas/memory-web — memory.leumas.tech",
  "name": "@leumas/memory-web",
  "eyebrow": "memory.leumas.tech",
  "chip": null,
  "summary": "memory.leumas.tech — knowledge banks a person owns, and the scoped agent tokens that let Cursor, Claude, Ollama and Leviathan read exactly the parts they were given.",
  "keywords": [
    "knowledge bank",
    "memory.leumas.tech",
    "agent token",
    "dynamic variable",
    "memory:// uri",
    "knowledge webhook",
    "document ingest"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# @leumas/memory-web — memory.leumas.tech\n\nKnowledge banks a person owns, and the scoped agent tokens that let Cursor, Claude, Ollama and\nLeviathan read exactly the parts they were given.\n\nA thin React shell. Everything it does is `/api/knowledge`, served by\n[`@leumas/knowledge-service`](../../shared/services/knowledge) — read that package first if you are\nchanging behaviour rather than layout.\n\n```\npnpm dev memory          # http://127.0.0.1:5088  (needs `pnpm dev api` for the API)\npnpm -F @leumas/memory-web build\npnpm smoke:memory        # the routes, over real HTTP\npnpm check:memory-access # the permission rules, against real SQLite\n```\n\n## Pages\n\n| Route | What it is |\n|---|---|\n| `/` | The front door. The only page that works signed out. |\n| `/banks` | Your banks, with inline creation. Built-in reference banks sit behind a second tab. |\n| `/banks/:id` | One bank — Content (with the drop surface), Variables, Push, Access. |\n| `/search` | Cross-bank search. Scoping is entirely server-side. |\n| `/agents` | Mint, list and revoke agent tokens. The secret is shown once. |\n| `/connect` | Paste-ready setup per client, plus the webhook and live-stream recipes. Public. |\n| `/activity` | What agents did, including what they were refused. |\n| `/pricing` | Reads `GET /membership/plans`. Never hard-codes a price. |\n\n## Four things that will bite you\n\n**Never introduce a base URL.** Every path in `src/api.js` is relative, because the httpOnly\n`leumas_session` cookie only travels same-origin. In production the Imperium worker proxies `/api`\nand `/auth` to leumas-api; in dev the Vite proxy forwards the same list. Setting `VITE_API_BASE`\nmakes every call cross-origin, the cookie is dropped, and the whole app renders as permanently\nsigned out with nothing in the console to explain it.\n\n**The site row is provisioned by the API, not by this deploy target.** `ensureMemorySite` runs at\nleumas-api boot. `pnpm deploy:memory` alone ships the frontend and leaves the subdomain 404ing until\nleumas-api is released too.\n\n**Media never travels as JSON.** A document is posted to `/banks/:bank/ingest` as `{ filename, text }`\nand parsed into items. An image or video goes to `/api/storage/upload` as multipart (512MB) and is\nthen LINKED to an item by reference. Putting a video in a JSON body is how upload broke in the media\nmanager.\n\n**`Button` takes no `as` prop.** It renders a real `<button>` and spreads the rest onto it, so\n`as={Link}` is silently ignored — the control renders, looks right, and does nothing. Navigate with\n`useNavigate`.\n\n## Ports\n\n`memory` is **5088** in `ops/infra/scripts/ports.mjs`, which both `dev.mjs` and `vite.config.js` read.\nIt was authored as 5080 on the branch it was built on; `workflows` has that number now.\n",
  "source": {
    "path": "products/leumas-memory/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 2939,
    "hash": "aea6c1a424e400e2b07cfdc824ebe46c8a45d512"
  },
  "urls": {
    "html": "/p/products/memory-web",
    "json": "/docs/products/memory-web.json",
    "md": "/docs/products/memory-web.md"
  },
  "links": {
    "composes": [
      "pkg:@leumas/leviathan",
      "pkg:@leumas/theme",
      "pkg:@leumas/ui"
    ],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  },
  "exports": null
}
