diff --git a/CLAUDE.md b/CLAUDE.md
index b8767ea..089eac8 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -98,6 +98,10 @@ tests/
unit/ # Vitest unit tests
e2e/ # Playwright e2e tests (Gherkin-style)
helpers/fixtures.ts # Shared authenticatedPage fixture
+website/ # Static landing page (crumbs.bretzel.app)
+ index.html # Landing page with feature cards, screenshots, deploy section
+ styles.css # Landing page styles (retro parchment theme)
+ assets/ # Favicon, screenshots
```
## Commands
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index 5a44334..bcad068 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -22,36 +22,31 @@ export const handle: Handle = async ({ event, resolve }) => {
return resolve(event);
}
- // MCP endpoint: authenticate via Bearer token (API key)
- if (pathname.startsWith('/api/mcp')) {
+ // API routes: authenticate via Bearer token if present
+ if (pathname.startsWith('/api/')) {
const authHeader = event.request.headers.get('authorization');
const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
- if (!token) {
- return new Response(JSON.stringify({ error: 'Unauthorized' }), {
- status: 401,
- headers: { 'Content-Type': 'application/json' }
- });
- }
+ if (token) {
+ const { valid, userId } = validateApiKey(token);
+ if (!valid || !userId) {
+ return new Response(JSON.stringify({ error: 'Unauthorized' }), {
+ status: 401,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
- const { valid, userId } = validateApiKey(token);
- if (!valid || !userId) {
- return new Response(JSON.stringify({ error: 'Unauthorized' }), {
- status: 401,
- headers: { 'Content-Type': 'application/json' }
- });
- }
+ const user = getUserForApiKey(userId);
+ if (!user) {
+ return new Response(JSON.stringify({ error: 'Unauthorized' }), {
+ status: 401,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
- const user = getUserForApiKey(userId);
- if (!user) {
- return new Response(JSON.stringify({ error: 'Unauthorized' }), {
- status: 401,
- headers: { 'Content-Type': 'application/json' }
- });
+ event.locals.user = user;
+ return resolve(event);
}
-
- event.locals.user = user;
- return resolve(event);
}
// Check if setup is complete
diff --git a/src/routes/(app)/settings/+layout.svelte b/src/routes/(app)/settings/+layout.svelte
index 8f6e89f..51174eb 100644
--- a/src/routes/(app)/settings/+layout.svelte
+++ b/src/routes/(app)/settings/+layout.svelte
@@ -32,7 +32,7 @@
class="flex items-center gap-2 rounded-sm px-3 py-2 text-sm whitespace-nowrap transition-colors {pathname === '/settings/mcp' ? 'bg-[var(--primary)]/15 text-[var(--primary)] font-medium' : 'text-[var(--text)] hover:bg-[var(--bg-base)]'}"
>
- API keys allow external tools (like Claude Code) to access your notes via the MCP protocol. + API keys allow external tools to access your notes via the MCP protocol and the REST API.
@@ -229,6 +229,16 @@ ${mcpJsonConfig}`); {/if} + +
+ Use your API key to authenticate requests to any /api/* endpoint:
+
curl -H "Authorization: Bearer {activeApiKey}" \
+ {baseUrl}/api/notes
+Built-in AI integration. Connect Claude, Cursor, or any MCP client to read and write your notes.
+Full REST API with token auth. Integrate with n8n, Zapier, or scripts. Built-in MCP server for AI tools like Claude and Cursor.