Skip to content

Components

The building blocks of krites and how they fit together.

The deterministic engine

krites' value is its engine, and it lives in the pkg/ tree as a deliberate public Go API. The engine is pure Go and cgo-free, and compiles to GOOS=js GOARCH=wasm today. It never imports a native backend directly; those sit behind provider interfaces.

Package Role
analyze/quality Sharpness and exposure maths
analyze/dedup Perceptual hashing and clustering of near-duplicate bursts
cull Verdict resolution from a cull profile
develop (straighten, crop, look, bake) The geometry and colour edit records, and the pure-Go export bake
shoot The workspace model: manifest, sidecar, verdicts, edit records, removal patches
xmp The Lightroom XMP sidecar
export The render — the only pixel-producing path

Why the WASM constraint is load-bearing

Keeping the engine WASM-compilable is not about shipping krites to a browser tomorrow. It is a structural check that the judgement logic has no hidden dependencies.

The moment a verdict depends on a native library, the verdict becomes platform-dependent — the same shoot could cull differently on two machines, and the reason would be buried in a linker. Compiling to WebAssembly, where none of that is available, makes any such dependency a build failure rather than a mystery.

It is enforced as a build target in the project's own CI, so it cannot quietly rot.

Provider seams

Every heavy or native backend is injected behind a narrow interface, faked in tests, and selected from config. Each adapter is its own package, so native code never leaks into the WASM-compilable engine.

Interface Backend
Decoder Image decode and image operations
FaceAnalyzer Face and eye landmarks, expression and pose — local ONNX
AestheticScorer Learned whole-frame aesthetic scoring — no backend ships yet
Inpainter Generative object removal — LaMa or MI-GAN, local ONNX
Clusterer Burst clustering

Adding a backend is purely additive: a new adapter package behind the same interface, with no call-site changes. face.detector selecting between two face detectors, and remove.backend between two inpainters, are that seam showing in configuration.

The seam is also what makes "off by default" a real state rather than a skipped call — with a provider disabled the native code is never loaded. See Why the heavy providers are opt-in.

The studio

krites studio is the primary surface: a local, single-user, localhost-bound web UI — a Svelte single-page app embedded into the binary — served over an HTTP and SSE API onto the same shoot files the CLI uses. The cull-review grid stays responsive on 4,000-plus frames because previews are cached and the grid is virtualised.

The studio adds no domain logic of its own. Every endpoint maps onto an engine operation, which is what keeps the CLI and the studio genuinely interchangeable mid-shoot rather than approximately so. Its API is documented in the studio API reference.

Because the SPA is embedded at build time, a binary built without the generate step serves a placeholder — which is why go install produces a broken studio.

Built on go-tool-base

The root command, the dependency-injection container, the Cobra command tree, the config and keychain layers, the MCP server and the self-update machinery all come from go-tool-base. krites is a downstream consumer that leans on those primitives rather than re-implementing them.

That is why krites has commands it never wrote — init, config, doctor, update — and why they behave identically in every phpboyscout tool. It is also why the four global flags exist on every command without krites declaring them.

Generated files carry a DO NOT EDIT banner; new commands are scaffolded rather than hand-written, which is why every command has the same flag conventions and the same --shoot semantics.