Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b5858fe
feat(self-host): add /lorekit-self-host skill, a public self-host doc…
mthines Oct 17, 2026
b83f683
test(install): cover the self-host endpoint path and lock the hosted …
mthines Oct 17, 2026
aea2d1d
address review comment: use port 4200 for the locally-run dashboard
mthines Oct 17, 2026
44cee13
address review comment: run BYOD bootstrap/doctor with storage vars i…
mthines Oct 17, 2026
c18d307
address review comment: add --create-dirs to the self-host skill curl…
mthines Oct 17, 2026
bbe0ed8
address review comment: correct what reads a committed mcp.endpoint
mthines Oct 17, 2026
4dd1ac4
address review comment: carve out self-host instructions from the no-…
mthines Oct 17, 2026
42b2072
address review comment: word the BYOD "Never" rule version-neutrally
mthines Oct 17, 2026
e8c208e
address review comment: stop presenting the hosted-URL constant as an…
mthines Oct 17, 2026
91399a2
docs(self-host): hooks check ~/.claude.json before mcp.endpoint
mthines Oct 17, 2026
fea0e2a
docs(decisions): record the self-host placeholder exception
mthines Oct 17, 2026
39727ba
docs(self-host): health returns "db":"ok", and a 503 "db":"error" on …
mthines Oct 17, 2026
c417dd3
docs(self-host): put the hosted URL in a code block so the page fits …
mthines Oct 17, 2026
f2c3642
docs(self-host): say exactly when install falls back to the hosted URL
mthines Oct 17, 2026
fe86161
docs(self-host): doctor can't catch an endpoint missing /mcp, so chec…
mthines Oct 17, 2026
1ae4259
docs(self-host): show the delete call Step 9 asks for
mthines Oct 17, 2026
8eefb4e
docs(self-host): --hooks, --mcp-json and skill upgrades also rewrite …
mthines Oct 17, 2026
0c3ebe8
docs(self-host): list --completions among the install paths that rewr…
mthines Oct 17, 2026
22f1140
docs(self-host): say the GitHub App needs its own registration when s…
mthines Oct 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<ref>` 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
Expand Down Expand Up @@ -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 `<ref>` / `<project-ref>`
placeholder for the MCP server URL.
placeholder for the MCP server URL. The one exception is a self-hosting
Comment thread
mthines marked this conversation as resolved.
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 `<your-project-ref>` is
correct; anywhere the HOSTED endpoint is meant, the concrete
`pqokxlhvnosogizsjztg` URL remains mandatory.

| URL | Auth | Purpose |
|-----|------|---------|
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

</details>

## One store, every agent
Expand Down Expand Up @@ -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.

</details>
Expand Down
17 changes: 13 additions & 4 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<ref>` 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://<ref>.supabase.co/...` or `<project-ref>`. A placeholder is only acceptable in migration/deploy tooling that legitimately targets multiple projects (e.g. the `resolveProjectConnection` `<project-ref>` guard, which *rejects* an unconfigured placeholder). See the Endpoints table.
**The MCP server endpoint is a static production URL — never write a `<ref>` 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://<ref>.supabase.co/...` or `<project-ref>`. A placeholder is only acceptable in migration/deploy tooling that legitimately targets multiple projects (e.g. the `resolveProjectConnection` `<project-ref>` 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 `<your-project-ref>` 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

Expand Down
99 changes: 68 additions & 31 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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://<your-project-ref>.supabase.co`

```bash
supabase link --project-ref pqokxlhvnosogizsjztg
supabase link --project-ref <your-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=<your-project-ref>" > .env.local
```

---
Expand All @@ -59,8 +62,8 @@ 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://<your-vercel-url>.vercel.app` (or `http://localhost:3000` for local dev)
- **Authorization callback URL:** `https://pqokxlhvnosogizsjztg.supabase.co/auth/v1/callback`
- **Homepage URL:** `https://<your-vercel-url>.vercel.app` (or `http://localhost:4200` for local dev)
- **Authorization callback URL:** `https://<your-project-ref>.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

Expand All @@ -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://<your-project-ref>.supabase.co \
SUPABASE_ANON_KEY=<publishable-key> \
SUPABASE_SERVICE_ROLE_KEY=<service-role-key> \
ALLOWED_ORIGINS=https://<your-dashboard-domain> \
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingress.europe-west4.gcp.dash0-dev.com \
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <dash0-token>" \
VERCEL_ENV=production \
--project-ref pqokxlhvnosogizsjztg
--project-ref <your-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
Expand All @@ -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 <your-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}
# expected: HTTP 200 {"status":"ok","version":"1.0.0","db":"ok","ts":"…"}

curl -s -o /dev/null -w '%{http_code}' https://<your-project-ref>.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:4200/api/auth/callback` to
Supabase → Auth → URL Configuration → Redirect URLs, then run
`pnpm nx serve web` (it listens on port 4200).

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
Expand All @@ -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://<your-project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY <publishable-key>
NEXT_PUBLIC_SUPABASE_PROJECT_REF pqokxlhvnosogizsjztg
NEXT_PUBLIC_SUPABASE_PROJECT_REF <your-project-ref>
NEXT_PUBLIC_APP_URL https://<your-vercel-url>.vercel.app
NEXT_PUBLIC_DASH0_OTLP_ENDPOINT https://ingress.europe-west4.gcp.dash0-dev.com
NEXT_PUBLIC_DASH0_AUTH_TOKEN <ingesting-only-dash0-token>
Expand All @@ -156,22 +186,29 @@ OTEL_EXPORTER_OTLP_HEADERS Authorization=Bearer <dash0-token>

## 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. `<your-project-ref>` 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://<your-project-ref>.supabase.co/functions/v1/mcp \
--token lk_rw_<your-token>
```

Almost every `lorekit install` rewrites the MCP entry — a new project, a
global install, `--force`, `--hooks`, `--mcp-json`, `--completions <shell>`, 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
that file in a committable form — it authenticates via a `${LOREKIT_TOKEN}`
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://<your-project-ref>.supabase.co/functions/v1/mcp
```

Commit the generated `.mcp.json` and set `LOREKIT_TOKEN` as an environment
Expand All @@ -191,7 +228,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://<your-project-ref>.supabase.co/functions/v1/mcp",
"--header", "Authorization:Bearer lk_rw_<your-token>"]
}
}
Expand All @@ -209,7 +246,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://<your-project-ref>.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
Expand All @@ -223,18 +260,18 @@ other repos' secrets are untouched.

```bash
# 1. Health check
curl https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/health
# → {"status":"ok","db":true}
curl https://<your-project-ref>.supabase.co/functions/v1/health
# → {"status":"ok","version":"1.0.0","db":"ok","ts":"…"}

# 2. Write a test lesson
curl -X POST https://pqokxlhvnosogizsjztg.supabase.co/functions/v1/mcp \
curl -X POST https://<your-project-ref>.supabase.co/functions/v1/mcp \
-H "Authorization: Bearer lk_rw_<your-token>" \
-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://<your-project-ref>.supabase.co/functions/v1/mcp \
-H "Authorization: Bearer lk_ro_<your-token>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"memory.read","arguments":{"scope":"global","key":"install-test"}}}'
Expand All @@ -255,7 +292,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://<your-project-ref>.supabase.co/functions/v1/mcp
LOREKIT_TOKEN: ${{ secrets.LOREKIT_TOKEN }}
```

Expand Down
8 changes: 8 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/),
Expand Down
Loading
Loading