# Draku: Exorcising the Ghosts in Your Camera Roll with Offline Open-Weight AI 🦇

> Source: <https://dev.to/devanshdubey/draku-exorcising-the-ghosts-in-your-camera-roll-with-offline-open-weight-ai-3826>
> Published: 2026-10-31 14:49:54+00:00

*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine](https://dev.to/challenges/hacktoberfest-week4-2026-10-26)*

You are standing at the pharmacy counter. The pharmacist asks for your prescription. You know it is on your phone—somewhere between 4,000 WhatsApp forwards, food photos, and old boarding passes. You scroll past `IMG-20261011-WA0004.jpg`, `Screenshot_2026-09-14.png`, and `scan_final_v2.pdf`. 

Screenshots are the most-used notebook on earth that nobody calls a notebook. The problem is that a screenshot stores **pixels**, not **meaning**. And inside those pixels live four kinds of digital ghosts haunting every laptop and phone:

Meet **Draku**—a fluffy black cat with crimson bat wings and eyes full of black flames. He sees everything on your device, and he tells no one.

**Draku** is a 100% offline, open-source desktop application (built with Tauri) that reads every screenshot, photo, and PDF in a folder **once** using local **PaddleOCR** and Google's open-weight **EmbeddingGemma**, turning that single reading into four concrete outcomes:

`IMG-20261011-WA0004.jpg` into `2026-10-11 Dr Patel blood test prescription.jpg` and embeds the semantic context so you can type `"blood test"` or `"router password"` in plain English and find it instantly.
Zero bytes uploaded. Zero accounts. Zero settings screens.

| Outcome | Before | After | What produces it | 
|---|---|---|---|
| **Find** | `IMG-20261011-WA0004.jpg` | `2026-10-11 Dr Patel blood test prescription.jpg` , findable by typing "blood test" | local OCR → rule-based reader → **EmbeddingGemma-300M** → semantic search | 
| **Remember** | A warranty screenshot quietly expiring | A real `.ics` calendar reminder, one tap | date extraction with surrounding words → confirm → local `.ics` | 
| **Protect** | A photo exposing an ID number and GPS | A safe copy with the blur burned into pixels and location metadata stripped | local OCR bounding boxes + EXIF → confirm → clean copy | 
| **Let go** | OTPs, dead tickets, duplicates | Rested in a folder you can restore from | transparent signals → an explainable dead score → "Set aside" | 

Every capture below is recorded directly from the real desktop application on Windows with **Wi-Fi off and Airplane Mode visibly enabled**—no cloud calls, no mockups, no staged telemetry. The full airplane-mode walkthrough script lives in the repo as [`DEMO.md`](https://github.com/0dvsh0/draku/blob/main/DEMO.md).

Local PaddleOCR extracts the text, a structured rule-reader extracts human naming cues, and Google's **EmbeddingGemma-300M** embeds the reading into a 768-dimensional local vector. Type *"internet password"* and the router login surface appears immediately—pure meaning match with zero keyword overlap.

Dates are extracted alongside their surrounding contextual words, allowing Draku to recognize that *"Warranty ends in 6 days"* or *"Refill by 10 Nov"* is an actionable deadline rather than an old receipt date. One click generates an RFC-compliant `.ics` calendar file accepted by your system calendar—no cloud sync intermediary.

PaddleOCR returns exact pixel bounding boxes for sensitive ID numbers, card digits, and credentials. Clicking "Make a safe copy" burns irreversible pixelation blur directly into new pixel buffers and strips hidden EXIF location metadata. The original document remains untouched.

*Before and after, side by side — the sensitive ID number and GPS coordinates visible in the first frame are permanently eradicated in the safe copy:*

Expired boarding passes, old OTP screenshots, and blurry duplicates earn transparent, inspectable "dead file" scores with human-readable explanations. Clicking "Set aside" lays them to rest in `.draku/rest/`—never deleted, completely reversible with one-tap Undo.

**Your files, finally understood.**

Draku is a free, open-source, on-device AI file manager for Windows. It reads
every screenshot, photo and PDF **on your machine** — it finds what you need
warns you before a date passes, hides what should stay private, and lets go of
what is dead. Nothing leaves your machine.

Built for **Hacktoberfest 2026 · Week 4 — Ghost in the Machine**.

```
draku/
├── CONTEXT.md     ← project context: plan, stack, tokens, status (read first)
├── site/          ← the landing page (static HTML/CSS/JS + WebGL embers)
├── app/           ← the product: Tauri 2 desktop app (Rust + React/TypeScript)
│   ├── src/           One Screen UI, mascot design tokens (tokens.css)
│   ├── scripts/       bundle prep + one-time model fetch for source builds
│   └── src-tauri/     scanner · reader · OCR · embeddings · actions
└── site/About.md
```

…
To build and run Draku from source (fetches the local models once, then runs completely offline forever):

```
git clone https://github.com/0dvsh0/draku && cd draku/app
npm install && node scripts/fetch-model.mjs
npm run tauri dev
draku/
├── site/               ← Interactive web sandbox & landing page (vanilla HTML/CSS/JS, WebGL embers)
└── app/
    ├── src/            ← The One Screen UI (React 19 + TypeScript, mascot design tokens)
    ├── scripts/        ← Model fetchers, NSIS installer packager, automated CI smoke test
    └── src-tauri/      ← Native Rust core touching every byte
        ├── scan.rs     ← Folder walker + dHash perceptual duplicate detection
        ├── extract/    ← The READER: PaddleOCR, PDF text parser, kamadak-exif, rule reader
        ├── embed.rs    ← Bundled EmbeddingGemma-300M loader + local cosine similarity search
        ├── score.rs    ← Transparent "Let Go" arithmetic engine — weights you can read
        ├── store.rs    ← SQLite (WAL mode) — single local embedded DB, zero daemons
        └── actions.rs  ← Safe copies, .ics generation, set-aside / restore (the ONLY disk mutations)
```

EmbeddingGemma's 4-bit quantized weights ship inside the installer as `model_q4.onnx` alongside an external initializer file. Draku's native Rust core loads both in memory directly into ONNX Runtime with DirectML hardware acceleration—without requiring CUDA or external services:

``` js
// app/src-tauri/src/embed.rs — the offline path is the only shipped path
let mut bundled = UserDefinedEmbeddingModel::new(read("model_q4.onnx")?, TokenizerFiles {
    tokenizer_file:          read("tokenizer.json")?,
    config_file:             read("config.json")?,
    special_tokens_map_file: read("special_tokens_map.json")?,
    tokenizer_config_file:   read("tokenizer_config.json")?,
})
.with_pooling(Pooling::Mean)
.with_quantization(QuantizationMode::None)
.with_external_initializer("model_q4.onnx_data".into(), read("model_q4.onnx_data")?);

TextEmbedding::try_new_from_user_defined(bundled, InitOptionsUserDefined::new())
```

When you search *"internet password"*, your query is converted to a 768-dimensional vector, and cosine similarity matches against local readings in SQLite. There is no HTTP client anywhere in the crate.

Rather than relying on an unexplainable black-box classifier, `score.rs` exposes a plain struct of inspectable signals. Every weight is an auditable constant with a clear rationale:

``` php
// app/src-tauri/src/score.rs
pub fn judge(s: &Signals) -> Verdict {
    let mut score = 0.10;
    score += (s.age_days / 180.0).min(0.30);
    if s.is_otp      { score += 0.44; }   // one-time codes only live once
    if s.date_passed { score += 0.50; }   // the deadline is gone; the file overstayed
    if s.is_dup      { score += 0.50; }   // a near-identical file exists
    // …the result clamps to [0, 1]; above 0.60 a file earns the Let-go card
    Verdict { dead: score.clamp(0.0, 1.0), reason: choose_reason(&s) }
}
```

`--no-default-features`) for machines that cannot run PaddleOCR.` scripts/smoke-test-installer.mjs`, wired into GitHub Actions, silent-installs the NSIS package on a clean Windows runner and verifies every bundled binary: `draku.exe`, `DirectML.dll`, all 4 OCR models, and all 6 embedding files.` SHA256: 19473E9A5AEC88BDE82DF50CD697E7A9450A0B0EA88B49E65B57F89843E52791`
Most AI tools bolt four separate pipelines onto four separate problems: one tool for semantic search, another for reminders, another for PII redaction, and a fourth for storage cleanup. That wastes compute and clutters the UI.

Draku is built around a single architectural rule: **One reading. Four outcomes.**

```
[ Screenshot / Photo / PDF ]
             │
             ▼
   ┌───────────────────┐
   │  Local PaddleOCR  │ ──► Extracts text + exact pixel bounding boxes + EXIF
   └─────────┬─────────┘
             │
             ▼
   ┌───────────────────┐
   │ Structured Reader │ ──► Parses dates, PII patterns, category & dead-score
   └─────────┬─────────┘
             │
             ▼
   ┌───────────────────┐
   │  EmbeddingGemma   │ ──► Generates local semantic vector embeddings
   └─────────┬─────────┘
             │
             ▼
      [ reading.json ]
             │
   ┌─────────┴──────────┬────────────────────┬───────────────────┐
   ▼                    ▼                    ▼                   ▼
[ FIND ]           [ REMEMBER ]         [ PROTECT ]         [ LET GO ]
Semantic search    Contextual .ics      Pixel-burned PII    Transparent
& smart rename     deadline alerts      & EXIF stripping    rest scoring
```

When Draku scans a file like `IMG-20261011-WA0004.jpg`, it produces a single structured internal artifact (` reading.json`):

```
{
  "name":     "2026-10-11 Dr Patel blood test prescription",
  "category": "medical / prescription",
  "dates":    [{ "refill_by": "2026-11-10" }],
  "private":  ["patient_id", "gps"],
  "dead":     0.04,
  "network":  null
}
```

| Layer | Choice | Why It Earns Its Place | 
|---|---|---|
| **Shell & Core** | **Tauri 2 · Rust** | Native filesystem speed, minimal memory footprint, and zero open sockets. Bundles everything into a clean 182 MiB installer instead of a bloated Electron container. | 
| **Semantic Memory** | **Google EmbeddingGemma-300M (4-bit ONNX)** | Generates 768-dimensional embeddings entirely on-device from open weights, making true offline semantic search possible from the first run. | 
| **Local Runtime** | **fastembed + ONNX Runtime + DirectML** | Hardware acceleration across modern Windows GPUs/NPUs with zero CUDA configuration. Loads external weight initializers directly in memory. | 
| **Vision & Text** | **PaddleOCR PP-OCR (Apache-2.0)** | Detection + classification + recognition returning exact bounding boxes that directly feed Protect's pixel-burned redaction. | 
| **Duplicate & Metadata** | **dHash + kamadak-exif** | 64-bit visual perceptual hashing detects rescaled copies; parses EXIF metadata to reveal hidden GPS coordinates and camera serials. | 
| **Frontend** | **React 19 + TypeScript + Vite** | Type-safe IPC boundaries, crisp UI with mascot tokens, and `@fontsource` vendored typography (zero CDN calls). | 
| **Persistence** | **SQLite (WAL) via rusqlite** | Single embedded database in the local app directory storing readings, vectors, and approved action history. | 

`"pharmacy receipt"` or `"cholesterol check"`, EmbeddingGemma matches the meaning even if those exact words aren't in the filename.`OTP`, `ticket`, `receipt`), whether the extracted date has passed, perceptual duplicate distance, blurriness, and byte size. You can read the exact reason Draku suggests letting a file rest.`network` is always `null`.
Windows is the primary platform where DirectML and WebView2 ensure a guaranteed, zero-friction experience out of the box. For hardware without OCR capabilities, Draku provides a lightweight build (`--no-default-features`): semantic search, dates, duplicate detection, and rest actions function without OCR, while Protect reports what can be discerned from names and metadata.

If a tool asks to read your entire camera roll and `Downloads` folder, **it is asking to read your life**: your prescriptions, tax returns, national ID cards, bank statements, and private family photos.

You cannot build a trustworthy version of Draku on a closed, cloud-hosted API. Here is why open-weight models and open-source code are the only way this product can exist:

`fetch-model.mjs`), your personal file guardian will work identically ten years from now on an air-gapped laptop.
When I saw the **Ghost in the Machine** theme coinciding with Halloween and *Día de los Muertos*, I didn't want to build another spooky terminal chatbot pretending to be a poltergeist. I started thinking about the real ghosts that live inside our machines every single day.

Our devices are full of unfinished business: the prescription we forgot to refill, the warranty we missed by three days, the old ticket stub from a trip we took a year ago, and the dead OTP screenshots cluttering the drawer. In the spirit of *Día de los Muertos*, some things in our digital lives deserve to be **remembered and honoured on time**, our private secrets deserve to be **protected**, and the things whose time has passed deserve to be **laid peacefully to rest**.

That is why Draku is a tiny guardian familiar—eyes of black flame to read the dark pixels, bat wings to shield your secrets, and a quiet nudge to help you let go of what is dead.

`ci.yml`): linting and type-checking (React 19 + TypeScript + Vitest), dual cargo test suites (light and full OCR) with model caching, and a Windows runner job that builds the NSIS installer, *Built with 🦇 for Hacktoberfest 2026 — [Draku on GitHub](https://github.com/0dvsh0/draku) · [Landing page](https://0dvsh0.github.io/draku/) · [Download the installer](https://github.com/0dvsh0/draku/releases/latest/download/Draku_0.1.0_x64-setup.exe)*
