cull has no authentication. It is built to run on a private network you control — a Tailscale tailnet or a home LAN — where the network itself is the security boundary. There are no users, no passwords, no sessions, and no authorization checks anywhere in the API.
The container binds 0.0.0.0 so it is reachable from other devices on that
network. That is deliberate, and it is safe only under the assumption above.
Anyone who can reach the port has full control of your image library. They
can browse every image, read embedded prompts and metadata, move files to
_trash, collapse duplicates, and — if update_enabled is on — make the NAS
download and run a new build of the application.
Concretely, do not:
- port-forward
8080(or whatever you set) from your router, - publish the port on a VPS with a public IP,
- put it behind a plain reverse proxy with no authentication,
- or attach it to an untrusted or shared network (a coworking space, a hotel, a shared hosting VLAN).
If you need access from outside your home, use a VPN or an overlay network such as Tailscale, WireGuard, or ZeroTier. That is the intended deployment, and it keeps the boundary where the design expects it.
If you genuinely must expose it more widely, put an authenticating reverse proxy in front (Caddy with basic auth, Authelia, oauth2-proxy, Cloudflare Access, or similar). cull will not do it for you, and it does not pretend to.
These endpoints change or destroy data. None of them require credentials:
| Endpoint | What it can do |
|---|---|
POST /action (trash) |
moves image files into a _trash sibling directory |
POST /duplicates/collapse* |
moves files into a _duplicates quarantine |
POST /duplicates/quarantined/purge |
moves the quarantine into _trash |
POST /rescan |
forces a full re-index (expensive, not destructive) |
POST /update |
asks the NAS-side watcher to rebuild and restart the app |
Triage is reversible by design — trash is a file move into _trash, not an
unlink, and restore puts it back. cull never calls unlink on your images.
Emptying _trash is left to you, deliberately.
POST /update is the sharpest edge, because it causes code to be downloaded
and executed on the NAS. It is off by default; it only works when you set
update_enabled: true and run the host-side watcher. If that combination
makes you uncomfortable, leave it off — everything else works without it.
Please use GitHub's private vulnerability reporting (Security → Report a vulnerability) rather than opening a public issue, so a fix can land before details are public.
Since the project assumes a trusted network, "no auth on the API" is a known and documented design decision, not a vulnerability. Reports that are useful: path traversal that escapes the configured library root, ways to make cull delete or overwrite files outside it, command injection into the update path, or anything that turns a request into code execution.