DMS

Stash images. Find them with a phrase.

Self-hosted Dank Macro Stash for screenshots and photos. Every upload fans out to a queue of categorizers — thumbnails, OCR, image description, tag extraction, similarity. Anything any pipeline notices becomes a tag, so a year from now "McMaster catalog cover" still finds it.

Get started · invite-only Log in

What you get

A document-management system that does the boring parts — categorizing, tagging, indexing — so a year from now you can still find that one screenshot.

Five auto-categorizers

Every upload fans out to thumbnail, OCR (ocrs), image describe (VLM), tag extraction, and similarity-search. Each pipeline can be re-run on demand from the detail page.

Namespaced tags

desc:dog, Series:Friends, ocr:mcmaster. Tags carry their origin so categorizer output never collides with your own labels — and you can filter by namespace.

Phrase search

Search by file name, item description, or any tag from any pipeline. OCR text becomes ocr:* tags, descriptions become desc:* — toggle sources per query, or use ns:* to enumerate every value in a namespace.

Android companion

QR-pair from /api-tokens and you're in. Share images straight from the gallery into DMS, search and retag from your pocket, share back out via the system sheet. Uploads run in a foreground service — fire and forget.

Invite-only by default

Public registration is off until you flip REGISTRATION_ENABLED. Generate scoped invite links from /invites — optional email pin, optional expiry, one-click revoke.

Workers scale horizontally

RabbitMQ competing-consumer queues per categorizer. Run just worker ocr N times across N machines — work is distributed; failures dead-letter; idempotency is per-job.

Operable

The Work page shows live queue depth, consumer counts, recent completions per kind, and every failed job. One click retries a job, drains a DLQ back into the main queue, or replays everything at once.

Content-addressed storage

Files land at sha256/aa/bb/<hash>. Re-uploading the same image short-circuits to the existing record — no duplicate bytes, no duplicate categorizer runs.

JSON API parity

Every HTML surface — uploads, search, tags, retries, DLQ requeue — has a JSON twin under /api, authenticated with the same bearer token the Android app uses. Build whatever client you want.

Three steps to searchable

From "I have an image" to "I can find that image again" — the only manual step is the upload.

  1. 01

    Get in

    DMS is invite-only. Ask an existing user to generate a link from /invites, or run just invite on the server.

  2. 02

    Drop something in

    Upload from the web, or pair the Android app from /api-tokens and share images straight from your gallery. Categorizer jobs fire off in the background — watch the status grid on the detail page.

  3. 03

    Find it again

    Filter by free-text, tag, or namespace. Click any tag on a detail page to pivot to other uploads sharing it. Re-run a pipeline anytime if you swap in a smarter model.

Built on Rust

axum + sea-orm on Postgres, RabbitMQ for the categorizer queues, ocrs for OCR, minijinja + Tailwind + DaisyUI on the front, a Kotlin/Compose Android client. Three binaries: webservice, queue-worker, registration-token.