Repository navigation
Add /lorekit-self-host skill and make install.md self-host-correct - #694
Conversation
…s 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 <your-project-ref> throughout with an all-functions deploy step.
…default 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).
|
The dashboard preview for this PR — redeployed on each push that changes the web app.
Preview always points at this PR's latest commit; Deployment is the immutable build of |
✅ No issues foundThe delta fixes the last open finding: the self-host page now says the GitHub App needs its own registration and GITHUB_APP_ENABLED. Every review thread is resolved and nothing new turned up. Checked: 1 of 1 changed file read · 0 of 4 dependent files traced · no possible issues found Progress: open review threads 0 → 1 → 2 → 1 → 0 across the last 5 reviews What this change reaches — 1 changed export · 4 dependent files · 0 checked · 4 not checkedflowchart LR
subgraph pr["Changed in this PR"]
s1["DOCS_SECTIONS<br/>body changed"]:::changed
end
s1c1["packages/web/src/app/docs/page.tsx<br/>? not checked"]:::unknown
s1c2["packages/web/src/components/command/NavigationCommands.tsx<br/>? not checked"]:::unknown
s1c3["packages/web/src/components/docs/DocsCommands.tsx<br/>? not checked"]:::unknown
s1c4["packages/web/src/lib/docs/sections.spec.ts<br/>? not checked"]:::unknown
s1 -.-> s1c1
s1 -.-> s1c2
s1 -.-> s1c3
s1 -.-> s1c4
classDef changed fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
classDef ok fill:#e7f6ec,stroke:#16a34a,color:#14532d
classDef partial fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef unknown fill:#f3f4f6,stroke:#9ca3af,stroke-dasharray:4 3,color:#374151
classDef bad fill:#fde8e8,stroke:#dc2626,color:#7f1d1d
classDef warn fill:#fff4e5,stroke:#d97706,color:#78350f
classDef imports fill:#f8fafc,stroke:#64748b,stroke-dasharray:4 3,color:#1e293b
Review details
Found Quality — produced 0 → posted inline 0 · cleared 0 · carried forward 0 · deferred 0 · below-bar 0 Run incremental · 6 lines in delta · tier standard · depth checkout Nothing to report — optimality (skipped), measurability (skipped), severity, 0 files skipped.
|
Addresses @mthines's comment: #694 (comment) Refs: #694
…nline Addresses @mthines's comment: #694 (comment) Refs: #694
… one-liner Addresses @mthines's comment: #694 (comment) Refs: #694
Addresses @mthines's comment: #694 (comment) Refs: #694
…placeholder endpoint rule Addresses @mthines's comment: pr-reviewer report #694 (comment) Refs: #694
Addresses @mthines's comment: pr-reviewer report #694 (comment) Refs: #694
… env var Addresses @mthines's comment: pr-reviewer report #694 (comment) Refs: #694
…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.
…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.
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.
…k 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.
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.
Local verify —
|
| PR | #694 · no ticket |
| Commit verified | 1ae4259c (branch feat/lorekit-self-host-skill) |
| Stack | Leg 1: supabase (fresh) + functions + web :4200 · Leg 2: + storage stack :55321, functions from a #690+#694 merge · daemon bound to this checkout |
| Telemetry | default via dash0-dev · 2 loop(s) |
| Run | 2026-10-17T09:49:05Z → 2026-10-17T10:11:12Z |
Acceptance criteria
| ID | Criterion | Result | Evidence |
|---|---|---|---|
| AC-1 | Step 3: a fresh project gets every migration, none pending | pass | migration list --local |
| AC-2 | Step 6: health 200 + db ok; unauthenticated /memories → 401 |
pass | HTTP 200 / 401 |
| AC-3 | Step 7: dashboard on :4200 against the own project → sign up, mint lk_rw_ under Connect your agent |
pass | S-4 · S-5 · S-6 · S-1 |
| AC-4 | Step 9: install --endpoint <own>/functions/v1/mcp writes it; doctor passes connectivity + auth |
pass | doctor output |
| AC-5 | Skill Step 9: memory.write / memory.read round-trip over MCP |
pass | JSON-RPC 200 |
| AC-6 | Remote CLI list / search over REST see the lesson |
pass | CLI output |
| AC-7 | SessionStart hook injects the lesson from the own endpoint | pass | hook JSON |
| AC-8 | Full self-host: the dashboard shows the MCP-written lesson | pass | S-7 |
| AC-9 | /docs/self-host renders, nav after Remote storage, comparison table |
pass | S-8 · S-2 |
| AC-10 | Hosted default: install with no --endpoint writes the hosted URL |
pass | .mcp.json |
| AC-11 | Leftover global ~/.claude.json beats mcp.endpoint; global re-install with --endpoint fixes it |
pass | CLI output |
| AC-12 ᴮ | BYOD 6.4: lorekit bootstrap with inline storage vars reports the schema state |
pass | CLI output |
| AC-13 ᴮ | BYOD: MCP write lands in the storage project, not the function project | pass | psql row counts |
| AC-14 ᴮ | BYOD: inline-env doctor prints a passing byod storage line |
pass | doctor output |
| AC-15 ᴮ | BYOD caveat: REST / remote CLI / dashboard do not see the BYOD row | pass | S-3 |
ᴮ = Leg 2, self-host + BYOD, on a local merge of #690 + #694 (the BYOD bootstrap changes exist only in #690).
Edge cases
| ID | Probe | Result | Evidence |
|---|---|---|---|
| EC-1 | --endpoint missing /mcp |
fixed → pass (F-4) | missing /mcp passes doctor, MCP 404; skill gate now checks the suffix |
| EC-2 | token not minted on this project | pass | foreign token → -32001 'Invalid or unknown API token…' |
| EC-3 | unknown function path | pass | unknown function → 404, matching the doc's diagnostic |
| EC-4 | lk_ro_ token writes |
pass | lk_ro_ write → -32003 with an actionable message; read still works |
| EC-5 | doctor with no LOREKIT_STORAGE_* |
pass | doctor without LOREKIT_STORAGE_* prints no byod storage line |
| EC-6 | re-run install without --endpoint |
fixed → pass (F-3) | plain re-run keeps the self-host entry; --force / new project → hosted |
Not applicable (3)
- Viewport / a11y beyond the new page: the PR changes no UI component; the MDX page was checked at 390 px (F-2).
- Concurrency: docs-only PR, no new request paths.
- A real cloud
supabase link/functions deploy: no throwaway cloud project; a fresh local stack stood in, with each cloud step mapped to its local equivalent (db push→ freshsupabase start/db push --local;secrets set+functions deploy→functions serve --env-file). CI's deploy.yml runs the samefunctions deploycommand.
Findings
| ID | Severity | Kind | What happened | Status |
|---|---|---|---|---|
| F-1 | medium | functional | health returns 200 {"status":"ok","version":"1.0.0","db":"ok","ts":…}; docs/skill/page promised {"status":"ok","db":true} and {"db":false} on failure | fixed 39727ba4 |
| F-4 | medium | functional | --endpoint …/functions/v1 (no /mcp) passes every doctor check (REST base is the same) while MCP initialize at that URL returns 404 | fixed fe861611 |
| F-6 | medium | telemetry-error | pre-existing: web logged ERROR lorekit.api_token.generate.failed: "revalidatePath /lore during render is unsupported" (tokens.ts via buildOnboardingSteps); a "My first token" row is created but the auto-generated t | open (follow-up) |
| F-2 | low | ux | at 390px the inline hosted URL in the opening callout cannot wrap; page scrollWidth 462 > 390 | fixed c417dd3d |
| F-3 | low | functional | a plain lorekit install re-run on an installed project returns early and keeps the self-hosted .mcp.json entry; only --force, a new project, a global install or a missing entry fall back to the host |
fixed f2c36428 |
| F-5 | low | functional | Step 9 says write, read back, then delete — but shows no delete call | fixed 1ae4259c |
| F-7 | low | functional | pre-existing: resolveMcpUrls() only handles https://.supabase.co; any other NEXT_PUBLIC_SUPABASE_URL (local stack, Supabase custom domain) yields a mangled snippet URL (https://http://127.0.0.1:54321.supabase. | open (follow-up) |
| F-8 | low | functional | the {"db":false} troubleshooting row has the same wrong shape as F-1 | escalated |
Fixes
| Commit | Finding | Change | Re-verified by |
|---|---|---|---|
39727ba4 |
F-1 | docs quote the real response shape | /docs/self-host renders "db":"ok"; git grep finds no "db":true outside the install.md Troubleshooting row (left for afte |
fe861611 |
F-4 | the skill's Step 8 gate catches a missing /mcp suffix | Step 8 verify now requires the /functions/v1/mcp suffix and names Step 9 as the real MCP check |
c417dd3d |
F-2 | no page-level horizontal scroll at phone width | unclipped-overflow probe empty at 390px; sections + mdx-render specs 9/9 |
f2c36428 |
F-3 | the warning describes when the hosted fallback actually happens | wording matches all four probed cases |
1ae4259c |
F-5 | the runbook shows every call it asks for | memory.delete archives; follow-up read returns null |
Screenshots — 8
Screenshots uploaded. Keep the lv:shot markers so a re-run keeps them.
S-1 · F-6 (medium) — web logged ERROR lorekit.api_token.generate.failed: "revalidatePath /lore during render is unsupported" (tokens.ts via buildOnboardingSteps); a "My first token" row is created but the auto-generated token is never shown
S-2 · F-2 (low) — at 390px the inline hosted URL in the opening callout cannot wrap; page scrollWidth 462 > 390
S-3 · AC-15 (pass)
S-4 · AC-3 (pass)
S-5 · AC-3 (pass)
S-6 · AC-3 (pass)
S-7 · AC-8 (pass)
S-8 · AC-9 (pass)
Under the hood
- Leg 1, full self-host (Add /lorekit-self-host skill and make install.md self-host-correct #694 head): fresh stack, 108 migrations →
functions serve(all 8,VERCEL_ENV=production) → dashboardnx serve web:4200 → sign up → mintlk_rw_→lorekit install --endpoint→ MCP write/read → REST (list/search) → SessionStart hook → Explorer. All 15 CLI spans in Dash0 (service.name=cli,deployment.environment.name=local) areOK; 0 ERROR spans. - Leg 2, self-host + BYOD (local merge of fix(byod): generate the bootstrap from migrations and fix BYOD's limits #690 + Add /lorekit-self-host skill and make install.md self-host-correct #694, clean, no conflicts): second empty stack →
lorekit bootstrap(inline env) →db push+storage-overlay.sql→ functions re-served with the Step 6.3 storage secrets → same dashboard token writes over MCP → row in storage project only. - Local edge functions ran without OTel export (no
OTEL_*secrets, which the docs mark optional), so their side was read from the function logs: 0 error lines in either leg. The dashboard dev server logged one ERROR (F-6, pre-existing).
Open and escalated
- F-8 (escalated): install.md Troubleshooting still says
{"db":false}. This row sits inside fix(byod): generate the bootstrap from migrations and fix BYOD's limits #690's install.md hunk, and the PR deliberately leaves that table untouched. Options: fix it in fix(byod): generate the bootstrap from migrations and fix BYOD's limits #690, or right after fix(byod): generate the bootstrap from migrations and fix BYOD's limits #690 merges. Recommendation: fix it in fix(byod): generate the bootstrap from migrations and fix BYOD's limits #690, which already rewrites that table. - F-6 (pre-existing, not in this PR): the first sign-in auto-token calls
revalidatePathduring render. It logslorekit.api_token.generate.failedand leaves a "My first token" row that the user never sees. Suggested follow-up: web: first-sign-in auto token calls revalidatePath during render. - F-7 (pre-existing, not in this PR):
resolveMcpUrls()only handleshttps://<ref>.supabase.co, so a Supabase custom domain gets a mangled MCP snippet in "Connect your agent". Suggested follow-up: web: derive the MCP snippet URL from NEXT_PUBLIC_SUPABASE_URL for custom domains. - CLI follow-up (behind F-4):
lorekit doctorcould warn when the endpoint path does not end in/mcp. The skill now tells the reader to check the suffix, but the tool still passes it silently.
Lessons
local-verify-lessons::byod-two-stack-topology-on-colima(updated: Colima/private/tmpentrypoint failure,db push --localinstead of--db-url)local-verify-lessons::dashboard-accordion-click-before-hydration(new)
Generated by /local-verify · full artifacts in .agent/feat/lorekit-self-host-skill/local-verify/
…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.
…ite .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.
Today's install.md points a self-hoster at the hosted project
(
pqokxlhvnosogizsjztg) in nine places, a token minted onlorekit.iocan't authenticate their own deployment, and the documented deploy step
only ships
mcp+health— leavingmemories(hooks, remote CLIcommands, the dashboard's REST API) undeployed.
This adds a guided
/lorekit-self-hostskill (a gated runbook — do thestep, verify it, stop and diagnose on failure), a public
/docs/self-hostpage comparing Hosted / Full self-host / Self-host + BYOD, and fixes
install.md throughout to use
<your-project-ref>with an all-functionsdeploy step and a token-minting flow that actually targets your own
project.
The skill links to install.md/byod.md rather than duplicating their
commands, and is deliberately not added to the CLI's
SKILLSlist(D2 in the plan) — adding it there would turn every existing install's
lorekit doctorred until a re-install, for a runbook most users onlyneed once. It still ships via the Claude plugin and the npm tarball.
Added test coverage in
install.test.mjs(extending the file thatalready owns
--endpointcoverage, not a new harness): a self-hosted--endpointis written verbatim andmcpToRestBase()derives thematching REST base, omitting
--endpointstill falls back to the hostedURL, and the new skill is never scaffolded by a fresh install.
The docs now state the real precedence rules, checked against
config.mjs:installreads only--endpoint, thenLOREKIT_MCP_URL, thenLOREKIT_ENDPOINT. Almost every install rewrites the MCP entry: a new project, global,--force,--hooks,--mcp-json,--completions <shell>, or a re-run after an upgrade adds a skill. Without--endpointit writes the hosted URL. Only a plain re-run on a fully installed project leaves.mcp.jsonalone.GITHUB_APP_ENABLED=true."db":"ok"(503"db":"error"on failure). The skill's Step 8 gate also checks the/mcpsuffix, becausedoctorprobes over REST and can't see it..mcp.json, then~/.claude.json, thenmcp.endpointin.lorekit.json. A leftover hosted global install therefore wins over a committedmcp.endpoint, and the docs tell the reader to re-run it with--endpoint.pnpm nx serve web) runs on port 4200, and the auth callback URLs use it.CLAUDE.mdanddocs/decisions.mdnow allow<your-project-ref>in self-host instructions (install.md, byod.md, the skill, the self-host page). The concrete hosted URL is still mandatory wherever the hosted endpoint is meant.Verified end to end with
/local-verifyon a fresh local Supabase stack, following the docs literally. Two legs passed:install --endpoint, MCP, REST, hooks and the Explorer;The run's doc fixes are folded in. The
{"db":false}row in install.md's Troubleshooting table is left for #690, because that row sits inside #690's hunk of the file. Report: thelocal-verifycomment on this PR.Follow-up, out of scope here:
supabase/project.json'sfn:deploytarget still only deploys
mcp+health— this PR documents theall-functions
supabase functions deploycommand as the self-host path,but doesn't change the NX target itself.
Separate from #690 (BYOD schema rewrite). Both PRs edit
docs/install.md, so expect a merge conflict in whichever lands second. This PR doesn't touch docs/byod.md.🧪 UI verification spec — run against the preview deployment
Target: preview
Format: intent
Spec 1: A visitor reads the new self-hosting comparison page
Changed: the new
/docs/self-hostpage and its "Self-hosting" entry in the docs navStart: /docs/remote
Steps:
Expected:
/docs/self-hostwith the title "Self-hosting".docs/install.mdanddocs/byod.mdon GitHub, and mentions/lorekit-self-host.Hints:
DOCS_SECTIONSentry as a/docs/<id>link (seeSectionNav).Out of scope:
/lorekit-self-hostskill, not browser-testable).