<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<section class="py-20 sm:py-28 text-center">
<p class="font-mono text-xs uppercase tracking-[0.25em] text-accent mb-4">DMS</p>
<h1 class="text-4xl sm:text-6xl font-bold tracking-tight">
Stash images. Find them with a phrase.
</h1>
<p class="text-base-content/70 text-lg sm:text-xl mt-6 max-w-2xl mx-auto leading-relaxed">
OCR every screenshot, describe every photo, tag everything as it lands.
Search later by anything a categorizer noticed — "McMaster catalog cover"
finds the photo of the McMaster catalog cover.
</p>
<div class="flex justify-center gap-3 mt-8">
{% if user %}
<a href="/uploads" class="btn btn-primary">
<i class="fas fa-fw fa-images"></i> Open uploads
</a>
<a href="/invites" class="btn btn-ghost">
<i class="fas fa-fw fa-envelope"></i> Invites
</a>
{% else %}
{% if registration_open %}
<a href="/register" class="btn btn-primary">Get started</a>
{% else %}
<span class="btn btn-disabled" title="Registration is invite-only">
Get started · invite-only
</span>
{% endif %}
<a href="/login" class="btn btn-ghost">Log in</a>
{% endif %}
</div>
</section>
<section class="py-12 sm:py-16">
<h2 class="text-2xl sm:text-3xl font-bold mb-2">What you get</h2>
<p class="text-base-content/70 mb-8">
A document-management system that does the boring parts — categorizing,
tagging, indexing — so a year from now you can still find that one
screenshot.
</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="border border-base-300 rounded-box p-5 bg-base-200">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-fw fa-bolt size-5 text-accent"></i>
<h3 class="font-semibold">Auto-categorizers</h3>
</div>
<p class="text-sm text-base-content/80">
Every upload is fanned out to a queue: OCR, image describe (VLM),
similar-image detection, tag extraction. Each pipeline can be re-run
on demand from the detail page.
</p>
</div>
<div class="border border-base-300 rounded-box p-5 bg-base-200">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-fw fa-tags size-5 text-accent"></i>
<h3 class="font-semibold">Namespaced tags</h3>
</div>
<p class="text-sm text-base-content/80">
<code>desc:dog</code>, <code>Series:Friends</code>, <code>ocr:mcmaster</code>.
Tags carry their origin so categorizer output never collides with
your own labels — and you can filter by namespace.
</p>
</div>
<div class="border border-base-300 rounded-box p-5 bg-base-200">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-fw fa-magnifying-glass size-5 text-accent"></i>
<h3 class="font-semibold">Phrase search</h3>
</div>
<p class="text-sm text-base-content/80">
Search by file name, item description, or any tag from any pipeline.
OCR text becomes <code>ocr:*</code> tags, descriptions become
<code>desc:*</code> — the same query box hits all of them.
</p>
</div>
<div class="border border-base-300 rounded-box p-5 bg-base-200">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-fw fa-envelope-open-text size-5 text-accent"></i>
<h3 class="font-semibold">Invite-only by default</h3>
</div>
<p class="text-sm text-base-content/80">
Public registration is off until you flip <code>REGISTRATION_ENABLED</code>.
Generate scoped invite links from <a href="/invites" class="link">/invites</a>
— optional email pin, optional expiry, one-click revoke.
</p>
</div>
<div class="border border-base-300 rounded-box p-5 bg-base-200">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-fw fa-layer-group size-5 text-accent"></i>
<h3 class="font-semibold">Workers scale horizontally</h3>
</div>
<p class="text-sm text-base-content/80">
RabbitMQ competing-consumer queues per categorizer. Run <code>just worker
ocr</code> N times across N machines — work is distributed; failures
dead-letter; idempotency is per-job.
</p>
</div>
<div class="border border-base-300 rounded-box p-5 bg-base-200">
<div class="flex items-center gap-2 mb-2">
<i class="fas fa-fw fa-fingerprint size-5 text-accent"></i>
<h3 class="font-semibold">Content-addressed storage</h3>
</div>
<p class="text-sm text-base-content/80">
Files land at <code>sha256/aa/bb/<hash></code>. Re-uploading the
same image short-circuits to the existing record — no duplicate
bytes, no duplicate categorizer runs.
</p>
</div>
</div>
</section>
<section class="py-12 sm:py-16">
<h2 class="text-2xl sm:text-3xl font-bold mb-2">Three steps to searchable</h2>
<p class="text-base-content/70 mb-8">
From "I have an image" to "I can find that image again" — the only
manual step is the upload.
</p>
<ol class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<li class="border border-base-300 rounded-box p-5 bg-base-200 flex flex-col gap-2">
<span class="font-mono text-xs text-accent">01</span>
<h3 class="font-semibold">Get in</h3>
<p class="text-sm text-base-content/80">
{% if registration_open %}
Sign up directly — registration is open.
{% else %}
DMS is invite-only. Ask an existing user to generate a link from <a href="/invites" class="link">/invites</a>, or run <code>just invite</code> on the server.
{% endif %}
</p>
</li>
<li class="border border-base-300 rounded-box p-5 bg-base-200 flex flex-col gap-2">
<span class="font-mono text-xs text-accent">02</span>
<h3 class="font-semibold">Drop something in</h3>
<p class="text-sm text-base-content/80">
Visit <a href="/uploads" class="link">Uploads</a>, pick a file, add
optional tags. Thumbnails, OCR, and describe jobs fire off in the
background — watch the status grid on the detail page.
</p>
</li>
<li class="border border-base-300 rounded-box p-5 bg-base-200 flex flex-col gap-2">
<span class="font-mono text-xs text-accent">03</span>
<h3 class="font-semibold">Find it again</h3>
<p class="text-sm text-base-content/80">
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.
</p>
</li>
</ol>
</section>
<section class="py-12 sm:py-16 text-center">
<h2 class="text-2xl sm:text-3xl font-bold mb-2">Built on Rust</h2>
<p class="text-base-content/70 max-w-2xl mx-auto">
axum + sea-orm on Postgres, RabbitMQ for the categorizer queues,
<a href="https://github.com/robertknight/ocrs" class="link">ocrs</a>
for OCR, minijinja + Tailwind + DaisyUI on the front. Three binaries:
webservice, queue-worker, registration-token.
</p>
</section>
</div>
⭐
Log in to mark this repository.
7.3 KiB · text
History
1ce9641