Self-hosted web app for automating database and file backups with encryption, compression, and retention policies. Built with Next.js 16 (App Router), TypeScript, Prisma (SQLite), and Shadcn UI.
Supported sources include MySQL, PostgreSQL, MongoDB, MariaDB, SQLite, MSSQL, Redis, and Valkey. The adapter lists grow frequently. Never treat a hardcoded adapter list in any doc as exhaustive - run ls src/lib/adapters/{database,storage,notification}/ for the authoritative list before making claims about supported adapters.
Claude Code loads the nearest CLAUDE.md when you touch files in a directory. Read the matching guide before working in that area:
| Working on | Guide |
|---|---|
| Services, Server Actions, API routes, runner pipeline | src/CLAUDE.md |
| UI components and the design system | src/components/CLAUDE.md |
| Database, storage, or notification adapters | src/lib/adapters/CLAUDE.md |
| Wiki pages and the changelog | docs/CLAUDE.md |
| Unit and integration tests | tests/CLAUDE.md |
- Package manager is
pnpm. Nevernpm installoryarn. Prisma CLI calls go throughnpx prisma .... - Never use
console.log/console.error/console.warn. Use the logger from@/lib/logging/logger. This holds in Client Components too. - Every change updates
docs/changelog.mdin the same response, except AI tooling changes. See Changelog workflow. - Typography: no em dashes, no semicolons. Use a hyphen where a dash is needed, and end sentences with a period. Applies to code comments, docs, and commit messages.
- Language: all code, comments, and documentation in English.
- Never run
prisma migrate devwhilepnpm devis running, and never useprisma db push. See Prisma migrations. - Every Server Action and API route starts with a permission check. See src/CLAUDE.md.
src/app/ Route definitions and Server Actions - NO business logic
src/services/ All business logic, organized by domain <- core
src/lib/adapters/ Plugin system for databases, storage, notifications
src/lib/runner/ Step-based backup execution pipeline
Data flows one direction: page.tsx (Server Component) fetches through a Service, then passes props to a Client Component. Server Actions are thin wrappers: auth check, Zod validation, service call, revalidate.
A Server Action or API route that contains business logic is a bug. Move it into src/services/.
pnpm dev # Dev server - auto-applies pending migrations on startup
pnpm run build # Production build - run before committing
pnpm validate # Lint + typecheck + build (scripts/validate.sh)
pnpm type # tsc --noEmit only
pnpm test # Unit tests (vitest)
pnpm test:integration # Integration tests against real DB containers
pnpm test:ui # Spin up test DBs + seed local DB for manual testing
pnpm run database:reset # Reset dev DB from scratch via all migrations
pnpm changelog:next # Create a `## vNEXT` changelog placeholderDocs site and marketing site are separate workspaces: pnpm docs:dev (VitePress) and pnpm website:dev.
pnpm dev runs prisma migrate deploy on startup, so the local DB is always current after pulling. No manual step needed.
Schema changes:
- Stop the dev server (Ctrl+C in the node terminal).
npx prisma migrate dev --name <migration-name>- Restart
pnpm dev.
Running migrate dev against a live dev server can trigger an interactive DB reset, which fights the SQLite file lock and crashes the Node process - often taking VS Code with it. prisma db push applies schema changes without a migration file, which desyncs _prisma_migrations from the real schema and breaks database:deploy in production and for other developers.
Every change - feature, bug fix, refactor, docs, CI - gets an entry in docs/changelog.md in the same response. Do not defer it.
Exception: AI tooling changes never get a changelog entry. The changelog is published on the docs site for people who run DBackup. Anything that only configures the assistant is invisible to them:
CLAUDE.mdfiles anywhere in the tree.claude/in full - agents, skills, commands, settings, launch config.gitignorerules that only exist to track those files
The test is who the line is for. A reader upgrading their instance never needs to know a prompt file changed. Code that ships in the product still counts even when it exists to keep the assistant honest - a lint guard under tests/ changes the build for every contributor, so it belongs in ### 🧪 Tests.
Find the active version: either a ## vNEXT block at the top, or the topmost ## vX.Y.Z block marked *Release: In Progress*. If neither exists, run pnpm changelog:next first.
Section order (skip sections with no entries, never reorder):
| # | Change type | Section |
|---|---|---|
| 1 | New feature, adapter, or page | ### ✨ Features |
| 2 | Bug fix | ### 🐛 Bug Fixes |
| 3 | Security fix | ### 🔒 Security |
| 4 | Performance, UX, code quality | ### 🎨 Improvements |
| 5 | Behavior change (non-breaking) | ### 🔄 Changed |
| 6 | Deleted feature or code | ### 🗑️ Removed |
| 7 | New or updated docs article | ### 📝 Documentation |
| 8 | Test changes | ### 🧪 Tests |
| 9 | GitHub Actions, Dockerfile, scripts | ### 🔧 CI/CD |
| 10 | Docker image info (always last) | ### 🐳 Docker |
Entry format and full rules live in docs/CLAUDE.md.
- Naming:
kebab-case(backup-service.ts,user-table.tsx). - Exports: named exports preferred, no barrel files in
src/components/. - Path alias:
@/maps tosrc/. - Max file size: roughly 300 lines, then split. The runner pipeline in
src/lib/runner/steps/is the reference for how to split.
| Concern | Location |
|---|---|
| DB schema | prisma/schema.prisma |
| Types and interfaces | src/lib/core/ |
| Permissions | src/lib/auth/permissions.ts |
| Access control | src/lib/auth/access-control.ts |
| Adapters | src/lib/adapters/ |
| Services | src/services/ |
| Server Actions | src/app/actions/ |
| Scheduler (cron) | src/lib/server/scheduler.ts |
| Crypto (encrypt/decrypt) | src/lib/crypto/index.ts |
| Crypto (streams) | src/lib/crypto/stream.ts |
| Logger | src/lib/logging/logger.ts |
| Error classes | src/lib/logging/errors.ts |
| Shadcn primitives | src/components/ui/ |
| Test containers | docker-compose.test.yml |
ENCRYPTION_KEY- system key for encrypting secrets at rest. Required.LOG_LEVEL-debug|info(default) |warn|error.SQLITE_WAL_MODE-true(default) |false. WAL is on by default for the Prisma SQLite DB. Seesrc/lib/prisma.ts.DISABLE_EMAIL_LOGIN-true|false(default). Rejects browser email sign-in and sign-up. Server-sideauth.api.*calls stay open so admin user creation and password resets keep working. Seesrc/lib/auth/env-flags.ts.OIDC_AUTO_REDIRECT-SsoProvider.providerIdto redirect to instead of rendering the login page. Format checked inenv-validation.ts, existence instartup-checks.ts.