Gabriel Kaszewski c251a5c41f perf: concurrent worker with claim/execute split + graceful shutdown
- JobRepository::claim_next() — atomic SELECT FOR UPDATE SKIP LOCKED +
  UPDATE status=processing in one query, no duplicate claims
- ExecutePipelineHandler skips start() for already-claimed jobs
- Sweep spawns N concurrent tasks via JoinSet, claims are fast+sequential,
  execution is slow+concurrent
- Graceful shutdown: stop claiming, await all in-flight JoinSet tasks
- WORKER_CONCURRENCY env (default: CPU cores)
- DB_MAX_CONNECTIONS env (default: 20, was hardcoded 10)
- VolumeFileResolver impl for InMemoryFileStorage (test fix)
2026-06-01 02:14:44 +02:00
2026-05-31 22:27:55 +02:00

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<dyn Port> 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

# 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

docker compose up -d      # postgres + nats
cargo run -p bootstrap
cargo run -p worker

License

MIT

Description
Self-hosted media orchestrator and gallery. Alternative to Apple Photos, Google Photos, and Immich.
Readme MIT 1.8 MiB
Languages
Rust 67.7%
TypeScript 31.5%
CSS 0.5%
Dockerfile 0.2%