Krafa
Dashboard
Dashboard
Getting Started
QuickstartHow it works
The Canvas
Canvas overviewPasting and node typesOrganizing a boardCommand menuKeyboard shortcuts
Design Taste
Taste memoriesExtractionConflictsCurating your profile
Using with Agents
Connecting an agentMCP server referenceClaude CodeCursorCodexKiroAntigravity
Imports
Imports overviewXPinterestDribbble
Reference
Plans and billingHTTP APISelf-hostingFAQ
KrafaAll systems operational
Reference

Self-hosting

Run Krafa on your own infrastructure. Services, environment variables, and what degrades gracefully when a credential is missing.

Krafa is a pnpm workspace with two applications and three backing services. Everything except the database and object storage is optional, and the app degrades feature by feature rather than failing to start.

What runs

PieceStackDev port
frontend/Vite, React 19, Tailwind v4, TanStack Router5173 (5174 fallback)
backend/Hono on @hono/node-server, pg3000
PostgreSQLDocker5433
MinIODocker, S3-compatible9002 / 9003

The non-standard ports are deliberate, so a Krafa checkout doesn't collide with whatever else you have running.

Running it

Bring up the services

pnpm db:up

Starts PostgreSQL and MinIO from docker-compose.yml.

Configure the backend

cp backend/.env.example backend/.env

At minimum, set DATABASE_URL, BETTER_AUTH_SECRET, S3_ACCESS_KEY, and S3_SECRET_KEY. See the table below.

Install and run

pnpm install
pnpm dev

Runs the frontend and backend together. Schema migrations are idempotent and apply on backend startup.

Environment variables

Set these in backend/.env. They're validated at startup with t3-env, so a missing required variable fails immediately with a readable message rather than at first use. Empty strings count as unset.

Required

VariablePurpose
DATABASE_URLPostgreSQL connection string
BETTER_AUTH_SECRETSigns sessions and encrypts stored OAuth tokens. Minimum 32 characters
S3_ACCESS_KEYObject storage credentials
S3_SECRET_KEYObject storage credentials

Core

VariableDefaultPurpose
PORT3000Backend HTTP port
BETTER_AUTH_URLhttp://localhost:3000Auth base URL
FRONTEND_URLhttp://localhost:5174Frontend origin, used for CORS and redirects
S3_ENDPOINThttp://localhost:9002S3-compatible endpoint
S3_BUCKETkrafa-mediaUpload bucket

Taste extraction

VariablePurpose
ANTHROPIC_API_KEYPowers extraction. Without it, extraction silently no-ops
Warning

This is the one that turns Krafa from a paste canvas into a taste engine. Without it the canvas works perfectly, boards save, imports run, and the taste panel stays empty forever, with no error to tell you why.

Authentication

VariablePurpose
RESEND_API_KEYSends magic links and invites. Without it, links are logged to the backend console
MAGIC_LINK_FROMSender address. Defaults to Krafa <onboarding@resend.dev>
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETGitHub sign-in
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETGoogle sign-in
DEMO_EMAILEnables a shared demo account at /api/demo-login. Leave unset in production

OAuth callbacks are {BETTER_AUTH_URL}/api/auth/callback/github and .../google.

Tip

Console-logged magic links are the fastest path to a working local instance, since you can sign in with no email provider at all. Just don't ship that way.

Imports

VariablePurpose
X_CLIENT_ID / X_CLIENT_SECRETX bookmark import. The secret is optional for public clients
PINTEREST_CLIENT_ID / PINTEREST_CLIENT_SECRETPinterest OAuth

Unset credentials produce a not configured state in the import sheet rather than a broken flow. Bookmark access additionally needs an X API app on a tier that grants it.

Serving MCP

The MCP server is part of the backend, so it's live at {your-origin}/api/mcp with no extra process. Board tokens minted on your instance authenticate against your instance only.

Without Stripe credentials the instance runs unmetered: every account is treated as exempt, so generations are recorded for the usage panel but never counted against an allowance or charged. Locking your own users out of a feature nobody can pay for would be the wrong default.

Behind a reverse proxy, make sure it doesn't buffer responses or strip the Authorization header, since Streamable HTTP needs both to pass through cleanly. Point BETTER_AUTH_URL and FRONTEND_URL at your public origins so OAuth callbacks and invite links resolve correctly.

What lives where

DataStorage
Boards and canvas nodesPostgreSQL, boards.nodes as JSONB
Taste memoriesPostgreSQL, memories
MCP tokensPostgreSQL, boards.mcp_token
Uploaded mediaS3-compatible object storage, referenced from files
Import OAuth tokensPostgreSQL, encrypted with BETTER_AUTH_SECRET

Auth tables are managed by the Better Auth CLI; the app's own schema applies automatically on startup.

The only data that leaves your infrastructure is what extraction sends to Anthropic: the reference being analyzed and the board's existing memories. Leave ANTHROPIC_API_KEY unset and nothing leaves at all.

On this page

What runsRunning itBring up the servicesConfigure the backendInstall and runEnvironment variablesRequiredCoreTaste extractionAuthenticationImportsServing MCPWhat lives where