Gabriel Kaszewski 957737ac9b feat: frontend MVP — auth, timeline, upload, albums, admin, image viewer
Backend:
- user roles (DB + JWT + first-user-is-admin)
- volume-aware file resolver (multi-volume asset serving)
- directory scanner uses volume URI directly
- date-summary endpoint (capture date from EXIF)
- timeline ordered by capture date
- list endpoints: volumes, plugins, pipelines, library paths
- delete endpoints: volumes, library paths
- configurable upload body limit (MAX_UPLOAD_BYTES)

Frontend:
- auth: login/register, token refresh, role-based admin gate
- timeline: date-grouped grid, infinite scroll, date scrubber
- image viewer: fullscreen zoom/pan/pinch, metadata sidebar
- upload: drag-drop, sequential upload, progress tracking
- albums: create, add/remove photos, asset picker dialog
- admin: storage (import library), jobs (pagination, error details),
  plugins (list + toggle), pipelines, sidecars, duplicates
- multi-select mode with add-to-album action
- TanStack Query for all data fetching
2026-06-01 01:35:43 +02:00
2026-05-31 22:27:55 +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.

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
    common/             errors, events, value objects (SystemId, Checksum, Email,
                        Username, MimeType, RelativePath, etc.)
    identity/           user, role, permission, group, refresh token
    storage/            volumes, library paths, ingestion, quotas
    catalog/            assets, metadata, stacks, derivatives, duplicates
    organization/       albums, tags, collections
    sharing/            share scopes, targets, links, invites
    sidecar/            sidecar records, sync config
    processing/         jobs, batches, plugins, pipelines

  application/          CQRS commands + queries with Arc<dyn Port> injection
    identity/commands/  RegisterUser, LoginUser, RefreshToken, Logout
    identity/queries/   GetProfile
    storage/commands/   RegisterVolume, RegisterLibraryPath, IngestAsset
    storage/queries/    CheckQuota
    catalog/commands/   RegisterAsset, UpdateMetadata, CreateStack, DeleteStack,
                        DeleteAsset, DetectLivePhotos, ResolveDuplicate
    catalog/queries/    GetTimeline, GetAsset, GetStack, ReadAssetFile,
                        ReadDerivative, SearchAssets
    organization/       CreateAlbum, ManageAlbumEntries, TagAsset, GetAlbum
    sharing/            ShareResource, GenerateShareLink, RevokeShare, AccessSharedResource
    sidecar/            ExportSidecar, DetectChanges, Import, ResolveConflict, FullExport/Import
    processing/         EnqueueJob, StartJob, CompleteJob, FailJob, ExecutePipeline,
                        ManagePlugin, ConfigurePipeline, ListJobs, BatchProgress
    testing/            in-memory repo fakes + stub ports (in_memory_repo! macro)

  api-types/            HTTP request/response DTOs with OpenAPI derives
  adapters/
    auth/               bcrypt password hashing, JWT token issuer (configurable expiry)
    postgres/           all repository implementations, event store, migrations
    storage/            local filesystem + S3 (feature-gated)
    exif/               EXIF metadata extraction
    thumbnail/          derivative generation
    sidecar/            XMP sidecar reader/writer
    event-payload/      domain event serialization
    event-transport/    composite publisher (NATS + event store)
    nats/               NATS JetStream transport
  presentation/         axum handlers, routes, extractors, middleware, parsers
  bootstrap/            config, DI wiring, entry point
  worker/               background job runner (NATS consumer)

Auth

  • JWT access tokens (1h expiry, configurable)
  • Refresh token rotation (30d, SHA-256 hashed, stored in Postgres)
  • POST /auth/login — returns access + refresh tokens
  • POST /auth/refresh — rotates refresh token, issues new pair
  • POST /auth/logout — revokes all refresh tokens for user
  • require_auth middleware on all protected routes (defense in depth)
  • Handlers still use JwtClaims extractor for user_id — middleware is the safety net
  • Admin-only endpoints gated by role check (processing, storage, sidecar management)

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
RUST_LOG no info Log level filter

Development

# run tests (no DB required)
cargo test --workspace

# format + lint
cargo fmt --all
cargo clippy --workspace

206 tests cover domain entities, services, application use cases, and visibility filtering.

Docker

docker build -t k-photos .
docker run -e DATABASE_URL=... -e JWT_SECRET=... -e NATS_URL=... -p 8000:8000 k-photos

Worker (background jobs):

docker run -e DATABASE_URL=... -e NATS_URL=... k-photos ./k_photos-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%