Files
lightcraft/plan/architecture.md
T
Andrew FrahnandClaude Opus 5.5 6cbc0eb5a9 Phase 0: configurable brand, MIT licence, plan/ and Immich tracker
- brand.toml is the only place the product name lives; every crate and app
  reads it through dac-brand (env vars with legacy fallback, settings folder
  with copy-only migration, default library, preset extensions, i18n {app})
- cargo xtask brand check|test, run|install|package, docs, rename-crates,
  deny and immich; packaging files are templates rendered from brand.toml
- icons regenerated from brand/icon.svg; upstream branding removed
- MIT licence (segment and fetch stay Apache-2.0); cargo deny licence policy
- clean-room rules in AGENTS.md/CLAUDE.md; docs/upstream-merge.md
- plan/ rebuilt and committed; parity tracker reopens Classic rows, adds
  PLAN.md section 2 rows and the IMM Immich section; plan/immich.md and a
  pinned Immich test server

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 10:30:06 +11:00

5.1 KiB

Architecture

Target architecture of the fork, from PLAN.md §5. Section numbers here are cited by tooling: cargo xtask layers points to §3 for the layering rules.

1. Principles

  • Pure Rust. No C/C++ is linked and no foreign code is pasted in. Algorithms come from papers, prose specs or our own design (clean-room rules in AGENTS.md).
  • Every action is a command. The engine's command registry is the single API used by the UI, the CLI, the JSON control channel and the MCP server. New features land as commands first.
  • Downstream of upstream. The fork merges upstream regularly (docs/upstream-merge.md). Keep upstream crates as unchanged as possible and put Classic-only work in new crates and new UI modules, so merges stay cheap.
  • One name, one file. The product name lives only in brand.toml; code reads it through the brand crate.
  • Never crash. Malformed input and failures are errors, never panics (AGENTS.md, "Never crash").

2. Target layers

Packages are named dac-<name>; the prefix is an internal codename never shown to users. NEW marks crates the phases add; (donor) marks crates ported from the donor project.

L0  brand geom color raster tiff sysmem fetch heif
L1  raw codecs meta develop denoise-core denoise faces scenes
    cms (donor)      camdb NEW (per-camera data)      lensdb NEW (lens profiles)
    text (donor)     pdf NEW (multi-page PDF, font embedding, ICC)
L2  pipeline (+ region / tiled full-resolution renders)
    inpaint (donor algorithms: PatchMatch, content-aware fill, GrabCut refinement)
L3  gpu catalog preview export merge segment          catalog → indexed on-disk store
    layout NEW (pages, cells, templates for Print / Book / Slideshow / Web)
    geo NEW (tiles, projections, clustering, GPX, saved locations, geocoding)
    tether NEW (PTP over USB / PTP-IP; folder-watch fallback)
    media NEW (audio for slideshows; video decode/encode)
L4  engine → engine-core, engine-library, engine-develop, engine-export, engine-output, engine-publish
             (engine stays as a thin facade)
    plugins (donor: WASM sandbox, publish/export SDK)
L5  ui-egui (Classic shell: Module trait, panel framework, Classic keymap layer)   mcp
    net NEW (HTTPS client: JSON, multipart, proxies, custom CAs)
    credentials NEW (OS keychain / encrypted file)      immich NEW (client, link, publish, sync)
Apps: `app`, `app-cli` (+ web build), renamed from brand.toml when packaged

3. Crate layering rules

Enforced by cargo xtask layers (xtask/src/layers.rs, part of cargo xtask ci).

  1. A crate may depend only on crates in lower layers, or on crates in the same layer when an explicit intra-layer order allows it (below).
  2. Every workspace crate must be registered in the table; an unregistered crate fails the check.
  3. Standalone crates sit at L0 and may depend on no workspace crate.
  4. testkit may depend on anything up to L5; other crates may use it only as a dev-dependency.
  5. UI toolkits (egui, eframe, winit, egui_kittest, rfd, bevy*) may be used from L5 up only.
  6. Apps and build tooling (app, cli, web, xtask) are exempt.

Current table (keep in sync with TABLE in xtask/src/layers.rs):

Layer Crates
L0 brand, geom, color, raster, tiff, sysmem, fetch, heif
L1 denoise-core, denoise, raw, codecs, meta, develop, faces, scenes
L2 pipeline
L3 gpu, catalog, preview, export, merge, segment
L4 engine
L5 ui-egui, mcp
testkit testkit
exempt app, cli, web, xtask

Allowed orders inside a layer (earlier may be used by later):

Layer Order
L0 geom → color → raster; tiff → raster
L1 denoise-core → denoise; meta → raw; meta → codecs; codecs → raw; meta → develop; develop → scenes

Planned additions (register each in the table in the same change that adds the crate): cms, camdb, lensdb, text, pdf at L1; inpaint at L2; layout, geo, tether, media at L3; the engine-* split and plugins at L4; net, credentials, immich at L5 (or lower if they turn out to need no engine types; net is a candidate for L0).

4. Data and formats

  • Catalog: today an operation log plus snapshot held in memory (fine to ~85k photos). Phase 1 moves it to an on-disk indexed store behind the same catalog API, with lazy photo records, backup with integrity check, and a remote_identity table that maps photos to Immich assets.
  • Sidecars: XMP with our own namespace from brand.toml [stable], never derived from the product name.
  • Checksums: SHA-1 of the original file is computed at import (Immich uses SHA-1 for duplicate detection; see plan/immich.md).

5. Upstream strategy

  • Merge upstream on a merge/upstream-YYYYMMDD branch, run cargo xtask ci, fix, merge.
  • Crate renames were done once, mechanically, right after the fork; incoming paths are rewritten by tooling.
  • Avoid editing hot upstream files for Classic features: add a module or crate and hook it in at one point.