# K-Photos Self-hosted media orchestrator and gallery. Alternative to Apple Photos, Google Photos, and Immich. ## Philosophy - **Files are sacred** — original file bytes and embedded metadata are never modified. Ever. - **No lock-in** — all metadata exportable to standard formats (XMP, JSON). Stop using K-Photos and your tagged, organized file system remains intact. - **Virtual layer** — all edits (dates, tags, albums, face regions) live in a separate layer. DB at runtime, sidecar exports for portability. Corrupt the layer? Rebuild from originals. - **Modular** — core works without AI/ML. Face detection, classification, smart search are optional plugins. - **BYOS** — bring your own storage. Local NAS, S3, GCS — the domain doesn't care. ## Features ### Photo Management - **Timeline** — date-grouped photo grid sorted by EXIF capture date, infinite scroll, date scrubber for fast navigation - **Image viewer** — fullscreen with zoom/pan/pinch (react-zoom-pan-pinch), keyboard nav, collapsible metadata sidebar (EXIF, camera, location) - **Albums** — create, add/remove photos, asset picker dialog - **Upload** — drag-drop with per-file progress, sequential upload through Next.js proxy - **Multi-select** — select photos to bulk add to albums or delete - **Multi-volume** — import photos from NAS, external drives, or cloud storage without copying ### Safe Deletion - **Read-only volumes** (NAS, archives): delete removes DB records + derivatives. Original files never touched. - **Writable volumes** (uploads): soft-delete to trash with configurable grace period before permanent purge. - **Trash** — view trashed photos, restore before purge. `TRASH_RETENTION_DAYS` (default 30). ### Admin - **Storage** — register volumes + library paths, import library (one-click scan), delete - **Jobs** — queue dashboard with filtering, pagination, error details, start/fail actions - **Plugins** — list, enable/disable toggle, create - **Pipelines** — list configured pipelines, create trigger-based processing chains - **Sidecars** — detect changes, bulk export/import, per-asset conflict resolution - **Duplicates** — view duplicate groups with thumbnails, resolve by picking keeper ### Auth - JWT access tokens + refresh token rotation - Role-based access: first registered user auto-promoted to admin - Admin section in sidebar, hidden for regular users ## Architecture Hexagonal / DDD with CQRS. Dependencies point inward: ``` Infrastructure (Axum, Postgres, NATS, S3) -> Adapters (Controllers, Repos, Storage Providers) -> Application (Commands / Queries) -> Domain (Entities, Value Objects, Ports, Services) ``` ### Bounded Contexts | Context | Purpose | |---|---| | **Identity** | Users, roles, RBAC permissions, groups, refresh tokens | | **Storage** | Volumes, library paths, ingestion, quotas, BYOS | | **Catalog** | Assets, metadata layers, stacks, derivatives, duplicates, visibility filtering | | **Organization** | Albums, tags, collections (smart albums) | | **Sharing** | Share scopes, targets, links, invite codes, visibility filters | | **Sidecar** | XMP/JSON export, sync state, conflict resolution | | **Processing** | Jobs, batches, plugins, pipelines, directory scanner | ### Project Structure ``` crates/ domain/ pure Rust — entities, value objects, ports, services application/ CQRS commands + queries with Arc injection api-types/ HTTP request/response DTOs with OpenAPI derives adapters/ auth/ bcrypt + JWT postgres/ repos, event store, migrations storage/ local filesystem, volume-aware file resolver exif/ EXIF metadata extraction thumbnail/ derivative generation sidecar/ XMP reader/writer event-transport/ composite publisher (NATS + event store) nats/ NATS JetStream transport presentation/ axum handlers, routes, middleware bootstrap/ config, DI wiring, API server entry point worker/ background job runner (NATS consumer, sweep, trash purge) k-photos-frontend/ Next.js 16 + shadcn + TanStack Query app/(auth)/ login, register app/(app)/ timeline, albums, trash, admin pages components/ photo grid, image viewer, upload dialog, sidebars hooks/ auth, timeline, albums, upload, admin hooks lib/ API client, token helpers, types ``` ## Environment Variables | Variable | Required | Default | Description | |---|---|---|---| | `DATABASE_URL` | yes | — | Postgres connection string | | `JWT_SECRET` | yes | — | HMAC secret for JWT signing | | `NATS_URL` | no | `nats://localhost:4222` | NATS server URL | | `STORAGE_PATH` | no | `./data/media` | Local file storage root | | `HOST` | no | `0.0.0.0` | Bind address | | `PORT` | no | `8000` | Bind port | | `CORS_ALLOWED_ORIGINS` | no | — | Comma-separated origins | | `MAX_UPLOAD_BYTES` | no | `268435456` | Max upload size (256 MiB) | | `TRASH_RETENTION_DAYS` | no | `30` | Days before trashed assets are permanently purged | | `RUST_LOG` | no | `info` | Log level filter | ## Development ```bash # prerequisites: postgres, nats-server, bun # backend cp .env.example .env # edit DATABASE_URL, JWT_SECRET cargo run -p bootstrap # API server on :8000 cargo run -p worker # background job runner # frontend cd k-photos-frontend bun install bun run dev # Next.js on :3000 (proxies /api/v1 to :8000) # tests cargo test --workspace ``` ## Docker ```bash docker compose up -d # postgres + nats cargo run -p bootstrap cargo run -p worker ``` ## License [MIT](LICENSE)