Skip to content

What krites does not do

krites is deliberately narrow. This page is the list of things it does not do, separated into the ones that are design decisions — settled, not gaps — and the ones that are not built yet.

A question that ends here has a real answer. "No, that is not supported" is a better answer than a workaround that will not work.

It is not a photo library or a catalog

krites operates on one shoot at a time: a directory of frames, plus its .krites/ sidecar. There is no cross-shoot catalog, no collection model, no search across shoots, no keywording, no album, no client delivery.

The studio keeps a ~/.krites/shoots.yaml list of folders you have opened, so you can switch between them. That list is a bookmark bar, not a catalog — it holds {id, name, path} and nothing else, and the CLI never reads it.

That is Lightroom's job. krites hands off; it does not replace.

It is not a RAW developer

krites does not decode RAW sensor data. A RAW frame is culled, previewed and exported through the full-size JPEG preview embedded in its container, extracted in pure Go.

The consequence is the one that matters: you get no RAW latitude in krites. Pulling back a blown sky or lifting a crushed shadow needs the sensor data, and krites never has it. Those are Lightroom edits, made from your originals, after krites has told you which frames are worth the effort.

This is also why an exported RAW frame is a JPEG rather than a RAW file, named IMG_2043.ARW.jpg — the .jpg is appended rather than swapped so a RAW+JPEG pair cannot collide.

Not everything travels to Lightroom

XMP sidecars carry a subset of your krites work, and it is a smaller subset than it looks:

Does travel Does not travel
The verdict, as a star rating and colour label The look — white balance, tone, grade, LUT
The crop rectangle Object removals
The straighten angle The reasons behind a verdict
The AI review

Looks and removals are not omissions waiting to be fixed. Adobe's Camera-Raw XMP cannot express a generative fill, and a half-applied colour mapping would be worse than none — you would open the frame in Lightroom and see something that was neither krites' render nor your original. So they stay krites-export-only.

If a keeper has a removal you need in Lightroom, import the rendered file from export/, not the original.

A RAW+JPEG pair collides on one sidecar. IMG_2043.ARW and IMG_2043.JPG both map to IMG_2043.xmp, because the extension is stripped rather than appended. When both are in the shoot, one verdict overwrites the other, and which one wins is not deterministic. Shoot RAW-only or JPEG-only if you rely on sidecars.

The studio cannot be shared

The studio binds loopback only and refuses to start on anything else — not 0.0.0.0, not a LAN address, not a hostname. There is no configuration key that changes this, and no plan for one.

It is also single-user: one session credential per launch, no accounts, no per-user state. Two people on one machine share one studio. Running krites on a server so a team can cull together is not a supported configuration.

There is only one cull profile

wedding-default is the only profile. --profile is not a selector — it is an assertion, checked against the shoot's resolved profile name and rejected if it does not match:

unknown profile "portrait" — this shoot uses "wedding-default"

What you can change are its thresholds, through the cull.profile.* keys — globally, or per shoot. What you cannot do is keep several named rulesets and switch between them. A profile catalog (krites profile …) is on the roadmap and does not exist today.

There is only one look

The look catalog ships with exactly one entry, neutral — identity white balance, identity tone, no LUT. No krites command or studio action writes a custom look, so krites develop --look <anything-else> fails with:

unknown look "warm" — this shoot's catalog has: neutral

Because neutral is the identity, applying it changes nothing: a frame with only a neutral look and no geometry edit is copied verbatim at export.

A shoot's .krites/develop.yaml is read when it exists, so a hand-written catalog does work. But writing it by hand is the only route today, and the file format is not a stable public interface.

The aesthetic score does not exist yet

The cull carries an aesthetic ranking tier and an aesthetic slot in the analysis cache, and cull.profile.aesthetic_floor is a real key. No scorer backend ships, so the tier is inert and the floor does nothing. Setting aesthetic_floor to a non-zero value will not change a single verdict.

The whole-frame "is this a strong photograph" model is deferred until it can be built as a CLIP-plus-aesthetic-head, so the same pass yields a reusable per-frame embedding the learning loop can also use. Building it twice would be the waste.

There is no Windows build

Releases are published for macOS and Linux, on amd64 and arm64. The packaged .dmg app is Apple Silicon only.

Windows is not a gap in the release matrix — the target does not compile. The ONNX face provider loads its runtime through purego.Dlopen and the RTLD_* constants, which are POSIX-only and undefined on Windows.

Nothing learns from you yet

krites banks the data a learner would need — every cull records the machine's proposal alongside your final verdict, with the signals behind both. Nothing consumes it.

There is no release date for the learning loop. It is the last phase on the roadmap, its design is a draft, and it is deliberately parked pending real usage rather than scheduled — building a learner before there is a body of real decisions to learn from would be guessing.

What is already settled is the constraint any design has to satisfy: krites is local-first, and every cloud-capable provider is off by default and opt-in per capability, with disclosure before pixels leave the machine. Nothing that ships under those rules trains on your photographs without you turning it on.

Reset cannot be undone, and does not ask

None of the three reset subcommands prompt for confirmation, and none take --force or --dry-run. krites reset shoot in its default mode permanently deletes the object-removal patches — the only copies of those generative fills.

The safer instinct when a cull came out wrong is krites cull --reanalyze, which forces a fresh analysis pass and deletes nothing, or reset shoot --keep-picks, which keeps everything you decided by hand.

Ingest does not go looking

krites ingest registers the files directly inside the folder you name.

  • It is not recursive — a card dump with frames in DCIM/100CANON/ registers nothing and fails with "no supported frames".
  • It accepts exactly .jpg, .jpeg, .png, .arw, .cr2, .nef, .dng. Anything else — HEIC, TIFF, video, a .CR3 from a newer Canon body — is skipped silently, with no warning and no line in the report.
  • It registers in place. It does not copy or move anything, so it is not an importer: there is no card-to-disk ingest step, and the folder you name is the folder krites works in.

Background jobs do not survive a restart

A cull or export running in the studio lives in memory. Restarting the studio loses it. What survives is a breadcrumb under the shoot's .krites/jobs/, so the studio can tell you a cull was interrupted rather than silently forgetting — but it cannot resume one.

Closing the browser tab is different, and safe: the job keeps running and you can reattach when you come back.

There is also no concurrency limit. Nothing stops three shoots culling at once, and nothing queues them.

AI review is not part of the cull, and not reproducible

krites review never sets a verdict or a rating. It is advisory coaching, stored as a sidecar, offered to the future learner.

It also sends the full image to a third-party model, which is why it is off by default with no ambient key. And a generative critique is not reproducible: running it twice on the same frame gives two different readings. That is fine for advice and would not be fine for a verdict — which is exactly why the two are kept apart.

The studio API is not a stable interface

The /api/v1/ prefix marks the current shape, not a compatibility promise. It is an internal contract between the binary and the SPA embedded in it, and it changes without a deprecation window. Scripting against it is supported in the sense that it works; it is not supported in the sense that it will keep working.