- 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>
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 thebrandcrate. - 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).
- 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).
- Every workspace crate must be registered in the table; an unregistered crate fails the check.
- Standalone crates sit at L0 and may depend on no workspace crate.
- testkit may depend on anything up to L5; other crates may use it only as a dev-dependency.
- UI toolkits (
egui,eframe,winit,egui_kittest,rfd,bevy*) may be used from L5 up only. - 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
catalogAPI, with lazy photo records, backup with integrity check, and aremote_identitytable 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-YYYYMMDDbranch, runcargo 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.