From b5858fe57f2f3d19d4bd4929426ceb45aee881e6 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 10:59:30 +0200 Subject: [PATCH 01/19] feat(self-host): add /lorekit-self-host skill, a public self-host docs page, and self-host-correct install.md Today's install.md points a self-hoster at the hosted project in nine places, a lorekit.io-minted token can't authenticate their own project, and the default deploy only ships mcp + health (missing memories, which hooks, remote CLI commands, and the dashboard REST API depend on). Adds a guided /lorekit-self-host runbook, a public /docs/self-host comparison page, and fixes install.md to use throughout with an all-functions deploy step. --- README.md | 7 +- docs/cli.md | 17 +- docs/install.md | 90 ++++--- packages/cli/README.md | 8 + packages/cli/skill/lorekit-self-host/SKILL.md | 225 ++++++++++++++++++ packages/schemas/src/llms/template.md | 6 +- packages/web/public/llms.txt | 7 +- .../web/src/content/docs/claude-code-web.mdx | 2 +- packages/web/src/content/docs/config.mdx | 2 +- packages/web/src/content/docs/deep-links.mdx | 2 +- packages/web/src/content/docs/grooming.mdx | 2 +- .../web/src/content/docs/organization.mdx | 2 +- packages/web/src/content/docs/private.mdx | 2 +- packages/web/src/content/docs/remote.mdx | 2 +- packages/web/src/content/docs/self-host.mdx | 118 +++++++++ packages/web/src/content/docs/tags.mdx | 2 +- packages/web/src/content/docs/use-cases.mdx | 2 +- packages/web/src/lib/docs/sections.ts | 2 + plugins/README.md | 2 +- .../skills/lorekit-self-host/SKILL.md | 225 ++++++++++++++++++ 20 files changed, 678 insertions(+), 47 deletions(-) create mode 100644 packages/cli/skill/lorekit-self-host/SKILL.md create mode 100644 packages/web/src/content/docs/self-host.mdx create mode 100644 plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md diff --git a/README.md b/README.md index 14c99a3a3..0261b3eb6 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,10 @@ lorekit install --completions auto # zsh / fish tab completion - `lorekit-groom` — tidies a noisy store: merges near-duplicates, lints weak lessons, sets expiry. Always proposes a plan before changing anything. +Also shipped in the Claude plugin, but **not** installed by `lorekit install` +(it's a one-time runbook, not everyday agent memory): `lorekit-self-host` — a +slash-only guided deployment walkthrough. Invoke it with `/lorekit-self-host`. + ## One store, every agent @@ -245,7 +249,8 @@ self-hosted instance sets its own limits. The whole stack — MCP server, dashboard, database — is in this repo under MIT, and deploys to your own Supabase and Vercel projects in about five minutes -([self-hosting guide](./docs/install.md)). Or skip the hosted store entirely +([self-hosting guide](./docs/install.md), or the walkthrough at +[lorekit.io/docs/self-host](https://lorekit.io/docs/self-host)). Or skip the hosted store entirely and keep lessons as markdown in your repo. diff --git a/docs/cli.md b/docs/cli.md index 3c740ddc5..ab918a417 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -485,12 +485,21 @@ on every TAB, so it is METERED, never traced). ## Skills shipped with the CLI -Ships three skills under `skill/*` — `lorekit-memory` (runtime read/write), `lorekit-setup` -(authoring a self-improvement loop into a host), and `lorekit-groom` (a propose-then-confirm +Ships four skills under `skill/*` — `lorekit-memory` (runtime read/write), `lorekit-setup` +(authoring a self-improvement loop into a host), `lorekit-groom` (a propose-then-confirm grooming pass over an accumulated store: survey→lint→dedupe/merge→expiry→prune, analysis via the CLI read commands and mutation via the `lorekit archive`/`delete`/`restore` commands or the `memory.*` -MCP tools); `install`/`uninstall`/`doctor` iterate the `SKILLS` list in `config.mjs`, and -`scripts/codegen/sync-plugin-skill.mjs` mirrors every `skill/*` into the plugin. +MCP tools), and `lorekit-self-host` (a slash-only guided deployment runbook). `install`/`uninstall`/`doctor` +iterate the `SKILLS` list in `config.mjs`, and `scripts/codegen/sync-plugin-skill.mjs` mirrors every +`skill/*` into the plugin regardless of whether it's in `SKILLS`. + +**`lorekit-self-host` is deliberately NOT in `SKILLS`.** `doctor.mjs` reports `fail` — "not found — +run lorekit install" — for every `SKILLS` entry missing from an install, so adding it there would turn +every existing install's `doctor` red until a re-install, and would scaffold a one-time deployment +runbook onto every hosted user who never needs it. It still reaches users two other ways: the Claude +plugin vendors it (`sync-plugin-skill.mjs` mirrors every directory under `skill/*`, not just the +`SKILLS` subset), and the npm tarball's `files` field includes `skill/`, so a non-plugin user can fetch +`skill/lorekit-self-host/SKILL.md` directly. ## Telemetry diff --git a/docs/install.md b/docs/install.md index 695325a4c..f9728e11f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,6 +2,9 @@ > This guide is structured so an AI agent can execute every step autonomously, or a developer can follow it manually in about 5 minutes. Each section is self-contained with exact commands. +> Prefer a guided run? Use `/lorekit-self-host`. Choosing between full +> self-host and BYOD? See https://lorekit.io/docs/self-host. + --- ## Prerequisites @@ -31,16 +34,16 @@ pnpm install ## Step 2 — Link to your Supabase project -Your project ref is the subdomain of your Supabase URL: `https://pqokxlhvnosogizsjztg.supabase.co` +Your project ref is the subdomain of your Supabase URL: `https://.supabase.co` ```bash -supabase link --project-ref pqokxlhvnosogizsjztg +supabase link --project-ref ``` Create a `.env.local` file in the repo root (needed for NX targets): ```bash -echo "SUPABASE_PROJECT_REF=pqokxlhvnosogizsjztg" > .env.local +echo "SUPABASE_PROJECT_REF=" > .env.local ``` --- @@ -60,7 +63,7 @@ This applies all migrations in `supabase/migrations/` and creates all required t 1. Go to https://github.com/settings/developers → OAuth Apps → New OAuth App - **Application name:** LoreKit (or any name) - **Homepage URL:** `https://.vercel.app` (or `http://localhost:3000` for local dev) - - **Authorization callback URL:** `https://pqokxlhvnosogizsjztg.supabase.co/auth/v1/callback` + - **Authorization callback URL:** `https://.supabase.co/auth/v1/callback` 2. Note the **Client ID** and generate a **Client Secret** 3. In Supabase → Auth → Providers → GitHub: enable and paste both values @@ -70,17 +73,24 @@ This applies all migrations in `supabase/migrations/` and creates all required t ```bash supabase secrets set \ - SUPABASE_URL=https://pqokxlhvnosogizsjztg.supabase.co \ + SUPABASE_URL=https://.supabase.co \ SUPABASE_ANON_KEY= \ SUPABASE_SERVICE_ROLE_KEY= \ + ALLOWED_ORIGINS=https:// \ OTEL_EXPORTER_OTLP_ENDPOINT=https://ingress.europe-west4.gcp.dash0-dev.com \ OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer " \ VERCEL_ENV=production \ - --project-ref pqokxlhvnosogizsjztg + --project-ref ``` Find the Supabase keys in: Supabase dashboard → Project Settings → API +`ALLOWED_ORIGINS` is only needed if you deploy your own dashboard (Step 8) on +a domain other than `lorekit.io` — with `VERCEL_ENV=production` and no +`ALLOWED_ORIGINS`, the REST CORS allowlist defaults to `https://lorekit.io` +and your own dashboard's requests are blocked. The Dash0/OTel vars are +optional telemetry and safe to omit. + > `GITHUB_WEBHOOK_SECRET` is **not required** — webhook secrets are now > per-repository and generated from the dashboard (Step 10). The env var is > a legacy fallback only, for deployments that pre-date per-repo secrets; set @@ -89,34 +99,54 @@ Find the Supabase keys in: Supabase dashboard → Project Settings → API --- -## Step 6 — Deploy the MCP server +## Step 6 — Deploy all Edge Functions ```bash -pnpm nx fn:deploy supabase +supabase functions deploy --no-verify-jwt --project-ref ``` -This typechecks and deploys two Edge Functions: -- `mcp` — the MCP server (requires auth) -- `health` — unauthenticated health check endpoint +`pnpm nx fn:deploy supabase` only typechecks and deploys `mcp` + `health` — +fine for the hosted project's own deploy pipeline, but it leaves the +`memories` function (used by hooks, the remote CLI commands, and the +dashboard's REST API), `orgs`, `openapi`, `blog`, `profiling`, and +`groom-sweep` undeployed. Deploy all 8 Edge Functions with the command above +instead — it is exactly what `.github/workflows/deploy.yml` runs, and +`supabase/config.toml` sets `verify_jwt = false` for all of them. Verify the deployment: ```bash pnpm nx health supabase # expected: {"status":"ok","db":true} + +curl -s -o /dev/null -w '%{http_code}' https://.supabase.co/functions/v1/memories +# expected: 401 (deployed, auth required) — 404 means it did not deploy ``` --- ## Step 7 — Generate an API token -1. Open the web dashboard: https://lorekit.io/lore (or your own Vercel URL after step 8) -2. Go to **Lore Explorer → Step 2: Connect your agent** -3. Click **Generate new token** -4. Enter a name (e.g. `claude-local`, `ci-github-actions`) -5. Choose **Read + Write** for agents that learn, **Read only** for CI context injection, +A token must be minted on a dashboard that is connected to **your** project — +a token minted on `lorekit.io` only authenticates the hosted project and will +not work against your self-hosted deployment. Open a dashboard connected to +your project first: + +- Deploy your own dashboard now (see Step 8 below) and open it, **or** +- Run it locally against your project: set `NEXT_PUBLIC_SUPABASE_URL` and + `NEXT_PUBLIC_SUPABASE_ANON_KEY` in `packages/web/.env.local` to your + project's values, add `http://localhost:3000/api/auth/callback` to + Supabase → Auth → URL Configuration → Redirect URLs, then run + `pnpm nx serve web`. + +Then, in that dashboard: + +1. Go to **Lore Explorer → Step 2: Connect your agent** +2. Click **Generate new token** +3. Enter a name (e.g. `claude-local`, `ci-github-actions`) +4. Choose **Read + Write** for agents that learn, **Read only** for CI context injection, or **Write only** for a one-way lesson feeder that should never read -6. Copy the token — it is shown **once only** +5. Copy the token — it is shown **once only** Token formats: - `lk_rw_<32 chars>` — read + write @@ -138,9 +168,9 @@ If you want your own dashboard instance (rather than using `lorekit.io`): 3. Add environment variables: ``` -NEXT_PUBLIC_SUPABASE_URL https://pqokxlhvnosogizsjztg.supabase.co +NEXT_PUBLIC_SUPABASE_URL https://.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY -NEXT_PUBLIC_SUPABASE_PROJECT_REF pqokxlhvnosogizsjztg +NEXT_PUBLIC_SUPABASE_PROJECT_REF NEXT_PUBLIC_APP_URL https://.vercel.app NEXT_PUBLIC_DASH0_OTLP_ENDPOINT https://ingress.europe-west4.gcp.dash0-dev.com NEXT_PUBLIC_DASH0_AUTH_TOKEN @@ -156,14 +186,18 @@ OTEL_EXPORTER_OTLP_HEADERS Authorization=Bearer ## Step 9 — Connect your agent -The fastest path is the CLI — it scaffolds the skill, MCP connection, and lifecycle hooks in one command: +The fastest path is the CLI — it scaffolds the skill, MCP connection, and lifecycle hooks in one command. `` is the same ref you linked in Step 2 — this is YOUR project, never the hosted one: ```bash npx @lorekit/cli install \ - --endpoint https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp \ + --endpoint https://.supabase.co/functions/v1/mcp \ --token lk_rw_ ``` +Re-running `lorekit install` **without** `--endpoint` rewrites the MCP entry +back to the hosted URL — always pass `--endpoint`, or export +`LOREKIT_MCP_URL` in your shell profile, on every subsequent install. + **Claude Code on the web** clones the repo fresh into an ephemeral container, so it only sees a committed, repo-root `.mcp.json`. Add `--mcp-json` to also write that file in a committable form — it authenticates via a `${LOREKIT_TOKEN}` @@ -171,7 +205,7 @@ reference instead of an embedded token, so it holds no secret: ```bash npx @lorekit/cli install --global --mcp-json \ - --endpoint https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp + --endpoint https://.supabase.co/functions/v1/mcp ``` Commit the generated `.mcp.json` and set `LOREKIT_TOKEN` as an environment @@ -191,7 +225,7 @@ For any other MCP-compatible agent, add the endpoint and Bearer token to the age "lorekit": { "command": "npx", "args": ["-y", "mcp-remote", - "https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp", + "https://.supabase.co/functions/v1/mcp", "--header", "Authorization:Bearer lk_rw_"] } } @@ -209,7 +243,7 @@ are **per-repository** — generate one for each repo you want to webhook: 2. Under **Webhook secrets**, add the repo (`owner/repo`) and copy the generated secret — it's shown once 3. Go to that repo on GitHub → Settings → Webhooks → Add webhook -4. **Payload URL:** `https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp/webhooks/github` +4. **Payload URL:** `https://.supabase.co/functions/v1/mcp/webhooks/github` 5. **Content type:** `application/json` 6. **Secret:** the value you copied in step 2 7. **Events:** Pull request review comments + Pull request reviews @@ -223,18 +257,18 @@ other repos' secrets are untouched. ```bash # 1. Health check -curl https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/health +curl https://.supabase.co/functions/v1/health # → {"status":"ok","db":true} # 2. Write a test lesson -curl -X POST https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp \ +curl -X POST https://.supabase.co/functions/v1/mcp \ -H "Authorization: Bearer lk_rw_" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"memory.write","arguments":{"scope":"global","key":"install-test","value":"LoreKit is working."}}}' # → {"jsonrpc":"2.0","id":1,"result":{"id":"...","created_at":"..."}} # 3. Read it back -curl -X POST https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp \ +curl -X POST https://.supabase.co/functions/v1/mcp \ -H "Authorization: Bearer lk_ro_" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"memory.read","arguments":{"scope":"global","key":"install-test"}}}' @@ -255,7 +289,7 @@ Store a read+write token as `LOREKIT_TOKEN` in your repo secrets, then use it in -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"memory.list","arguments":{"scope":"repo::${{ github.repository }}"}}}' env: - LOREKIT_MCP_URL: https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp + LOREKIT_MCP_URL: https://.supabase.co/functions/v1/mcp LOREKIT_TOKEN: ${{ secrets.LOREKIT_TOKEN }} ``` diff --git a/packages/cli/README.md b/packages/cli/README.md index 1b9fe36a4..c8645c71e 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1277,6 +1277,14 @@ before mutating (archive is preferred over hard-delete), because the store is shared and a merge or delete is permanent for every agent. See its `SKILL.md`, `rules/grooming-pass.md`, and `references/merge-and-expiry.md`. +A fourth skill, **`lorekit-self-host`**, ships in the `skill/*` directory and in +the Claude plugin, but is **not** scaffolded by `install` — it's a one-time, +slash-only deployment runbook (`/lorekit-self-host`), not everyday agent +memory, so it is deliberately absent from the `SKILLS` list `install`/`doctor` +iterate. It walks deploying your own LoreKit backend (full self-host, or +self-host plus BYOD) as a series of gates: do the step, verify it, stop and +diagnose on failure. See its `SKILL.md`. + The **skills** are model-invoked (the agent chooses to use them). For a **deterministic** guarantee — lessons injected on every session start, a nudge on every tool failure — use the framework plugins in [`plugins/`](../../plugins/), diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md new file mode 100644 index 000000000..017c32304 --- /dev/null +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -0,0 +1,225 @@ +--- +name: lorekit-self-host +description: > + Guided runbook for deploying your OWN LoreKit backend instead of using the + hosted service — full self-host (your Supabase project runs everything) or + self-host + BYOD (memory rows live in a second Supabase project you + control). Walks Supabase link → migrate → secrets → deploy-all-functions → + dashboard/token → connect-your-agent → write/read round-trip, each step a + gate: do the thing, verify it, stop and diagnose if the verification fails. + Links out to docs/install.md and docs/byod.md rather than duplicating their + commands. Slash-only — not installed by `lorekit install`, and never reads + or writes a lesson itself. Use when deploying your own LoreKit instance, not + for everyday read/write (see lorekit-memory) or store maintenance (see + lorekit-groom). Triggers ONLY on explicit invocation: + "/lorekit-self-host". +user-invocable: true +disable-model-invocation: true +argument-hint: '[full|byod]' +license: MIT +metadata: + author: mthines + version: '1.0.0' + workflow_type: self-host-deployment-runbook + tags: + - lorekit + - self-host + - supabase + - byod + - setup +--- + +# LoreKit Self-Host + +A guided runbook for running your own LoreKit backend. Every step below is a +gate: **do** the thing, **verify** it, and **stop and diagnose** before moving +on if the verification fails. This skill links to the canonical docs instead +of copying their commands, so it never drifts from them: + +- Full install reference: [docs/install.md](https://github.com/mthines/lorekit/blob/main/docs/install.md) +- BYOD reference: [docs/byod.md](https://github.com/mthines/lorekit/blob/main/docs/byod.md) + +**Never echo a token, API key, or service-role key value** in any command +output you show the user — redact it, or reference the env var name only. + +## Step 0 — Choose your setup + +| Setup | What you run | Memory rows live in | Dashboard/REST/hooks see them? | +| ----- | ------------ | -------------------- | ------------------------------- | +| Hosted (default) | Nothing — use `lorekit.io` | LoreKit's hosted project | Yes | +| Full self-host | Your own Supabase project, all 8 Edge Functions | Your project's own DB | Yes | +| Self-host + BYOD | Your function project **plus** a second Supabase project for storage | The second (storage) project's DB | **No** — the dashboard, REST API, remote CLI commands, and hooks all read the **function project's own DB**, never the BYOD storage project | + +If the user only wants the hosted service, **stop here** and point them at +`npx @lorekit/cli install` — nothing below applies. + +**Recommendation: choose full self-host unless your memory rows must live in +a separate database** (data residency, an existing Supabase subscription you +want billed separately, or org policy). BYOD is full self-host **plus** a +second storage project layered on top — the hosted endpoint has no BYOD, and +BYOD is never a substitute for deploying your own function project; it only +changes where rows are written. State this trade-off to the user before +proceeding so they pick the right branch of this runbook (`full` or `byod` — +matches this skill's argument hint). + +## Step 1 — Prerequisites + +Follow [docs/install.md § Prerequisites](https://github.com/mthines/lorekit/blob/main/docs/install.md#prerequisites): +Node ≥ 18, pnpm, the Supabase CLI, Git, and a Supabase project (create one at +https://supabase.com if you don't have one yet). + +**Verify:** `node -v`, `pnpm -v`, `supabase -v`, `git --version` all print a +version. **On failure:** install the missing tool per the Prerequisites table +before continuing. + +## Step 2 — Link your Supabase project + +Follow [docs/install.md § Step 2](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-2--link-to-your-supabase-project): +`supabase link --project-ref `, then write +`SUPABASE_PROJECT_REF=` into `.env.local`. + +**Verify:** `supabase projects list` shows your project, and the link command +exits 0. **On failure:** re-check the project ref (the subdomain of your +Supabase project URL) and that you're logged in (`supabase login`). + +## Step 3 — Apply database migrations + +Follow [docs/install.md § Step 3](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-3--apply-database-migrations): +`pnpm nx db:push supabase`. + +**Verify:** the command exits 0 and `supabase migration list` shows no +pending migrations. **On failure:** read the migration error — a partially +applied migration usually needs a manual fix before re-running `db:push`. + +## Step 4 — Set Edge Function secrets + +Follow [docs/install.md § Step 5](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-5--set-supabase-edge-function-secrets). +Required: `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`. +Optional: `ALLOWED_ORIGINS` (only if you deploy your own dashboard on a +non-`lorekit.io` domain — otherwise its REST calls are CORS-blocked), and the +Dash0/OTel variables (telemetry only, safe to skip). + +**Verify:** `supabase secrets list --project-ref ` lists +the three required names (never print their values). **On failure:** re-run +`supabase secrets set` with the correct project ref. + +## Step 5 — Deploy ALL Edge Functions + +The project ships 8 Edge Functions (`mcp`, `health`, `memories`, `orgs`, +`openapi`, `blog`, `profiling`, `groom-sweep`). `pnpm nx fn:deploy supabase` +only deploys `mcp` + `health` — that leaves the `memories` function (used by +hooks, the remote CLI commands, and the dashboard's REST API) undeployed. +Deploy every function instead: + +```bash +supabase functions deploy --no-verify-jwt --project-ref +``` + +**Verify, in order:** + +1. Health check: `curl https://.supabase.co/functions/v1/health` + must return `{"status":"ok","db":true}`. `{"db":false}` means the function + can't reach the DB — recheck the secrets from Step 4. +2. `memories` deployed: `curl -s -o /dev/null -w '%{http_code}' https://.supabase.co/functions/v1/memories` + must return `401` (deployed, auth required). A `404` means the function + wasn't deployed — re-run the deploy command above; it deploys every + function found under `supabase/functions/`. + +**On failure:** check `supabase functions list --project-ref ` +for which functions actually deployed, and re-run the command. + +## Step 6 (BYOD only) — Point the function project at your storage project + +Skip this step for full self-host. + +1. Create a second Supabase project for storage (or reuse an existing one). +2. Apply the LoreKit schema to it by following + [docs/byod.md § Apply the LoreKit schema](https://github.com/mthines/lorekit/blob/main/docs/byod.md#2-apply-the-lorekit-schema) — + this runbook does not duplicate those commands since they vary by + revision; follow what that doc (and `lorekit bootstrap` itself) prints. +3. Set `LOREKIT_STORAGE_URL`, `LOREKIT_STORAGE_ANON_KEY`, AND + `LOREKIT_STORAGE_SERVICE_KEY` as **Edge Function secrets on the function + project** (`supabase secrets set ... --project-ref `) + — NOT in `.mcp.json`. An older `docs/byod.md` revision may say to put + these in `.mcp.json`; that env never reaches the edge function, only your + local MCP client process, so it has no effect on a deployed function. +4. With that env set, run `lorekit bootstrap` (follow what it prints) and + then `lorekit doctor` to confirm the storage connection is healthy. + +**Caveat — repeat this to the user:** the dashboard, REST API, remote CLI +commands, and hooks all read the **function project's own DB**. They do +**not** see rows written to the BYOD storage project. There is no dashboard +view into BYOD data — it is intentionally invisible to everything except the +`memory.*` MCP tools and the `lorekit` CLI talking directly to that endpoint. + +**Verify:** `lorekit doctor` reports the BYOD storage connection healthy, with +no error reading or resolving `LOREKIT_STORAGE_URL`. **On failure:** confirm +all three secrets are set on the function project (not the storage project), +and that the schema from Step 6.2 applied cleanly. + +## Step 7 — Dashboard and API token + +A token must be minted on a dashboard connected to **your** project — a token +minted on `lorekit.io` only authenticates the hosted project and will not work +against your self-hosted deployment. + +Either: + +- Deploy your own dashboard (follow + [docs/install.md § Step 8](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-8--deploy-the-web-dashboard-optional)), or +- Run it locally against your project: set `NEXT_PUBLIC_SUPABASE_URL` and + `NEXT_PUBLIC_SUPABASE_ANON_KEY` in `packages/web/.env.local` to your + project's values, add `http://localhost:3000/api/auth/callback` to + Supabase → Auth → URL Configuration → Redirect URLs, then `pnpm nx serve web`. + +Then, in that dashboard: **Lore Explorer → Step 2: Connect your agent → +Generate new token**. Choose Read + Write (`lk_rw_`), Read only (`lk_ro_`), or +Write only (`lk_wo_`). Copy it — it is shown once. + +**Verify:** the token modal shows a token in the format `lk_rw_<32 chars>` (or +`lk_ro_`/`lk_wo_`). **On failure:** confirm the dashboard's Supabase env vars +point at your project, not the hosted one. + +## Step 8 — Connect your agent + +```bash +npx @lorekit/cli install --endpoint https://.supabase.co/functions/v1/mcp --token +``` + +(or export `LOREKIT_MCP_URL=https://.supabase.co/functions/v1/mcp` +and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). + +**Warning:** re-running `lorekit install` **without** `--endpoint` rewrites +the MCP entry back to the hosted URL (`install.mjs` falls back to +`LOREKIT_MCP_ENDPOINT` when `--endpoint` is omitted). Always pass +`--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every +subsequent install. + +**Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports +connectivity + authentication passing. **On failure:** re-check the endpoint +URL (must end in `/functions/v1/mcp`) and that the token matches the +permission tier the connection needs. + +## Step 9 — Write/read round-trip + +Write a `global` test lesson, read it back, then delete it: + +```json +memory.write { scope: "global", key: "self-host-test", value: "It works." } +memory.read { scope: "global", key: "self-host-test" } +``` + +**Verify:** the read returns the value you wrote. **For BYOD**, also confirm +the row landed in the **storage project's** `memories` table — not the +function project's — since that is the whole point of the BYOD layering. +**On failure:** re-check Step 8's endpoint/token, and for BYOD, re-check Step +6's secrets. + +## Never + +- Never print or echo a token, API key, or service-role key value. +- Never apply the BYOD storage overlay to the function project's own + database — it only ever goes on the separate storage project; the function + project gets the Step 6.3 secrets only. +- Never point a self-hosted deployment's agent connection at the hosted + endpoint, and never assume a `lorekit.io`-minted token works against it. diff --git a/packages/schemas/src/llms/template.md b/packages/schemas/src/llms/template.md index b926a73ca..bab2e03fa 100644 --- a/packages/schemas/src/llms/template.md +++ b/packages/schemas/src/llms/template.md @@ -65,7 +65,9 @@ scope that answered in the response. An omitted scope means EVERYWHERE, never `hybrid` (default, list + that map), `index` (list only) or `map` (map + the three most salient). -Full self-hosting guide (your own Supabase + Vercel): https://github.com/mthines/lorekit/blob/main/docs/install.md +Self-hosting your own backend (your own Supabase + Vercel)? Compare full +self-host vs BYOD at https://lorekit.io/docs/self-host, then follow the full +install guide: https://github.com/mthines/lorekit/blob/main/docs/install.md ## Local mode (offline, no account) @@ -419,6 +421,6 @@ id: a `401` makes streamable-HTTP clients retry a session handshake and hang. - API tokens: https://github.com/mthines/lorekit/blob/main/docs/api-tokens.md - Limits: https://github.com/mthines/lorekit/blob/main/docs/limits.md - Organizations & sharing: https://github.com/mthines/lorekit/blob/main/docs/org-sharing.md -- Deployment / self-hosting: https://github.com/mthines/lorekit/blob/main/docs/install.md +- Deployment / self-hosting: https://lorekit.io/docs/self-host (full guide: https://github.com/mthines/lorekit/blob/main/docs/install.md) - CLI reference: https://github.com/mthines/lorekit/blob/main/packages/cli/README.md - Plugins (Claude / Cursor / Codex): https://github.com/mthines/lorekit/blob/main/plugins/README.md diff --git a/packages/web/public/llms.txt b/packages/web/public/llms.txt index 2fbb18c8a..25a371cea 100644 --- a/packages/web/public/llms.txt +++ b/packages/web/public/llms.txt @@ -76,7 +76,9 @@ scope that answered in the response. An omitted scope means EVERYWHERE, never `hybrid` (default, list + that map), `index` (list only) or `map` (map + the three most salient). -Full self-hosting guide (your own Supabase + Vercel): https://github.com/mthines/lorekit/blob/main/docs/install.md +Self-hosting your own backend (your own Supabase + Vercel)? Compare full +self-host vs BYOD at https://lorekit.io/docs/self-host, then follow the full +install guide: https://github.com/mthines/lorekit/blob/main/docs/install.md ## Local mode (offline, no account) @@ -940,6 +942,7 @@ id: a `401` makes streamable-HTTP clients retry a session handshake and hang. - [Getting started](https://lorekit.io/docs/setup) — Connect LoreKit to your AI coding agent in three steps — read and write memories so knowledge accumulates across sessions. - [Offline storage](https://lorekit.io/docs/offline) — Store lore locally on your machine using the LoreKit CLI — no account, no network, full privacy. Memories live in a plain directory you own and control. - [Remote storage](https://lorekit.io/docs/remote) — Sync lore to the hosted LoreKit MCP server so your memories are available on every machine, from every agent, without any extra setup on each device. +- [Self-hosting](https://lorekit.io/docs/self-host) — Run your own LoreKit backend — full self-host on your own Supabase project, or self-host plus BYOD for memory rows in a second database. Compare the three setups and connect your agent to your own endpoint. - [Team sharing](https://lorekit.io/docs/organization) — Create an organization, invite teammates, and share one authoritative set of lore across the whole team. Updates are instant — no syncing, no copy-fan-out. - [Private lore](https://lorekit.io/docs/private) — Every memory is personal by default — visible only to the user or token that wrote it. This tutorial explains how personal and org-owned lore coexist, and how to keep sensitive memories private even in a shared team environment. - [Tags & scopes](https://lorekit.io/docs/tags) — Scopes partition lore by location (global, repo, branch). Tags are free-form labels that cut across scopes — use them to identify memory type, source agent, workflow, or any dimension you care about. @@ -956,6 +959,6 @@ id: a `401` makes streamable-HTTP clients retry a session handshake and hang. - API tokens: https://github.com/mthines/lorekit/blob/main/docs/api-tokens.md - Limits: https://github.com/mthines/lorekit/blob/main/docs/limits.md - Organizations & sharing: https://github.com/mthines/lorekit/blob/main/docs/org-sharing.md -- Deployment / self-hosting: https://github.com/mthines/lorekit/blob/main/docs/install.md +- Deployment / self-hosting: https://lorekit.io/docs/self-host (full guide: https://github.com/mthines/lorekit/blob/main/docs/install.md) - CLI reference: https://github.com/mthines/lorekit/blob/main/packages/cli/README.md - Plugins (Claude / Cursor / Codex): https://github.com/mthines/lorekit/blob/main/plugins/README.md diff --git a/packages/web/src/content/docs/claude-code-web.mdx b/packages/web/src/content/docs/claude-code-web.mdx index 412557734..3ca86683c 100644 --- a/packages/web/src/content/docs/claude-code-web.mdx +++ b/packages/web/src/content/docs/claude-code-web.mdx @@ -1,7 +1,7 @@ --- title: "Claude Code on the web" description: "Set up LoreKit in Claude Code's cloud web environment. Each session runs in a fresh, ephemeral container, so either commit a project .mcp.json (auth via the LOREKIT_TOKEN environment variable) or install LoreKit globally from a setup script — the token always lives in LOREKIT_TOKEN, never in the repo." -order: 8 +order: 9 keywords: ["claude code on the web", "cloud", "ephemeral", "container", "setup script", "npm install -g", "cli", "install", "global", ".mcp.json", "mcp-json", "LOREKIT_TOKEN", "environment variables", "network policy", "doctor", "stats"] --- diff --git a/packages/web/src/content/docs/config.mdx b/packages/web/src/content/docs/config.mdx index c8f4fcb9f..6677e8cdb 100644 --- a/packages/web/src/content/docs/config.mdx +++ b/packages/web/src/content/docs/config.mdx @@ -1,7 +1,7 @@ --- title: "Configuration" description: "LoreKit reads two optional JSON config files that decide the memory mode, the store location, and write & hook behaviour. Both files share the same schema and every field is optional — start with an empty file and add only what you need." -order: 7 +order: 8 keywords: ["configuration", ".lorekit.json", "config.json", "mode", "store", "deny", "hooks", "tags.default", "scope.defaults", "telemetry", "environment variables", "precedence"] --- diff --git a/packages/web/src/content/docs/deep-links.mdx b/packages/web/src/content/docs/deep-links.mdx index 63e04f96f..806ba31ee 100644 --- a/packages/web/src/content/docs/deep-links.mdx +++ b/packages/web/src/content/docs/deep-links.mdx @@ -1,7 +1,7 @@ --- title: "Deep links" description: "Every view in the LoreKit dashboard is a shareable URL. Link straight to a scope in the Explorer, open one memory's detail sheet, or share a filtered search — from the terminal, a PR, or a chat message. Generate them by hand or with the CLI's link command and --link flag." -order: 10 +order: 11 keywords: ["deep link", "deeplink", "share link", "shareable url", "explorer", "scope link", "memory link", "lesson link", "lorekit link", "--link flag", "url command", "LOREKIT_APP_URL", "--base", "self-hosting", "useUrlState", "query params", "tags filter"] --- diff --git a/packages/web/src/content/docs/grooming.mdx b/packages/web/src/content/docs/grooming.mdx index 76b344428..41fdd7caf 100644 --- a/packages/web/src/content/docs/grooming.mdx +++ b/packages/web/src/content/docs/grooming.mdx @@ -1,7 +1,7 @@ --- title: "Retention policies" description: "Save scoped rules that automatically archive lessons you no longer need — reviewed manually or swept nightly — without ever hard-deleting anything. Plus the Insights grid that shows which lessons are earning their place, citations so an agent can say which lessons it actually applied, and the read-only Duplicate Clusters sidebar for spotting the same lesson written twice." -order: 11 +order: 12 keywords: ["grooming", "retention", "policy", "policy.create", "groom.preview", "groom.run", "memory.protect", "archive", "auto", "cleanup", "cli", "duplicates", "dedupe", "clusters", "near-duplicate", "insights", "pull-through", "delivered", "chosen", "noise tax", "dormant", "load-bearing", "cited", "citations"] --- diff --git a/packages/web/src/content/docs/organization.mdx b/packages/web/src/content/docs/organization.mdx index fbbc53395..b70b00dc0 100644 --- a/packages/web/src/content/docs/organization.mdx +++ b/packages/web/src/content/docs/organization.mdx @@ -1,7 +1,7 @@ --- title: "Team sharing" description: "Create an organization, invite teammates, and share one authoritative set of lore across the whole team. Updates are instant — no syncing, no copy-fan-out." -order: 4 +order: 5 keywords: ["organization", "team sharing", "org", "roles", "viewer", "member", "admin", "owner", "invite", "scope binding", "memory.write", "explorer", "ownership badge"] --- diff --git a/packages/web/src/content/docs/private.mdx b/packages/web/src/content/docs/private.mdx index 99d55c50f..0e07841da 100644 --- a/packages/web/src/content/docs/private.mdx +++ b/packages/web/src/content/docs/private.mdx @@ -1,7 +1,7 @@ --- title: "Private lore" description: "Every memory is personal by default — visible only to the user or token that wrote it. This tutorial explains how personal and org-owned lore coexist, and how to keep sensitive memories private even in a shared team environment." -order: 5 +order: 6 keywords: ["private lore", "personal", "org-owned", "memory.write", "scope binding", "explorer", "memory.archive", "memory.delete", "memory.restore", "token", "partition"] --- diff --git a/packages/web/src/content/docs/remote.mdx b/packages/web/src/content/docs/remote.mdx index a447dd3da..1437f3929 100644 --- a/packages/web/src/content/docs/remote.mdx +++ b/packages/web/src/content/docs/remote.mdx @@ -7,7 +7,7 @@ keywords: ["remote", "hosted", "mcp server", "api token", "lk_rw", "lk_ro", "lk_ -You can use the hosted instance at `lorekit.io` or self-host your own. This tutorial uses the hosted instance. For self-hosting see the [Installation Guide](https://github.com/mthines/lorekit/blob/main/docs/install.md). +You can use the hosted instance at `lorekit.io` or self-host your own. This tutorial uses the hosted instance. For self-hosting see the [Self-hosting](/docs/self-host) page and the [Installation Guide](https://github.com/mthines/lorekit/blob/main/docs/install.md). diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx new file mode 100644 index 000000000..5dc2dfa80 --- /dev/null +++ b/packages/web/src/content/docs/self-host.mdx @@ -0,0 +1,118 @@ +--- +title: "Self-hosting" +description: "Run your own LoreKit backend — full self-host on your own Supabase project, or self-host plus BYOD for memory rows in a second database. Compare the three setups and connect your agent to your own endpoint." +order: 4 +keywords: ["self-host", "self-hosting", "byod", "bring your own database", "supabase", "deploy", "install.md", "lorekit-self-host", "endpoint", "mcp url"] +--- + + + +The hosted instance at `lorekit.io` — MCP endpoint +`https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp` — needs none of +this; it's ready to use today. This page is for running LoreKit on +infrastructure you control. + + + +## Compare the three setups + +| | Hosted | Full self-host | Self-host + BYOD | +| --- | --- | --- | --- | +| What you run | Nothing | Your own Supabase project, all 8 Edge Functions | Your function project, plus a second Supabase project for storage | +| Where memories live | LoreKit's hosted project | Your project's own database | The second (storage) project's database | +| Dashboard / REST / hooks see your memories? | Yes | Yes | **No** — they all read the function project's own database, never the BYOD storage project | +| BYOD available? | No | N/A | Yes — layered on top of a full self-host deployment | + +**Full self-host is the right default** unless your memory rows must live in +a separate database — data residency, an existing Supabase subscription +billed separately, or org policy. BYOD is never a substitute for deploying +your own function project; it only changes where rows are written, and the +hosted endpoint has no BYOD at all. + + + +Follow the [Installation Guide](https://github.com/mthines/lorekit/blob/main/docs/install.md) +on GitHub — it covers linking a Supabase project, applying migrations, +setting Edge Function secrets, deploying all 8 Edge Functions (not just +`mcp` + `health` — the `memories` function is required for hooks, the remote +CLI commands, and the dashboard's REST API), and generating an API token on a +dashboard connected to your project. + +```bash +supabase link --project-ref +pnpm nx db:push supabase +supabase functions deploy --no-verify-jwt --project-ref +``` + +Verify with a health check: + +```bash +curl https://.supabase.co/functions/v1/health +# → {"status":"ok","db":true} +``` + + + + + +Only if your memory rows must live in a database you already run. Apply the +LoreKit schema to that second project and set `LOREKIT_STORAGE_URL` + +`LOREKIT_STORAGE_ANON_KEY` + `LOREKIT_STORAGE_SERVICE_KEY` as **Edge Function +secrets on your function project** (not in `.mcp.json` — that env never +reaches a deployed function). Follow the +[BYOD guide](https://github.com/mthines/lorekit/blob/main/docs/byod.md) for +the exact schema commands. + +Remember: the dashboard, REST API, remote CLI commands, and hooks all read +the function project's own database. None of them see rows written to the +BYOD storage project. + + + + + +One endpoint setting covers MCP, REST, hooks, and the remote CLI commands, +because the CLI derives the REST base from the MCP endpoint: + +```bash +npx @lorekit/cli install \ + --endpoint https://.supabase.co/functions/v1/mcp \ + --token lk_rw_ +``` + +(or export `LOREKIT_MCP_URL` / commit `"mcp.endpoint"` in `.lorekit.json` +instead of passing `--endpoint` every time) + + + +Re-running `npx @lorekit/cli install` **without** `--endpoint` rewrites the +MCP entry back to the hosted URL. Always pass `--endpoint`, or export +`LOREKIT_MCP_URL` in your shell profile, on every subsequent install. + + + + + + + +The Claude plugin ships a slash-only `/lorekit-self-host` skill that walks +every step above as a gate — do the thing, verify it, stop and diagnose if +the verification fails — and links to the docs instead of duplicating their +commands. Claude plugin users get it automatically with +`/plugin install lorekit-memory@lorekit`. Everyone else can fetch it +directly: + +```bash +curl -o ~/.claude/skills/lorekit-self-host/SKILL.md \ + https://raw.githubusercontent.com/mthines/lorekit/main/packages/cli/skill/lorekit-self-host/SKILL.md +``` + + + + + +**Next steps:** once you're connected, see [Remote storage](/docs/remote) for +the token-scoping, CI, and GitHub App features that work the same way against +a self-hosted endpoint. + + diff --git a/packages/web/src/content/docs/tags.mdx b/packages/web/src/content/docs/tags.mdx index 9e960c1a7..06bdffee8 100644 --- a/packages/web/src/content/docs/tags.mdx +++ b/packages/web/src/content/docs/tags.mdx @@ -1,7 +1,7 @@ --- title: "Tags & scopes" description: "Scopes partition lore by location (global, repo, branch). Tags are free-form labels that cut across scopes — use them to identify memory type, source agent, workflow, or any dimension you care about." -order: 6 +order: 7 keywords: ["tags", "scopes", "global", "project", "repo", "branch", "namespace", "memory.write", "memory.list", "memory.search", "wildcards", "precedence", "cli"] --- diff --git a/packages/web/src/content/docs/use-cases.mdx b/packages/web/src/content/docs/use-cases.mdx index a021199e5..0b920d8f1 100644 --- a/packages/web/src/content/docs/use-cases.mdx +++ b/packages/web/src/content/docs/use-cases.mdx @@ -1,7 +1,7 @@ --- title: "Use cases" description: "End-to-end examples showing how scopes, tags, and org sharing come together for real development workflows." -order: 9 +order: 10 keywords: ["use cases", "autonomous workflow", "aw", "stuck-loop", "ci", "github actions", "ci state", "state records", "flaky tests", "kind bus", "lk_wo", "clear-ttl", "org sharing", "scope binding", "branch scope", "ttl_days", "expiring_within_days", "memory.purge_expired", "runbook"] --- diff --git a/packages/web/src/lib/docs/sections.ts b/packages/web/src/lib/docs/sections.ts index ac27b2802..a90fbefe1 100644 --- a/packages/web/src/lib/docs/sections.ts +++ b/packages/web/src/lib/docs/sections.ts @@ -11,6 +11,7 @@ import { Link2, FileCode, Archive, + Server, type LucideIcon, } from 'lucide-react'; import type { SectionNavItem } from '@/components/ui/SectionNav'; @@ -38,6 +39,7 @@ export const DOCS_SECTIONS: readonly DocsSection[] = [ { id: 'setup', label: 'Getting started', icon: Rocket, summary: 'Connect your agent and generate a token' }, { id: 'offline', label: 'Offline storage', icon: HardDrive, summary: 'Store lessons locally without a server' }, { id: 'remote', label: 'Remote storage', icon: Cloud, summary: 'Sync lessons to the hosted LoreKit server' }, + { id: 'self-host', label: 'Self-hosting', icon: Server, summary: 'Run your own LoreKit backend, with optional BYOD' }, { id: 'organization', label: 'Team sharing', icon: Users, summary: 'Share lore across your organization' }, { id: 'private', label: 'Private lore', icon: Lock, summary: 'Keep sensitive lessons private' }, { id: 'tags', label: 'Tags & scopes', icon: Tag, summary: 'Organise lessons by scope and tag' }, diff --git a/plugins/README.md b/plugins/README.md index dfa27ae6a..730025ea3 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -69,7 +69,7 @@ See each bundle's README for copy-in instructions: ## Keeping the Claude skills in sync -The `lorekit-claude/skills/*` directories (`lorekit-memory/`, `lorekit-setup/`, `lorekit-groom/`) are vendored from the +The `lorekit-claude/skills/*` directories (`lorekit-memory/`, `lorekit-setup/`, `lorekit-groom/`, `lorekit-self-host/`) are vendored from the single sources at `packages/cli/skill/*`. `sync-plugin-skill.mjs` mirrors every skill it finds, so a newly added source skill is vendored automatically. Re-sync after editing any source: diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md new file mode 100644 index 000000000..017c32304 --- /dev/null +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -0,0 +1,225 @@ +--- +name: lorekit-self-host +description: > + Guided runbook for deploying your OWN LoreKit backend instead of using the + hosted service — full self-host (your Supabase project runs everything) or + self-host + BYOD (memory rows live in a second Supabase project you + control). Walks Supabase link → migrate → secrets → deploy-all-functions → + dashboard/token → connect-your-agent → write/read round-trip, each step a + gate: do the thing, verify it, stop and diagnose if the verification fails. + Links out to docs/install.md and docs/byod.md rather than duplicating their + commands. Slash-only — not installed by `lorekit install`, and never reads + or writes a lesson itself. Use when deploying your own LoreKit instance, not + for everyday read/write (see lorekit-memory) or store maintenance (see + lorekit-groom). Triggers ONLY on explicit invocation: + "/lorekit-self-host". +user-invocable: true +disable-model-invocation: true +argument-hint: '[full|byod]' +license: MIT +metadata: + author: mthines + version: '1.0.0' + workflow_type: self-host-deployment-runbook + tags: + - lorekit + - self-host + - supabase + - byod + - setup +--- + +# LoreKit Self-Host + +A guided runbook for running your own LoreKit backend. Every step below is a +gate: **do** the thing, **verify** it, and **stop and diagnose** before moving +on if the verification fails. This skill links to the canonical docs instead +of copying their commands, so it never drifts from them: + +- Full install reference: [docs/install.md](https://github.com/mthines/lorekit/blob/main/docs/install.md) +- BYOD reference: [docs/byod.md](https://github.com/mthines/lorekit/blob/main/docs/byod.md) + +**Never echo a token, API key, or service-role key value** in any command +output you show the user — redact it, or reference the env var name only. + +## Step 0 — Choose your setup + +| Setup | What you run | Memory rows live in | Dashboard/REST/hooks see them? | +| ----- | ------------ | -------------------- | ------------------------------- | +| Hosted (default) | Nothing — use `lorekit.io` | LoreKit's hosted project | Yes | +| Full self-host | Your own Supabase project, all 8 Edge Functions | Your project's own DB | Yes | +| Self-host + BYOD | Your function project **plus** a second Supabase project for storage | The second (storage) project's DB | **No** — the dashboard, REST API, remote CLI commands, and hooks all read the **function project's own DB**, never the BYOD storage project | + +If the user only wants the hosted service, **stop here** and point them at +`npx @lorekit/cli install` — nothing below applies. + +**Recommendation: choose full self-host unless your memory rows must live in +a separate database** (data residency, an existing Supabase subscription you +want billed separately, or org policy). BYOD is full self-host **plus** a +second storage project layered on top — the hosted endpoint has no BYOD, and +BYOD is never a substitute for deploying your own function project; it only +changes where rows are written. State this trade-off to the user before +proceeding so they pick the right branch of this runbook (`full` or `byod` — +matches this skill's argument hint). + +## Step 1 — Prerequisites + +Follow [docs/install.md § Prerequisites](https://github.com/mthines/lorekit/blob/main/docs/install.md#prerequisites): +Node ≥ 18, pnpm, the Supabase CLI, Git, and a Supabase project (create one at +https://supabase.com if you don't have one yet). + +**Verify:** `node -v`, `pnpm -v`, `supabase -v`, `git --version` all print a +version. **On failure:** install the missing tool per the Prerequisites table +before continuing. + +## Step 2 — Link your Supabase project + +Follow [docs/install.md § Step 2](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-2--link-to-your-supabase-project): +`supabase link --project-ref `, then write +`SUPABASE_PROJECT_REF=` into `.env.local`. + +**Verify:** `supabase projects list` shows your project, and the link command +exits 0. **On failure:** re-check the project ref (the subdomain of your +Supabase project URL) and that you're logged in (`supabase login`). + +## Step 3 — Apply database migrations + +Follow [docs/install.md § Step 3](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-3--apply-database-migrations): +`pnpm nx db:push supabase`. + +**Verify:** the command exits 0 and `supabase migration list` shows no +pending migrations. **On failure:** read the migration error — a partially +applied migration usually needs a manual fix before re-running `db:push`. + +## Step 4 — Set Edge Function secrets + +Follow [docs/install.md § Step 5](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-5--set-supabase-edge-function-secrets). +Required: `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`. +Optional: `ALLOWED_ORIGINS` (only if you deploy your own dashboard on a +non-`lorekit.io` domain — otherwise its REST calls are CORS-blocked), and the +Dash0/OTel variables (telemetry only, safe to skip). + +**Verify:** `supabase secrets list --project-ref ` lists +the three required names (never print their values). **On failure:** re-run +`supabase secrets set` with the correct project ref. + +## Step 5 — Deploy ALL Edge Functions + +The project ships 8 Edge Functions (`mcp`, `health`, `memories`, `orgs`, +`openapi`, `blog`, `profiling`, `groom-sweep`). `pnpm nx fn:deploy supabase` +only deploys `mcp` + `health` — that leaves the `memories` function (used by +hooks, the remote CLI commands, and the dashboard's REST API) undeployed. +Deploy every function instead: + +```bash +supabase functions deploy --no-verify-jwt --project-ref +``` + +**Verify, in order:** + +1. Health check: `curl https://.supabase.co/functions/v1/health` + must return `{"status":"ok","db":true}`. `{"db":false}` means the function + can't reach the DB — recheck the secrets from Step 4. +2. `memories` deployed: `curl -s -o /dev/null -w '%{http_code}' https://.supabase.co/functions/v1/memories` + must return `401` (deployed, auth required). A `404` means the function + wasn't deployed — re-run the deploy command above; it deploys every + function found under `supabase/functions/`. + +**On failure:** check `supabase functions list --project-ref ` +for which functions actually deployed, and re-run the command. + +## Step 6 (BYOD only) — Point the function project at your storage project + +Skip this step for full self-host. + +1. Create a second Supabase project for storage (or reuse an existing one). +2. Apply the LoreKit schema to it by following + [docs/byod.md § Apply the LoreKit schema](https://github.com/mthines/lorekit/blob/main/docs/byod.md#2-apply-the-lorekit-schema) — + this runbook does not duplicate those commands since they vary by + revision; follow what that doc (and `lorekit bootstrap` itself) prints. +3. Set `LOREKIT_STORAGE_URL`, `LOREKIT_STORAGE_ANON_KEY`, AND + `LOREKIT_STORAGE_SERVICE_KEY` as **Edge Function secrets on the function + project** (`supabase secrets set ... --project-ref `) + — NOT in `.mcp.json`. An older `docs/byod.md` revision may say to put + these in `.mcp.json`; that env never reaches the edge function, only your + local MCP client process, so it has no effect on a deployed function. +4. With that env set, run `lorekit bootstrap` (follow what it prints) and + then `lorekit doctor` to confirm the storage connection is healthy. + +**Caveat — repeat this to the user:** the dashboard, REST API, remote CLI +commands, and hooks all read the **function project's own DB**. They do +**not** see rows written to the BYOD storage project. There is no dashboard +view into BYOD data — it is intentionally invisible to everything except the +`memory.*` MCP tools and the `lorekit` CLI talking directly to that endpoint. + +**Verify:** `lorekit doctor` reports the BYOD storage connection healthy, with +no error reading or resolving `LOREKIT_STORAGE_URL`. **On failure:** confirm +all three secrets are set on the function project (not the storage project), +and that the schema from Step 6.2 applied cleanly. + +## Step 7 — Dashboard and API token + +A token must be minted on a dashboard connected to **your** project — a token +minted on `lorekit.io` only authenticates the hosted project and will not work +against your self-hosted deployment. + +Either: + +- Deploy your own dashboard (follow + [docs/install.md § Step 8](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-8--deploy-the-web-dashboard-optional)), or +- Run it locally against your project: set `NEXT_PUBLIC_SUPABASE_URL` and + `NEXT_PUBLIC_SUPABASE_ANON_KEY` in `packages/web/.env.local` to your + project's values, add `http://localhost:3000/api/auth/callback` to + Supabase → Auth → URL Configuration → Redirect URLs, then `pnpm nx serve web`. + +Then, in that dashboard: **Lore Explorer → Step 2: Connect your agent → +Generate new token**. Choose Read + Write (`lk_rw_`), Read only (`lk_ro_`), or +Write only (`lk_wo_`). Copy it — it is shown once. + +**Verify:** the token modal shows a token in the format `lk_rw_<32 chars>` (or +`lk_ro_`/`lk_wo_`). **On failure:** confirm the dashboard's Supabase env vars +point at your project, not the hosted one. + +## Step 8 — Connect your agent + +```bash +npx @lorekit/cli install --endpoint https://.supabase.co/functions/v1/mcp --token +``` + +(or export `LOREKIT_MCP_URL=https://.supabase.co/functions/v1/mcp` +and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). + +**Warning:** re-running `lorekit install` **without** `--endpoint` rewrites +the MCP entry back to the hosted URL (`install.mjs` falls back to +`LOREKIT_MCP_ENDPOINT` when `--endpoint` is omitted). Always pass +`--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every +subsequent install. + +**Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports +connectivity + authentication passing. **On failure:** re-check the endpoint +URL (must end in `/functions/v1/mcp`) and that the token matches the +permission tier the connection needs. + +## Step 9 — Write/read round-trip + +Write a `global` test lesson, read it back, then delete it: + +```json +memory.write { scope: "global", key: "self-host-test", value: "It works." } +memory.read { scope: "global", key: "self-host-test" } +``` + +**Verify:** the read returns the value you wrote. **For BYOD**, also confirm +the row landed in the **storage project's** `memories` table — not the +function project's — since that is the whole point of the BYOD layering. +**On failure:** re-check Step 8's endpoint/token, and for BYOD, re-check Step +6's secrets. + +## Never + +- Never print or echo a token, API key, or service-role key value. +- Never apply the BYOD storage overlay to the function project's own + database — it only ever goes on the separate storage project; the function + project gets the Step 6.3 secrets only. +- Never point a self-hosted deployment's agent connection at the hosted + endpoint, and never assume a `lorekit.io`-minted token works against it. From b83f683f06d1601dd4ed96c67cbbb9c0da48df28 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:15:56 +0200 Subject: [PATCH 02/19] test(install): cover the self-host endpoint path and lock the hosted default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends install.test.mjs — the file that already owns --endpoint/.mcp.json coverage — rather than inventing a new harness: a self-hosted --endpoint is written verbatim and its REST base derives correctly via mcpToRestBase, omitting --endpoint still falls back to the hosted URL, and the new lorekit-self-host skill is never scaffolded by a fresh install (D2). --- packages/cli/test/install.test.mjs | 64 ++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/cli/test/install.test.mjs b/packages/cli/test/install.test.mjs index 8bd36e50f..607181814 100644 --- a/packages/cli/test/install.test.mjs +++ b/packages/cli/test/install.test.mjs @@ -9,8 +9,14 @@ import { install, defaultHookMode, HOOK_PROMPT_OPTIONS, tokenPlan, maskToken } f import { skillInstallDir, mcpConfigPath, homeDir, installedHookEvents, HOOK_MODES, CLAUDE_HOOK_EVENTS, } from '../src/shared/config.mjs'; +import { mcpToRestBase } from '../src/shared/mcp.mjs'; import { withHome } from './helpers.mjs'; +// The one hosted LoreKit endpoint — self-hosting (docs/install.md, +// /lorekit-self-host) never changes this default; it only ever adds +// `--endpoint`/`LOREKIT_MCP_URL` to point somewhere else. +const HOSTED_ENDPOINT = 'https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp'; + function tmp(prefix) { return fs.mkdtempSync(path.join(os.tmpdir(), prefix)); } @@ -78,6 +84,64 @@ test('install --project writes into the repo, not home', async () => { assert.ok(mcp.mcpServers.lorekit.args.some((a) => a.includes(ENDPOINT))); }); +test('install with no --endpoint and no env override still targets the hosted project (self-hosting never changes the default)', async () => { + const root = tmp('lk-default-endpoint-'); + const prevUrl = process.env.LOREKIT_MCP_URL; + const prevEndpoint = process.env.LOREKIT_ENDPOINT; + delete process.env.LOREKIT_MCP_URL; + delete process.env.LOREKIT_ENDPOINT; + try { + const code = await install({ dir: root, token: TOKEN, yes: true, project: true }); + assert.equal(exitOf(code), 0); + const mcp = JSON.parse(fs.readFileSync(path.join(root, '.mcp.json'), 'utf8')); + assert.ok( + mcp.mcpServers.lorekit.args.some((a) => a.includes(HOSTED_ENDPOINT)), + 'omitting --endpoint falls back to the hosted MCP URL, not a self-hosted one', + ); + } finally { + if (prevUrl === undefined) delete process.env.LOREKIT_MCP_URL; + else process.env.LOREKIT_MCP_URL = prevUrl; + if (prevEndpoint === undefined) delete process.env.LOREKIT_ENDPOINT; + else process.env.LOREKIT_ENDPOINT = prevEndpoint; + } +}); + +test('install --endpoint writes that project into .mcp.json, and the same URL derives the matching REST base', async () => { + const root = tmp('lk-self-host-'); + const selfHostedEndpoint = 'https://my-own-ref.supabase.co/functions/v1/mcp'; + const code = await install({ dir: root, endpoint: selfHostedEndpoint, token: TOKEN, yes: true, project: true }); + assert.equal(exitOf(code), 0); + + const mcp = JSON.parse(fs.readFileSync(path.join(root, '.mcp.json'), 'utf8')); + assert.ok( + mcp.mcpServers.lorekit.args.some((a) => a.includes(selfHostedEndpoint)), + 'the self-hosted endpoint is written verbatim, never silently rewritten to the hosted one', + ); + assert.ok( + !mcp.mcpServers.lorekit.args.some((a) => a.includes(HOSTED_ENDPOINT)), + 'the hosted endpoint is NOT also present once a self-hosted one is given', + ); + + // One endpoint setting covers MCP, REST, hooks, and the remote CLI commands + // (docs/install.md "REST/hook base" fact, encoded in the self-host skill + // and the public self-host page): the REST base the CLI derives for that + // same project's `memories`/`health` calls must point at the project the + // agent was just connected to. + assert.equal(mcpToRestBase(selfHostedEndpoint), 'https://my-own-ref.supabase.co/functions/v1'); +}); + +test('the lorekit-self-host runbook skill is never scaffolded by install (D2 — it is a one-time deployment runbook, not everyday agent memory)', async () => { + const root = tmp('lk-no-self-host-skill-'); + const code = await install({ dir: root, endpoint: ENDPOINT, token: TOKEN, yes: true, project: true }); + assert.equal(exitOf(code), 0); + + assert.ok(fs.existsSync(path.join(root, '.claude', 'skills', 'lorekit-memory', 'SKILL.md'))); + assert.ok( + !fs.existsSync(path.join(root, '.claude', 'skills', 'lorekit-self-host')), + 'lorekit-self-host must stay absent from a fresh install — it ships only via the Claude plugin / npm tarball', + ); +}); + test('install --global writes into ~/.claude and preserves existing user config', async () => { const home = tmp('lk-home-'); const root = tmp('lk-cwd-'); From aea2d1de603a5b1af73a09043c26043b29bf81d5 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:34:13 +0200 Subject: [PATCH 03/19] address review comment: use port 4200 for the locally-run dashboard Addresses @mthines's comment: https://github.com/mthines/lorekit/pull/694#discussion_r4302213498 Refs: https://github.com/mthines/lorekit/pull/694 --- docs/install.md | 6 +++--- packages/cli/skill/lorekit-self-host/SKILL.md | 5 +++-- plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/install.md b/docs/install.md index f9728e11f..65fc89353 100644 --- a/docs/install.md +++ b/docs/install.md @@ -62,7 +62,7 @@ This applies all migrations in `supabase/migrations/` and creates all required t 1. Go to https://github.com/settings/developers → OAuth Apps → New OAuth App - **Application name:** LoreKit (or any name) - - **Homepage URL:** `https://.vercel.app` (or `http://localhost:3000` for local dev) + - **Homepage URL:** `https://.vercel.app` (or `http://localhost:4200` for local dev) - **Authorization callback URL:** `https://.supabase.co/auth/v1/callback` 2. Note the **Client ID** and generate a **Client Secret** 3. In Supabase → Auth → Providers → GitHub: enable and paste both values @@ -135,9 +135,9 @@ your project first: - Deploy your own dashboard now (see Step 8 below) and open it, **or** - Run it locally against your project: set `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` in `packages/web/.env.local` to your - project's values, add `http://localhost:3000/api/auth/callback` to + project's values, add `http://localhost:4200/api/auth/callback` to Supabase → Auth → URL Configuration → Redirect URLs, then run - `pnpm nx serve web`. + `pnpm nx serve web` (it listens on port 4200). Then, in that dashboard: diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index 017c32304..a124fd88e 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -169,8 +169,9 @@ Either: [docs/install.md § Step 8](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-8--deploy-the-web-dashboard-optional)), or - Run it locally against your project: set `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` in `packages/web/.env.local` to your - project's values, add `http://localhost:3000/api/auth/callback` to - Supabase → Auth → URL Configuration → Redirect URLs, then `pnpm nx serve web`. + project's values, add `http://localhost:4200/api/auth/callback` to + Supabase → Auth → URL Configuration → Redirect URLs, then `pnpm nx serve web` + (it listens on port 4200). Then, in that dashboard: **Lore Explorer → Step 2: Connect your agent → Generate new token**. Choose Read + Write (`lk_rw_`), Read only (`lk_ro_`), or diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index 017c32304..a124fd88e 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -169,8 +169,9 @@ Either: [docs/install.md § Step 8](https://github.com/mthines/lorekit/blob/main/docs/install.md#step-8--deploy-the-web-dashboard-optional)), or - Run it locally against your project: set `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` in `packages/web/.env.local` to your - project's values, add `http://localhost:3000/api/auth/callback` to - Supabase → Auth → URL Configuration → Redirect URLs, then `pnpm nx serve web`. + project's values, add `http://localhost:4200/api/auth/callback` to + Supabase → Auth → URL Configuration → Redirect URLs, then `pnpm nx serve web` + (it listens on port 4200). Then, in that dashboard: **Lore Explorer → Step 2: Connect your agent → Generate new token**. Choose Read + Write (`lk_rw_`), Read only (`lk_ro_`), or From 44cee1320fd761cc1b2b0de5fac5061f9de8c644 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:34:33 +0200 Subject: [PATCH 04/19] address review comment: run BYOD bootstrap/doctor with storage vars inline Addresses @mthines's comment: https://github.com/mthines/lorekit/pull/694#discussion_r4302213503 Refs: https://github.com/mthines/lorekit/pull/694 --- packages/cli/skill/lorekit-self-host/SKILL.md | 27 ++++++++++++++----- .../skills/lorekit-self-host/SKILL.md | 27 ++++++++++++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index a124fd88e..dc85825d1 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -143,8 +143,21 @@ Skip this step for full self-host. — NOT in `.mcp.json`. An older `docs/byod.md` revision may say to put these in `.mcp.json`; that env never reaches the edge function, only your local MCP client process, so it has no effect on a deployed function. -4. With that env set, run `lorekit bootstrap` (follow what it prints) and - then `lorekit doctor` to confirm the storage connection is healthy. +4. `lorekit bootstrap` and `lorekit doctor` read `LOREKIT_STORAGE_*` from + your **local shell**, not from the Edge Function secrets set in 6.3 — so + pass them inline, matching docs/byod.md: + + ```bash + LOREKIT_STORAGE_URL=https://.supabase.co \ + LOREKIT_STORAGE_SERVICE_KEY= \ + lorekit bootstrap + + LOREKIT_STORAGE_URL=https://.supabase.co \ + LOREKIT_STORAGE_ANON_KEY= \ + lorekit doctor + ``` + + Without those vars in the shell, `doctor` silently skips the BYOD check. **Caveat — repeat this to the user:** the dashboard, REST API, remote CLI commands, and hooks all read the **function project's own DB**. They do @@ -152,10 +165,12 @@ commands, and hooks all read the **function project's own DB**. They do view into BYOD data — it is intentionally invisible to everything except the `memory.*` MCP tools and the `lorekit` CLI talking directly to that endpoint. -**Verify:** `lorekit doctor` reports the BYOD storage connection healthy, with -no error reading or resolving `LOREKIT_STORAGE_URL`. **On failure:** confirm -all three secrets are set on the function project (not the storage project), -and that the schema from Step 6.2 applied cleanly. +**Verify:** the inline-env `lorekit doctor` run from 6.4 prints a **passing** +`byod storage` line. No `byod storage` line at all means the check was +skipped — the vars weren't set in that shell; re-run with them inline. +**On failure:** check the storage URL/anon key you passed, that the schema +from Step 6.2 applied cleanly, and that all three secrets are set on the +function project (not the storage project). ## Step 7 — Dashboard and API token diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index a124fd88e..dc85825d1 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -143,8 +143,21 @@ Skip this step for full self-host. — NOT in `.mcp.json`. An older `docs/byod.md` revision may say to put these in `.mcp.json`; that env never reaches the edge function, only your local MCP client process, so it has no effect on a deployed function. -4. With that env set, run `lorekit bootstrap` (follow what it prints) and - then `lorekit doctor` to confirm the storage connection is healthy. +4. `lorekit bootstrap` and `lorekit doctor` read `LOREKIT_STORAGE_*` from + your **local shell**, not from the Edge Function secrets set in 6.3 — so + pass them inline, matching docs/byod.md: + + ```bash + LOREKIT_STORAGE_URL=https://.supabase.co \ + LOREKIT_STORAGE_SERVICE_KEY= \ + lorekit bootstrap + + LOREKIT_STORAGE_URL=https://.supabase.co \ + LOREKIT_STORAGE_ANON_KEY= \ + lorekit doctor + ``` + + Without those vars in the shell, `doctor` silently skips the BYOD check. **Caveat — repeat this to the user:** the dashboard, REST API, remote CLI commands, and hooks all read the **function project's own DB**. They do @@ -152,10 +165,12 @@ commands, and hooks all read the **function project's own DB**. They do view into BYOD data — it is intentionally invisible to everything except the `memory.*` MCP tools and the `lorekit` CLI talking directly to that endpoint. -**Verify:** `lorekit doctor` reports the BYOD storage connection healthy, with -no error reading or resolving `LOREKIT_STORAGE_URL`. **On failure:** confirm -all three secrets are set on the function project (not the storage project), -and that the schema from Step 6.2 applied cleanly. +**Verify:** the inline-env `lorekit doctor` run from 6.4 prints a **passing** +`byod storage` line. No `byod storage` line at all means the check was +skipped — the vars weren't set in that shell; re-run with them inline. +**On failure:** check the storage URL/anon key you passed, that the schema +from Step 6.2 applied cleanly, and that all three secrets are set on the +function project (not the storage project). ## Step 7 — Dashboard and API token From c18d307a8a62717d2f367454a33730fbf470e12f Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:34:41 +0200 Subject: [PATCH 05/19] address review comment: add --create-dirs to the self-host skill curl one-liner Addresses @mthines's comment: https://github.com/mthines/lorekit/pull/694#discussion_r4302213506 Refs: https://github.com/mthines/lorekit/pull/694 --- packages/web/src/content/docs/self-host.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index 5dc2dfa80..1392697df 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -103,7 +103,7 @@ commands. Claude plugin users get it automatically with directly: ```bash -curl -o ~/.claude/skills/lorekit-self-host/SKILL.md \ +curl --create-dirs -o ~/.claude/skills/lorekit-self-host/SKILL.md \ https://raw.githubusercontent.com/mthines/lorekit/main/packages/cli/skill/lorekit-self-host/SKILL.md ``` From bbe0ed89e6f12d8defe6366cc5a025459cf596cf Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:34:54 +0200 Subject: [PATCH 06/19] address review comment: correct what reads a committed mcp.endpoint Addresses @mthines's comment: https://github.com/mthines/lorekit/pull/694#discussion_r4302213513 Refs: https://github.com/mthines/lorekit/pull/694 --- packages/web/src/content/docs/self-host.mdx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index 1392697df..d131c0d5c 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -80,8 +80,12 @@ npx @lorekit/cli install \ --token lk_rw_ ``` -(or export `LOREKIT_MCP_URL` / commit `"mcp.endpoint"` in `.lorekit.json` -instead of passing `--endpoint` every time) +(or export `LOREKIT_MCP_URL` instead of passing `--endpoint` every time — +`install` reads only `--endpoint`, then `LOREKIT_MCP_URL`, then +`LOREKIT_ENDPOINT`. A committed `"mcp.endpoint"` in `.lorekit.json` is never +read by `install`; it is only a fallback for the remote CLI commands and +hooks when `.mcp.json` has no LoreKit entry, and `.mcp.json` wins whenever it +has one.) From 4dd1ac49b86ad5d1bd11a87a8807f5cab3281536 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:35:16 +0200 Subject: [PATCH 07/19] address review comment: carve out self-host instructions from the no-placeholder endpoint rule Addresses @mthines's comment: pr-reviewer report https://github.com/mthines/lorekit/pull/694#issuecomment-6232322098 Refs: https://github.com/mthines/lorekit/pull/694 --- CLAUDE.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3ef9391af..c02537a76 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -175,7 +175,8 @@ observable effect. | `README.md` | repo root | The change alters the pitch, the install path, or the package map. | Writing rules for all of the above: always the concrete MCP endpoint, never a `` placeholder -(see Endpoints and Key decisions); tag every fenced code block with its language; keep `llms.txt` +(see Endpoints and Key decisions — self-hosting instructions for the reader's OWN project are the one +exception); tag every fenced code block with its language; keep `llms.txt` consistent with the MDX docs — when the two disagree, agents read `llms.txt` and get it wrong. Definition of done: the diff either touches the surfaces above, or the PR description says in one @@ -365,7 +366,12 @@ Trace-context propagation (W3C `traceparent` — who sends/receives, the origin The production Supabase project ref is **`pqokxlhvnosogizsjztg`** (static). Always write the concrete endpoint below in any user-facing surface — dashboard copy, Learn pages, config examples, docs — **NEVER** a `` / `` -placeholder for the MCP server URL. +placeholder for the MCP server URL. The one exception is a self-hosting +instruction addressed to someone running THEIR OWN Supabase project +(`docs/install.md`, `docs/byod.md`, the `lorekit-self-host` skill, and the +self-host sections of the `self-host` docs page), where `` is +correct; anywhere the HOSTED endpoint is meant, the concrete +`pqokxlhvnosogizsjztg` URL remains mandatory. | URL | Auth | Purpose | |-----|------|---------| From 42b20728fa150a313f5ba2c6da70b5c6aaf55b5d Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:35:23 +0200 Subject: [PATCH 08/19] address review comment: word the BYOD "Never" rule version-neutrally Addresses @mthines's comment: pr-reviewer report https://github.com/mthines/lorekit/pull/694#issuecomment-6232322098 Refs: https://github.com/mthines/lorekit/pull/694 --- packages/cli/skill/lorekit-self-host/SKILL.md | 6 +++--- plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index dc85825d1..fa87c0a40 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -234,8 +234,8 @@ function project's — since that is the whole point of the BYOD layering. ## Never - Never print or echo a token, API key, or service-role key value. -- Never apply the BYOD storage overlay to the function project's own - database — it only ever goes on the separate storage project; the function - project gets the Step 6.3 secrets only. +- Never apply the BYOD storage-only schema steps from docs/byod.md to the + function project's own database — they only ever go on the separate + storage project; the function project gets the Step 6.3 secrets only. - Never point a self-hosted deployment's agent connection at the hosted endpoint, and never assume a `lorekit.io`-minted token works against it. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index dc85825d1..fa87c0a40 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -234,8 +234,8 @@ function project's — since that is the whole point of the BYOD layering. ## Never - Never print or echo a token, API key, or service-role key value. -- Never apply the BYOD storage overlay to the function project's own - database — it only ever goes on the separate storage project; the function - project gets the Step 6.3 secrets only. +- Never apply the BYOD storage-only schema steps from docs/byod.md to the + function project's own database — they only ever go on the separate + storage project; the function project gets the Step 6.3 secrets only. - Never point a self-hosted deployment's agent connection at the hosted endpoint, and never assume a `lorekit.io`-minted token works against it. From e8c208e6157d94b0ad792064fcbd4ffe28b083bd Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:35:30 +0200 Subject: [PATCH 09/19] address review comment: stop presenting the hosted-URL constant as an env var Addresses @mthines's comment: pr-reviewer report https://github.com/mthines/lorekit/pull/694#issuecomment-6232322098 Refs: https://github.com/mthines/lorekit/pull/694 --- packages/cli/skill/lorekit-self-host/SKILL.md | 5 +++-- plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index fa87c0a40..81d9df401 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -206,8 +206,9 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). **Warning:** re-running `lorekit install` **without** `--endpoint` rewrites -the MCP entry back to the hosted URL (`install.mjs` falls back to -`LOREKIT_MCP_ENDPOINT` when `--endpoint` is omitted). Always pass +the MCP entry back to the hosted URL: when neither `--endpoint` nor the +`LOREKIT_MCP_URL` / `LOREKIT_ENDPOINT` env vars are set, `install` uses the +hosted URL hardcoded in `install.mjs`. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every subsequent install. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index fa87c0a40..81d9df401 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -206,8 +206,9 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). **Warning:** re-running `lorekit install` **without** `--endpoint` rewrites -the MCP entry back to the hosted URL (`install.mjs` falls back to -`LOREKIT_MCP_ENDPOINT` when `--endpoint` is omitted). Always pass +the MCP entry back to the hosted URL: when neither `--endpoint` nor the +`LOREKIT_MCP_URL` / `LOREKIT_ENDPOINT` env vars are set, `install` uses the +hosted URL hardcoded in `install.mjs`. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every subsequent install. From 91399a2c73acdc706a7e0706919d949127fdd580 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:43:05 +0200 Subject: [PATCH 10/19] docs(self-host): hooks check ~/.claude.json before mcp.endpoint --- packages/web/src/content/docs/self-host.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index d131c0d5c..bb72ce4e3 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -83,9 +83,11 @@ npx @lorekit/cli install \ (or export `LOREKIT_MCP_URL` instead of passing `--endpoint` every time — `install` reads only `--endpoint`, then `LOREKIT_MCP_URL`, then `LOREKIT_ENDPOINT`. A committed `"mcp.endpoint"` in `.lorekit.json` is never -read by `install`; it is only a fallback for the remote CLI commands and -hooks when `.mcp.json` has no LoreKit entry, and `.mcp.json` wins whenever it -has one.) +read by `install`. The remote CLI commands and hooks check the project's +`.mcp.json` first, then the global `~/.claude.json`, and use `mcp.endpoint` +only when neither has a LoreKit entry. A leftover hosted +`install --global` entry in `~/.claude.json` therefore still wins over a +committed `mcp.endpoint`, so re-run the global install with `--endpoint` too.) From fea0e2aad367a0c867f352211985321af00e44b6 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 11:43:05 +0200 Subject: [PATCH 11/19] docs(decisions): record the self-host placeholder exception --- docs/decisions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decisions.md b/docs/decisions.md index 9118d1cb7..505f7c00f 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -14,7 +14,7 @@ rationale before proposing a change that reverses one. ## MCP server endpoint is a static production URL -**The MCP server endpoint is a static production URL — never write a `` placeholder in user-facing content.** The hosted MCP server lives at a fixed Supabase project ref (`pqokxlhvnosogizsjztg`), so any URL an end user sees — Getting started / Learn pages, `.lorekit.json` / config examples, dashboard copy, docs — MUST use the concrete `https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp` (the same literal `GettingStartedContent.tsx`'s `MCP_URL` uses), NOT `https://.supabase.co/...` or ``. A placeholder is only acceptable in migration/deploy tooling that legitimately targets multiple projects (e.g. the `resolveProjectConnection` `` guard, which *rejects* an unconfigured placeholder). See the Endpoints table. +**The MCP server endpoint is a static production URL — never write a `` placeholder in user-facing content.** The hosted MCP server lives at a fixed Supabase project ref (`pqokxlhvnosogizsjztg`), so any URL an end user sees — Getting started / Learn pages, `.lorekit.json` / config examples, dashboard copy, docs — MUST use the concrete `https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp` (the same literal `GettingStartedContent.tsx`'s `MCP_URL` uses), NOT `https://.supabase.co/...` or ``. A placeholder is only acceptable in migration/deploy tooling that legitimately targets multiple projects (e.g. the `resolveProjectConnection` `` guard, which *rejects* an unconfigured placeholder) and in self-hosting instructions addressed to someone running their OWN Supabase project (`docs/install.md`, `docs/byod.md`, the `lorekit-self-host` skill, the self-host sections of the `/docs/self-host` page), where `` is the correct thing to write. Wherever the HOSTED endpoint is meant, the concrete URL stays mandatory. See the Endpoints table. ## Lore Explorer filters through ONE two-level command menu From 39727ba4f5eff8c2e23aee758d32ed1c09c303e6 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:00:49 +0200 Subject: [PATCH 12/19] docs(self-host): health returns "db":"ok", and a 503 "db":"error" on failure The health function answers 200 {"status":"ok","db":"ok",...} and 503 {"status":"degraded","db":"error",...}. install.md's Step 6 and smoke-test blocks, the self-host page, and the skill all promised "db":true / "db":false, so a self-hoster comparing output literally would read a healthy deploy as wrong. Found by local-verify. The Troubleshooting row is left for after #690 lands: that table sits inside #690's install.md hunk. --- docs/install.md | 4 ++-- packages/cli/skill/lorekit-self-host/SKILL.md | 5 +++-- packages/web/src/content/docs/self-host.mdx | 2 +- plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 5 +++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/install.md b/docs/install.md index 65fc89353..8bc755e39 100644 --- a/docs/install.md +++ b/docs/install.md @@ -117,7 +117,7 @@ Verify the deployment: ```bash pnpm nx health supabase -# expected: {"status":"ok","db":true} +# expected: HTTP 200 {"status":"ok","version":"1.0.0","db":"ok","ts":"…"} curl -s -o /dev/null -w '%{http_code}' https://.supabase.co/functions/v1/memories # expected: 401 (deployed, auth required) — 404 means it did not deploy @@ -258,7 +258,7 @@ other repos' secrets are untouched. ```bash # 1. Health check curl https://.supabase.co/functions/v1/health -# → {"status":"ok","db":true} +# → {"status":"ok","version":"1.0.0","db":"ok","ts":"…"} # 2. Write a test lesson curl -X POST https://.supabase.co/functions/v1/mcp \ diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index 81d9df401..6df19094a 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -118,8 +118,9 @@ supabase functions deploy --no-verify-jwt --project-ref **Verify, in order:** 1. Health check: `curl https://.supabase.co/functions/v1/health` - must return `{"status":"ok","db":true}`. `{"db":false}` means the function - can't reach the DB — recheck the secrets from Step 4. + must return HTTP 200 with `"status":"ok"` and `"db":"ok"`. A 503 with + `"status":"degraded"` and `"db":"error"` means the function can't reach the + DB — recheck the secrets from Step 4. 2. `memories` deployed: `curl -s -o /dev/null -w '%{http_code}' https://.supabase.co/functions/v1/memories` must return `401` (deployed, auth required). A `404` means the function wasn't deployed — re-run the deploy command above; it deploys every diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index bb72ce4e3..5f2d547c5 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -48,7 +48,7 @@ Verify with a health check: ```bash curl https://.supabase.co/functions/v1/health -# → {"status":"ok","db":true} +# → {"status":"ok","version":"1.0.0","db":"ok","ts":"…"} ``` diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index 81d9df401..6df19094a 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -118,8 +118,9 @@ supabase functions deploy --no-verify-jwt --project-ref **Verify, in order:** 1. Health check: `curl https://.supabase.co/functions/v1/health` - must return `{"status":"ok","db":true}`. `{"db":false}` means the function - can't reach the DB — recheck the secrets from Step 4. + must return HTTP 200 with `"status":"ok"` and `"db":"ok"`. A 503 with + `"status":"degraded"` and `"db":"error"` means the function can't reach the + DB — recheck the secrets from Step 4. 2. `memories` deployed: `curl -s -o /dev/null -w '%{http_code}' https://.supabase.co/functions/v1/memories` must return `401` (deployed, auth required). A `404` means the function wasn't deployed — re-run the deploy command above; it deploys every From c417dd3db52618aea055fc75c072eb4184c12618 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:01:15 +0200 Subject: [PATCH 13/19] docs(self-host): put the hosted URL in a code block so the page fits a phone At 390px the inline 57-character URL in the opening callout could not wrap, so the whole page scrolled sideways. A fenced block scrolls on its own, as the other docs pages already do for this URL. Found by local-verify. --- packages/web/src/content/docs/self-host.mdx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index 5f2d547c5..0fff76b57 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -7,10 +7,14 @@ keywords: ["self-host", "self-hosting", "byod", "bring your own database", "supa -The hosted instance at `lorekit.io` — MCP endpoint -`https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp` — needs none of -this; it's ready to use today. This page is for running LoreKit on -infrastructure you control. +The hosted instance at `lorekit.io` needs none of this; it's ready to use +today, at this MCP endpoint: + +```text +https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp +``` + +This page is for running LoreKit on infrastructure you control. From f2c364286f34bc171940c41098cad63e5a21c9ce Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:02:11 +0200 Subject: [PATCH 14/19] docs(self-host): say exactly when install falls back to the hosted URL A plain re-run on an already-installed project returns early and leaves .mcp.json alone. The hosted fallback applies when install WRITES the entry without --endpoint: a --force reinstall, a new project, a global install, or a missing entry. The advice (always pass --endpoint or export LOREKIT_MCP_URL) is unchanged. Found by local-verify. --- docs/install.md | 9 ++++++--- packages/cli/skill/lorekit-self-host/SKILL.md | 13 +++++++------ packages/web/src/content/docs/self-host.mdx | 7 ++++--- .../skills/lorekit-self-host/SKILL.md | 13 +++++++------ 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/install.md b/docs/install.md index 8bc755e39..7c62bffa9 100644 --- a/docs/install.md +++ b/docs/install.md @@ -194,9 +194,12 @@ npx @lorekit/cli install \ --token lk_rw_ ``` -Re-running `lorekit install` **without** `--endpoint` rewrites the MCP entry -back to the hosted URL — always pass `--endpoint`, or export -`LOREKIT_MCP_URL` in your shell profile, on every subsequent install. +Any `lorekit install` that writes the MCP entry **without** `--endpoint` — +a `--force` reinstall, a new project, a global install, or a project whose +`.mcp.json` lost its LoreKit entry — points it at the hosted URL. (A plain +re-run on an already-installed project leaves `.mcp.json` untouched.) Always +pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every +install. **Claude Code on the web** clones the repo fresh into an ephemeral container, so it only sees a committed, repo-root `.mcp.json`. Add `--mcp-json` to also write diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index 6df19094a..58df9be19 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -206,12 +206,13 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct (or export `LOREKIT_MCP_URL=https://.supabase.co/functions/v1/mcp` and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). -**Warning:** re-running `lorekit install` **without** `--endpoint` rewrites -the MCP entry back to the hosted URL: when neither `--endpoint` nor the -`LOREKIT_MCP_URL` / `LOREKIT_ENDPOINT` env vars are set, `install` uses the -hosted URL hardcoded in `install.mjs`. Always pass -`--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every -subsequent install. +**Warning:** any `lorekit install` that writes the MCP entry **without** +`--endpoint` — a `--force` reinstall, a new project, a global install, or a +project whose `.mcp.json` lost its LoreKit entry — uses the hosted URL +hardcoded in `install.mjs` when neither the `LOREKIT_MCP_URL` nor the +`LOREKIT_ENDPOINT` env var is set. (A plain re-run on an already-installed +project leaves `.mcp.json` untouched.) Always pass `--endpoint`, or export +`LOREKIT_MCP_URL` in your shell profile, on every install. **Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports connectivity + authentication passing. **On failure:** re-check the endpoint diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index 0fff76b57..915eab648 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -95,9 +95,10 @@ committed `mcp.endpoint`, so re-run the global install with `--endpoint` too.) -Re-running `npx @lorekit/cli install` **without** `--endpoint` rewrites the -MCP entry back to the hosted URL. Always pass `--endpoint`, or export -`LOREKIT_MCP_URL` in your shell profile, on every subsequent install. +Any `npx @lorekit/cli install` that writes the MCP entry **without** +`--endpoint` — a `--force` reinstall, a new project, or a global install — +points it at the hosted URL. Always pass `--endpoint`, or export +`LOREKIT_MCP_URL` in your shell profile, on every install. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index 6df19094a..58df9be19 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -206,12 +206,13 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct (or export `LOREKIT_MCP_URL=https://.supabase.co/functions/v1/mcp` and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). -**Warning:** re-running `lorekit install` **without** `--endpoint` rewrites -the MCP entry back to the hosted URL: when neither `--endpoint` nor the -`LOREKIT_MCP_URL` / `LOREKIT_ENDPOINT` env vars are set, `install` uses the -hosted URL hardcoded in `install.mjs`. Always pass -`--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every -subsequent install. +**Warning:** any `lorekit install` that writes the MCP entry **without** +`--endpoint` — a `--force` reinstall, a new project, a global install, or a +project whose `.mcp.json` lost its LoreKit entry — uses the hosted URL +hardcoded in `install.mjs` when neither the `LOREKIT_MCP_URL` nor the +`LOREKIT_ENDPOINT` env var is set. (A plain re-run on an already-installed +project leaves `.mcp.json` untouched.) Always pass `--endpoint`, or export +`LOREKIT_MCP_URL` in your shell profile, on every install. **Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports connectivity + authentication passing. **On failure:** re-check the endpoint From fe8616117b63424baa339ec97236b21c4c339c8f Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:03:29 +0200 Subject: [PATCH 15/19] docs(self-host): doctor can't catch an endpoint missing /mcp, so check it lorekit doctor checks connectivity and auth over REST, and the REST base drops the /mcp suffix, so --endpoint .../functions/v1 passes every doctor check while mcp-remote gets a 404. Step 8's gate now also checks the suffix and points at Step 9 as the real MCP check. Found by local-verify. --- packages/cli/skill/lorekit-self-host/SKILL.md | 6 +++++- plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index 58df9be19..db73e8470 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -215,7 +215,11 @@ project leaves `.mcp.json` untouched.) Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every install. **Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports -connectivity + authentication passing. **On failure:** re-check the endpoint +connectivity + authentication passing, **and** that endpoint line ends in +`/functions/v1/mcp`. Check the suffix yourself: `doctor` probes over REST, +which drops the `/mcp` suffix, so an endpoint missing it still passes +`doctor` while the agent's MCP client gets a 404. Step 9 is the real MCP +check. **On failure:** re-check the endpoint URL (must end in `/functions/v1/mcp`) and that the token matches the permission tier the connection needs. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index 58df9be19..db73e8470 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -215,7 +215,11 @@ project leaves `.mcp.json` untouched.) Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every install. **Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports -connectivity + authentication passing. **On failure:** re-check the endpoint +connectivity + authentication passing, **and** that endpoint line ends in +`/functions/v1/mcp`. Check the suffix yourself: `doctor` probes over REST, +which drops the `/mcp` suffix, so an endpoint missing it still passes +`doctor` while the agent's MCP client gets a 404. Step 9 is the real MCP +check. **On failure:** re-check the endpoint URL (must end in `/functions/v1/mcp`) and that the token matches the permission tier the connection needs. From 1ae4259c1f57a0cc3b0796389ce22766eafa2f5a Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:03:45 +0200 Subject: [PATCH 16/19] docs(self-host): show the delete call Step 9 asks for Step 9 said to write, read, then delete the test lesson but showed no delete call. Add memory.delete and note that it archives, so a later read returns null. Found by local-verify. --- packages/cli/skill/lorekit-self-host/SKILL.md | 4 ++++ plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index db73e8470..c0586cc13 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -230,8 +230,12 @@ Write a `global` test lesson, read it back, then delete it: ```json memory.write { scope: "global", key: "self-host-test", value: "It works." } memory.read { scope: "global", key: "self-host-test" } +memory.delete { scope: "global", key: "self-host-test" } ``` +`memory.delete` archives the lesson (`"archived":true`); a later +`memory.read` returns `null`. + **Verify:** the read returns the value you wrote. **For BYOD**, also confirm the row landed in the **storage project's** `memories` table — not the function project's — since that is the whole point of the BYOD layering. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index db73e8470..c0586cc13 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -230,8 +230,12 @@ Write a `global` test lesson, read it back, then delete it: ```json memory.write { scope: "global", key: "self-host-test", value: "It works." } memory.read { scope: "global", key: "self-host-test" } +memory.delete { scope: "global", key: "self-host-test" } ``` +`memory.delete` archives the lesson (`"archived":true`); a later +`memory.read` returns `null`. + **Verify:** the read returns the value you wrote. **For BYOD**, also confirm the row landed in the **storage project's** `memories` table — not the function project's — since that is the whole point of the BYOD layering. From 8eefb4ef9fac86a6f6c08bf83ecd8edaf1a284d1 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:16:14 +0200 Subject: [PATCH 17/19] docs(self-host): --hooks, --mcp-json and skill upgrades also rewrite the endpoint The early return in install only fires for a plain re-run on a fully installed project; --hooks, --mcp-json, --completions, --force, or a missing skill all rewrite the MCP entry with the hosted URL when no --endpoint is given. Say that instead of listing a subset. --- docs/install.md | 12 ++++++------ packages/cli/skill/lorekit-self-host/SKILL.md | 12 ++++++------ packages/web/src/content/docs/self-host.mdx | 6 +++--- .../lorekit-claude/skills/lorekit-self-host/SKILL.md | 12 ++++++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/install.md b/docs/install.md index 7c62bffa9..da6d3414f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -194,12 +194,12 @@ npx @lorekit/cli install \ --token lk_rw_ ``` -Any `lorekit install` that writes the MCP entry **without** `--endpoint` — -a `--force` reinstall, a new project, a global install, or a project whose -`.mcp.json` lost its LoreKit entry — points it at the hosted URL. (A plain -re-run on an already-installed project leaves `.mcp.json` untouched.) Always -pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every -install. +Almost every `lorekit install` rewrites the MCP entry — a new project, a +global install, `--force`, `--hooks`, `--mcp-json`, or a re-run after an +upgrade adds a skill — and **without** `--endpoint` it writes the hosted URL. +Only a plain re-run on a fully installed project leaves `.mcp.json` alone. +Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, +on every install. **Claude Code on the web** clones the repo fresh into an ephemeral container, so it only sees a committed, repo-root `.mcp.json`. Add `--mcp-json` to also write diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index c0586cc13..e8b918bc9 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -206,12 +206,12 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct (or export `LOREKIT_MCP_URL=https://.supabase.co/functions/v1/mcp` and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). -**Warning:** any `lorekit install` that writes the MCP entry **without** -`--endpoint` — a `--force` reinstall, a new project, a global install, or a -project whose `.mcp.json` lost its LoreKit entry — uses the hosted URL -hardcoded in `install.mjs` when neither the `LOREKIT_MCP_URL` nor the -`LOREKIT_ENDPOINT` env var is set. (A plain re-run on an already-installed -project leaves `.mcp.json` untouched.) Always pass `--endpoint`, or export +**Warning:** almost every `lorekit install` rewrites the MCP entry — a new +project, a global install, `--force`, `--hooks`, `--mcp-json`, or a re-run +after an upgrade adds a skill. Without `--endpoint` (and with neither +`LOREKIT_MCP_URL` nor `LOREKIT_ENDPOINT` set) it writes the hosted URL +hardcoded in `install.mjs`. Only a plain re-run on a fully installed project +leaves `.mcp.json` alone. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every install. **Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index 915eab648..c6c0bf99f 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -95,9 +95,9 @@ committed `mcp.endpoint`, so re-run the global install with `--endpoint` too.) -Any `npx @lorekit/cli install` that writes the MCP entry **without** -`--endpoint` — a `--force` reinstall, a new project, or a global install — -points it at the hosted URL. Always pass `--endpoint`, or export +Almost every `npx @lorekit/cli install` rewrites the MCP entry — a new +project, a global install, `--force`, `--hooks`, or a re-run after an upgrade +adds a skill — and **without** `--endpoint` it writes the hosted URL. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every install. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index c0586cc13..e8b918bc9 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -206,12 +206,12 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct (or export `LOREKIT_MCP_URL=https://.supabase.co/functions/v1/mcp` and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). -**Warning:** any `lorekit install` that writes the MCP entry **without** -`--endpoint` — a `--force` reinstall, a new project, a global install, or a -project whose `.mcp.json` lost its LoreKit entry — uses the hosted URL -hardcoded in `install.mjs` when neither the `LOREKIT_MCP_URL` nor the -`LOREKIT_ENDPOINT` env var is set. (A plain re-run on an already-installed -project leaves `.mcp.json` untouched.) Always pass `--endpoint`, or export +**Warning:** almost every `lorekit install` rewrites the MCP entry — a new +project, a global install, `--force`, `--hooks`, `--mcp-json`, or a re-run +after an upgrade adds a skill. Without `--endpoint` (and with neither +`LOREKIT_MCP_URL` nor `LOREKIT_ENDPOINT` set) it writes the hosted URL +hardcoded in `install.mjs`. Only a plain re-run on a fully installed project +leaves `.mcp.json` alone. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every install. **Verify:** `npx @lorekit/cli doctor` shows your endpoint and reports From 0c3ebe8c6d6fc92449c330b9eb290890d07f0556 Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:40:26 +0200 Subject: [PATCH 18/19] docs(self-host): list --completions among the install paths that rewrite .mcp.json Also align self-host.mdx with install.md (--mcp-json, the plain re-run exception) and make the skill description admit Step 9's throwaway write/read/delete round-trip. --- docs/install.md | 2 +- packages/cli/skill/lorekit-self-host/SKILL.md | 7 ++++--- packages/web/src/content/docs/self-host.mdx | 6 ++++-- plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md | 7 ++++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/install.md b/docs/install.md index da6d3414f..7f70a1d56 100644 --- a/docs/install.md +++ b/docs/install.md @@ -195,7 +195,7 @@ npx @lorekit/cli install \ ``` Almost every `lorekit install` rewrites the MCP entry — a new project, a -global install, `--force`, `--hooks`, `--mcp-json`, or a re-run after an +global install, `--force`, `--hooks`, `--mcp-json`, `--completions `, or a re-run after an upgrade adds a skill — and **without** `--endpoint` it writes the hosted URL. Only a plain re-run on a fully installed project leaves `.mcp.json` alone. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, diff --git a/packages/cli/skill/lorekit-self-host/SKILL.md b/packages/cli/skill/lorekit-self-host/SKILL.md index e8b918bc9..3ce6fd436 100644 --- a/packages/cli/skill/lorekit-self-host/SKILL.md +++ b/packages/cli/skill/lorekit-self-host/SKILL.md @@ -8,8 +8,8 @@ description: > dashboard/token → connect-your-agent → write/read round-trip, each step a gate: do the thing, verify it, stop and diagnose if the verification fails. Links out to docs/install.md and docs/byod.md rather than duplicating their - commands. Slash-only — not installed by `lorekit install`, and never reads - or writes a lesson itself. Use when deploying your own LoreKit instance, not + commands. Slash-only — not installed by `lorekit install`. Its only memory + call is Step 9's throwaway `self-host-test` write/read/delete round-trip. Use when deploying your own LoreKit instance, not for everyday read/write (see lorekit-memory) or store maintenance (see lorekit-groom). Triggers ONLY on explicit invocation: "/lorekit-self-host". @@ -207,7 +207,8 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). **Warning:** almost every `lorekit install` rewrites the MCP entry — a new -project, a global install, `--force`, `--hooks`, `--mcp-json`, or a re-run +project, a global install, `--force`, `--hooks`, `--mcp-json`, +`--completions `, or a re-run after an upgrade adds a skill. Without `--endpoint` (and with neither `LOREKIT_MCP_URL` nor `LOREKIT_ENDPOINT` set) it writes the hosted URL hardcoded in `install.mjs`. Only a plain re-run on a fully installed project diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index c6c0bf99f..71f638822 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -96,8 +96,10 @@ committed `mcp.endpoint`, so re-run the global install with `--endpoint` too.) Almost every `npx @lorekit/cli install` rewrites the MCP entry — a new -project, a global install, `--force`, `--hooks`, or a re-run after an upgrade -adds a skill — and **without** `--endpoint` it writes the hosted URL. Always pass `--endpoint`, or export +project, a global install, `--force`, `--hooks`, `--mcp-json`, `--completions `, +or a re-run after an upgrade adds a skill — and **without** `--endpoint` it +writes the hosted URL. Only a plain re-run on a fully installed project leaves +`.mcp.json` alone. Always pass `--endpoint`, or export `LOREKIT_MCP_URL` in your shell profile, on every install. diff --git a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md index e8b918bc9..3ce6fd436 100644 --- a/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md +++ b/plugins/lorekit-claude/skills/lorekit-self-host/SKILL.md @@ -8,8 +8,8 @@ description: > dashboard/token → connect-your-agent → write/read round-trip, each step a gate: do the thing, verify it, stop and diagnose if the verification fails. Links out to docs/install.md and docs/byod.md rather than duplicating their - commands. Slash-only — not installed by `lorekit install`, and never reads - or writes a lesson itself. Use when deploying your own LoreKit instance, not + commands. Slash-only — not installed by `lorekit install`. Its only memory + call is Step 9's throwaway `self-host-test` write/read/delete round-trip. Use when deploying your own LoreKit instance, not for everyday read/write (see lorekit-memory) or store maintenance (see lorekit-groom). Triggers ONLY on explicit invocation: "/lorekit-self-host". @@ -207,7 +207,8 @@ npx @lorekit/cli install --endpoint https://.supabase.co/funct and `LOREKIT_TOKEN` instead of passing `--endpoint`/`--token` each time). **Warning:** almost every `lorekit install` rewrites the MCP entry — a new -project, a global install, `--force`, `--hooks`, `--mcp-json`, or a re-run +project, a global install, `--force`, `--hooks`, `--mcp-json`, +`--completions `, or a re-run after an upgrade adds a skill. Without `--endpoint` (and with neither `LOREKIT_MCP_URL` nor `LOREKIT_ENDPOINT` set) it writes the hosted URL hardcoded in `install.mjs`. Only a plain re-run on a fully installed project From 22f1140d4303306d4c5cff6c68eddfa4ca7348fb Mon Sep 17 00:00:00 2001 From: mthines Date: Sat, 17 Oct 2026 12:46:03 +0200 Subject: [PATCH 19/19] docs(self-host): say the GitHub App needs its own registration when self-hosting --- packages/web/src/content/docs/self-host.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/web/src/content/docs/self-host.mdx b/packages/web/src/content/docs/self-host.mdx index 71f638822..4adf95adf 100644 --- a/packages/web/src/content/docs/self-host.mdx +++ b/packages/web/src/content/docs/self-host.mdx @@ -125,7 +125,9 @@ curl --create-dirs -o ~/.claude/skills/lorekit-self-host/SKILL.md \ **Next steps:** once you're connected, see [Remote storage](/docs/remote) for -the token-scoping, CI, and GitHub App features that work the same way against -a self-hosted endpoint. +token scoping and CI, which work the same way against a self-hosted endpoint. +The GitHub App does not: it is off until you register your own App, point its +webhook at your project, and set `GITHUB_APP_ENABLED=true` — see the +[GitHub App guide](https://github.com/mthines/lorekit/blob/main/docs/github-app.md).