Photo Enhancer
Bulk nature-photo enhancement that runs locally, proves its own output, and deletes everything within 24 hours.
Project identity
Photo Enhancer takes dozens of nature photographs at once and returns balanced, corrected versions with a before-and-after review gallery and a single download. It runs as one container on the local host, nothing is uploaded anywhere, and every file it holds is deleted within twenty-four hours.
The application is a Python service using an asynchronous web framework, a headless build of a computer-vision library, numeric array processing, and an in-process scheduler for retention. The frontend is served as static files from the same container.
The enhancement pipeline
Seven stages run in a fixed order, and the order is load-bearing: these operations do not commute, so running them in a different sequence produces a different and worse photograph.
Output is full-resolution JPEG at quality 92 with full chroma sampling, an embedded standard colour profile, and original camera metadata preserved — except location data, which is stripped.
The two settings most responsible for photographs that look cheap are local-contrast strength and clarity amount. Both ship well below their maximum on purpose. The tuning document records why, so a future change that raises them is a deliberate choice rather than an accident.
Retention and the purge
Everything the application stores — originals included — is deleted twenty-four hours after the batch is created. The interface shows a live countdown per batch, and a batch that is not downloaded before the clock runs out is gone.
How the purge behaves
- Runs every fifteen minutes, and once at startup before the API accepts traffic.
- Is batch-atomic: an entire batch is removed or none of it is, so the gallery is never half empty.
- Keys expiry on the recorded creation timestamp in UTC, never on file modification time.
- Touches only batch and export directories; anything else left in the folder is never eligible, at any age.
How it refuses to run
- A marker file must exist in the storage root, so an unconfigured path deletes nothing.
- The storage root must be at least one directory below a drive root.
- Every deletion target must resolve inside that root.
- It ships in dry-run mode, so the first run reports what it would delete instead of deleting it.
Modification time is unreliable on the removable drive: that filesystem stores local wall-clock time and shifts under daylight saving, so an expiry keyed on it would delete early or late twice a year. And if the external drive is unplugged while another volume claims the same drive letter, the marker-file and path checks mean nothing is deleted rather than the wrong thing being deleted.
The purge only runs while the container runs. Files never survive twenty-four hours of uptime, but they can linger while the service is stopped; the startup sweep catches up before the API accepts any traffic.
Storage design
Photographs and application state are deliberately kept on different volumes, because they have opposite lifetimes.
Archives are assembled in a memory-backed temporary filesystem before a single sequential copy to the external drive, because that drive is the slowest component in the system by a wide margin and everything hot is kept off it.
Security and exposure
- The published port is bound to the loopback address only. The service binds all interfaces inside the container because port publishing requires it, so that binding is the sole control keeping a personal photo library off the local network.
- Privilege escalation is disabled, and a memory limit is set because the processing pool holds several large floating-point buffers per worker; admission control adapts to that limit at runtime.
- Uploads are validated by file signature rather than by extension, with a decompression-bomb ceiling. A bad file is rejected individually and never takes the batch down with it.
- Location metadata is stripped from every output, so a shared photograph does not disclose where it was taken.
Verification and evidence
The service exposes a health endpoint, a summary of versions and environment, and a preflight report covering storage reachability, the marker file, writability, free space, and clock sanity. Output is validated rather than assumed: dimensions must match the source, values must be finite, and the frame must not be black, white, or flat.
A process that returns success can still write an unusable image. Checking the output rather than the exit code is what turns a pipeline run into evidence.
Failures are captured as structured issues at the moment they occur, deduplicated by fingerprint, and readable from the interface or exported through the API. That design exists because of the retention rule: the evidence is deleted within twenty-four hours, so a bug report written the next day has nothing behind it.
Lessons
- A retention policy is a destructive feature and deserves the same care as a database migration: preconditions, dry-run first, atomic units, and an audit log of every deletion.
- Storage that can be physically disconnected changes the threat model. The safe default is to delete nothing when the environment does not look the way it should.
- Separating durable application state from user data made both the purge and an unplugged drive survivable without special cases.
- Short retention has to be paired with immediate evidence capture, or every failure becomes unreproducible by the time anyone reads about it.
- Restrained defaults are a quality decision, and writing down why a setting is low prevents a future change from quietly undoing the work.
Roadmap
- Move the purge out of dry-run once the deletion log has been reviewed against a full retention cycle.
- Add a restore drill for the application-state volume, which currently survives by design but is not tested for recovery.
- Record per-batch processing statistics as release evidence so a pipeline regression is measurable rather than visual.
- Document a tuning profile for scenes other than foliage, since the current defaults are tuned for nature photography specifically.