This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Colibri is a self-hosted ebook library application with a web interface, built on SvelteKit. It supports ebook management, collections, metadata retrieval from public knowledge graphs, and passwordless authentication via Passkeys.
Key Technologies:
- Framework: SvelteKit with Svelte 5
- Database: PostgreSQL via Kysely ORM (managed with Supabase)
- API: tRPC for type-safe client-server communication
- Storage: S3-compatible object storage
- Authentication: WebAuthn Passkeys, OAuth 2.0/2.1, API Keys
- Styling: Tailwind CSS 4
- Monorepo: Turborepo + pnpm workspaces
- License: AGPL-3.0-or-later
# Install dependencies (requires pnpm 10.27+)
corepack enable pnpm
pnpm install
# Start local Supabase/Postgres database
pnpx supabase start
# Setup environment
cp .env.example .env
pnpx supabase status --output env >> .env
# Development
pnpm dev # Run all packages in dev mode
pnpm dev:app # Run only the web app (http://localhost:5173)
pnpm dev:cli # Run only the CLI
pnpm dev:docs # Run only the docs site (http://localhost:5174)
# Build
pnpm build # Build all packages
pnpm build:app # Build only the web app
pnpm build:cli # Build only the CLI
pnpm build:docs # Build only the docs site
# Testing
pnpm test # Run all tests (Vitest + Playwright)
pnpm test:coverage # Run tests with coverage reports
pnpm test:e2e # Run E2E tests only
# Single package tests:
cd packages/sdk && pnpm test # Vitest
cd apps/app && pnpm test # Playwright
cd apps/cli && pnpm test # Vitest
# Linting and Formatting
pnpm lint # Lint all packages
pnpm fmt # Format all packages
pnpm check # TypeScript type checking
# Generate database types from schema
cd packages/sdk && pnpm types
# Storybook (UI components)
pnpm storybookApps:
apps/app- Main SvelteKit web application with tRPC APIapps/cli- oclif-based CLI tool (colibricommand)apps/docs- Documentation site (Cloudflare Pages)
Packages:
packages/sdk- Core SDK: database (Kysely/Postgres), S3 storage, ebook parsing, metadata providers, authenticationpackages/ui- Svelte 5 component library with Storybook (37 components), uses bits-uipackages/shared- Shared utilities, blurhash, XML parsing; exports shared eslint/prettier/vitest configspackages/mobi- MOBI ebook format parser (binary-parser based)packages/pdf- pdf.js wrapper with conditional exports for node/browser/workerpackages/oauth- OAuth 2.0/2.1 authorization server and client implementationpackages/open-library-client- Open Library API client for metadatapackages/languages- ISO 639-3 language code resolutionpackages/setup- Interactive setup wizard for new instances
apps/app, apps/cli
└── packages/sdk
├── packages/mobi
├── packages/pdf
├── packages/oauth
├── packages/open-library-client
├── packages/languages
└── packages/shared
└── packages/ui
└── packages/sdk (types only)
The SDK is the core library with multiple entry points:
| Export Path | Purpose |
|---|---|
. |
Resources, database, scopes, settings |
./server |
Server-only: asset, image (Node.js APIs) |
./client |
Client-safe: icon-urn, scopes (no Node deps) |
./schema |
Database schema types (generated) |
./oauth |
OAuth server/client functionality |
./storage |
S3 storage abstraction layer |
./ebooks |
EPUB/MOBI/PDF parsing & metadata |
./metadata |
Metadata providers system |
./ingestion |
Full ebook ingestion pipeline |
./settings |
Settings registry & management |
./scopes |
Permission scopes (API keys, OAuth) |
./types |
Type-only exports (safe for client) |
Key SDK Modules:
src/resources/- High-level CRUD operations for all domain entitiessrc/ebooks/- Format parsers (EPUB v2/v3, MOBI, PDF)src/ingestion/- Complete import pipeline with duplicate detectionsrc/metadata/- Multi-provider enrichment (OpenLibrary, WikiData, VIAF, ISBNdb, etc.)src/storage/- S3 client abstraction with presigned URLssrc/scopes/- OAuth/API key permission system
Route Organization:
(library)/- Protected authenticated library areaauth/- Authentication flows (Passkeys, OAuth, passcode)api/- RESTful API endpoints.well-known/- Discovery endpoints (OpenID, OAuth, Colibri server)
Key Routes:
/works- Library browser with search and filtering/collections- User-created collections/creators,/publishers,/series- Metadata browsing/discover/[catalog]- OPDS catalog browser/instance/settings- Admin panel/auth/login- Authentication entry point
tRPC Router Organization (src/lib/trpc/):
14 domain-based route modules: accounts, apiKeys, books, catalogs, collections, comments, creators, languages, notifications, publishers, search, series, settings, users
Component Library (src/lib/components/):
Auth/- Authentication UI (digits input, OAuth prompts)Sidebar/- Navigation, collections list, user profileComments/- Threaded comments with reactionsUpload/- File upload, import queue, enrichmentPagination/- Paginated listsLinks/- Typed link components (WorkLink, AuthorLink, etc.)Form/- Form inputs with autocomplete
Topic-based commands:
| Topic | Commands |
|---|---|
works |
list, add, inspect, import |
creators |
list, add, inspect, edit |
publishers |
list, inspect, edit |
storage |
connect, list-buckets, list, make-bucket, copy, move |
settings |
version, get, set |
users |
list, add, update, remove |
oauth |
clients (list, add, update, remove) |
Root commands: connect, login, discover
| Table | Purpose |
|---|---|
work |
Container for a conceptual work |
edition |
Specific edition with ISBN, ASIN, cover |
asset |
Physical ebook files in S3 |
contribution |
Links creators to editions with MARC relator roles |
creator |
Authors, illustrators, translators |
publisher |
Publishing companies |
collection |
User-created collections with privacy settings |
series |
Book series groupings |
tag |
Subject/topic tags |
image |
Cover images with blurhash |
comment |
Threaded comments with moderation |
| Table | Purpose |
|---|---|
user |
App users with roles (admin/adult) |
authenticator |
WebAuthn passkey storage |
api_key |
API keys with scopes (SHA-256 hash) |
client |
OAuth clients with redirect URIs |
access_token |
OAuth access tokens |
refresh_token |
OAuth refresh tokens |
authorization_code |
PKCE-compliant auth codes |
device_challenge |
OAuth Device Grant flow |
All tables have RLS enabled. The app sets app.current_user_id setting before queries:
USING (user_id::text = current_setting('app.current_user_id', true))cd packages/sdk && pnpm types
# Runs: kysely-codegen --dialect postgres --env-file ../../.env --url 'env(DB_URL)'
# Generates: src/schema.d.ts- WebAuthn/Passkeys (Primary) - via
@simplewebauthn/server - Email Passcode (Fallback) - One-time codes sent to email
- OAuth 2.0/2.1 - Full authorization server for third-party apps
- API Keys - Programmatic access with scopes
- Cookie-based sessions with configurable cookie name
- JWT payload:
sub(userId),name,email - Verified in server hooks via
resolveUserId()
- Prefix:
col_+ 8 random characters - Storage: SHA-256 hash (never plain text)
- Scopes: Fine-grained permissions
- Features: Expiration, rotation with grace period, usage tracking
Hierarchical scope system for OAuth and API keys:
books:read,books:write,books:deletecollections:read,collections:writemetadata:read,metadata:writeusers:read,users:writesettings:read,settings:write
- Vitest - Unit and integration tests
- Playwright - E2E and browser tests
- SDK: 60% (statements, branches, functions, lines)
- Other packages: 80%
# apps/app/tests/
database.setup.ts # Seeds test data
authentication.setup.ts # Creates JWT cookie
database.teardown.ts # Cleanup test data
test-data.ts # Test constants (IDs)
base.ts # Fixtures with database accesspnpm test # All tests
pnpm test:coverage # With coverage
cd apps/app && pnpm test # E2E only- TypeScript with strict mode, ESNext target, NodeNext module resolution
- Svelte 5 for components (use
$state,$derived,$effect) - Tailwind CSS 4 for styling (use
@applysparingly) - ESLint + Prettier with shared configs from
@colibri-hq/shared
<script lang="ts">
// Props with $props()
let { value = $bindable(), onChange }: Props = $props();
// State with $state()
let count = $state(0);
// Derived values with $derived()
let doubled = $derived(count * 2);
// Effects with $effect()
$effect(() => {
console.log('count changed:', count);
});
</script>The UI package uses bits-ui for headless primitives:
<!-- Use snippets for composability -->
{#snippet item(data)}
<span>{data.label}</span>
{/snippet}
<Autocomplete items={options} {item} />Pre-commit:
- Formats staged files with Prettier
- Uses
stage_fixed: trueto re-stage formatted files - Skips during merge/rebase operations
Pre-push:
pnpm check- TypeScript type checkingpnpm lint- ESLintpnpm test- Test suite
Troubleshooting:
- Run
lefthook installafter modifyinglefthook.yaml - If hooks fail with "Cannot find package", ensure
@colibri-hq/sharedis in rootdevDependencies
| Variable | Purpose |
|---|---|
DB_URL |
PostgreSQL connection string |
JWT_SECRET |
JWT signing key |
APP_SECRET_KEY |
URL signing secret |
| Variable | Purpose |
|---|---|
DATABASE_CERTIFICATE |
SSL certificate for DB (base64) |
JWT_COOKIE_NAME |
Auth cookie name (default: jwt) |
PUBLIC_PASSCODE_LENGTH |
Passcode length |
COLIBRI_ENCRYPTION_KEY |
Encryption key for secrets |
# docker-compose.yaml services
colibri # Web app container
postgres # PostgreSQL 18 database
minio # S3-compatible object storageBuckets:
creator-images- Public, images onlycovers- Public, images onlyassets- Private storagecolibri- Public storage
CRITICAL: You MUST use specialized agents whenever working in their domain. These agents have deep, comprehensive knowledge of their areas including file locations, patterns, conventions, and gotchas. Using agents dramatically improves code quality and reduces errors. DO NOT attempt domain-specific work without consulting the appropriate agent first.
This project has specialized agents in .claude/agents/ with extensive memory of the codebase:
| Agent | Use For | Priority |
|---|---|---|
| sdk-expert | Database operations, ebook parsing, storage, Kysely ORM | HIGH |
| cli-expert | CLI commands, oclif patterns, terminal output | HIGH |
| web-app-expert | SvelteKit routes, tRPC API, authentication flows | HIGH |
| ui-components-expert | Svelte components, Storybook, bits-ui, accessibility | HIGH |
| database-expert | PostgreSQL schema, migrations, Kysely queries, RLS | HIGH |
| ebook-processing-expert | EPUB/MOBI/PDF parsing, metadata extraction, covers | HIGH |
| metadata-expert | OpenLibrary, WikiData, ISNI, VIAF, reconciliation | HIGH |
| infrastructure-expert | OAuth, shared utilities, cryptography, ESLint/Prettier | MEDIUM |
| tech-lead | Architecture decisions, feature planning, cross-cutting work | HIGH |
| technical-writer | Documentation, README files, user guides, content | MEDIUM |
- ALWAYS delegate to the appropriate agent when working in their domain
- Agents have comprehensive memory of codebase structure, patterns, conventions, and testing strategies
- For cross-cutting concerns, use the tech-lead agent to coordinate between specialists
- When exploring unfamiliar code, start with the relevant domain agent
- Multiple agents can work in parallel for complex tasks spanning multiple domains
User: "Add a new tRPC route for managing user preferences"
→ Use web-app-expert (tRPC routes, authentication)
User: "Parse ASIN from a MOBI file"
→ Use ebook-processing-expert (MOBI parsing)
User: "Add a new database table for notifications"
→ Use database-expert (schema design, migrations)
User: "Create a new Button variant"
→ Use ui-components-expert (Svelte components, Tailwind)
User: "Implement a new metadata provider"
→ Use metadata-expert (provider patterns, caching)
| File | Purpose |
|---|---|
turbo.json |
Turborepo build pipeline |
pnpm-workspace.yaml |
Workspace configuration |
lefthook.yaml |
Git hooks |
supabase/config.toml |
Supabase/database configuration |
.env.example |
Environment variables template |
| File | Purpose |
|---|---|
supabase/schemas/*.sql |
Schema definitions (00-22) |
supabase/migrations/*.sql |
Incremental migrations |
packages/sdk/src/schema.d.ts |
Generated TypeScript types |
packages/sdk/src/database.ts |
Kysely initialization |
| File | Purpose |
|---|---|
apps/app/src/lib/trpc/router.ts |
tRPC router setup |
apps/app/src/lib/trpc/routes/*.ts |
Domain-specific routes |
apps/app/src/lib/trpc/middleware.ts |
Auth middleware |
apps/app/src/hooks.server.ts |
Server hooks/middleware |
| File | Purpose |
|---|---|
packages/ui/src/lib/ui/index.ts |
Component exports |
packages/ui/.storybook/main.ts |
Storybook configuration |
apps/app/src/lib/components/ |
App-specific components |
- Create route file in
apps/app/src/lib/trpc/routes/ - Export router from the file
- Import and add to
router.ts - Use guards for authentication:
procedure.use(guards.authenticated)
- Add schema in
supabase/schemas/XX_name.sql - Create migration in
supabase/migrations/ - Run
cd packages/sdk && pnpm typesto regenerate types - Add resource functions in
packages/sdk/src/resources/
- Create component in
packages/ui/src/lib/ui/ComponentName/ - Export from
packages/ui/src/lib/ui/index.ts - Add Storybook story as
ComponentName.stories.svelte - Use bits-ui primitives where applicable
- Create command in
apps/cli/src/commands/topic/name.ts - Extend
BaseCommandfor shared functionality - Use flags from
apps/cli/src/flags/for consistency - Update oclif manifest:
pnpm -F cli run build
Feature plans are tracked in plans/*.md with corresponding GitHub issues. After implementing a feature:
- Update the plan file in
plans/to reflect current status:- Move items from "Remaining Work" to "Implemented"
- Update "Current Implementation Status" section
- Note any decisions made or scope changes
- Update the GitHub issue (linked at top of each plan file):
- Add comments with progress updates
- Check off completed items if using task lists
- Close the issue when the feature is complete
- Reference the issue in commit messages:
Implements #123orCloses #123
This ensures plans stay synchronized with actual implementation progress.
The SDK supports multiple metadata providers for book enrichment:
| Provider | Data Types |
|---|---|
| OpenLibrary | Books, authors, covers |
| WikiData | Authors, publishers |
| VIAF | Authority records |
| ISNI | Author identifiers |
| ISBNdb | Books, ISBNs |
| Google Books | Books, covers |
| Internet Archive | Books, full text |
| Crossref | Academic works |
| Springer | Academic books |
Features:
- Rate limiting per provider
- Caching with TTL
- Confidence scoring for merging
- Fuzzy matching for deduplication
| Format | Parser | Features |
|---|---|---|
| EPUB | @zip.js/zip.js + XML | Metadata, cover, TOC, v2/v3 |
| MOBI | packages/mobi | Metadata, cover (binary parsing) |
| pdf.js | Metadata, page count | |
| AZW3 | packages/mobi | Kindle format variant |
- Extract Metadata - Parse file & detect format
- Enrich (Optional) - Query external providers
- Duplicate Detection - Check by checksum, ISBN, fuzzy title
- Process Contributors - Find/create creators with fuzzy matching
- Cover Processing - Extract cover, generate blurhash
- Create Records - Transactional database inserts
| Endpoint | Purpose |
|---|---|
/api/import-events |
Upload progress, import status |
/api/comment-events |
New comments, reactions |
// apps/app/src/lib/components/Upload/ImportSubscription.svelte
const eventSource = new EventSource('/api/import-events');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
// Handle import progress
};