Local-first, cross-platform file transformation engine that works entirely offline.
Rupant is a privacy-first file conversion tool with a layered architecture where the core engine is the single source of truth. All interfaces (CLI, Desktop, Server) must use the core layer.
- Core-first: Single source of truth (core engine), all interfaces must use it
- No duplication: No business logic in CLI or UI layers
- Adapter pattern: External tools are wrapped in adapters (see below)
- Work pool: Queue + worker pool for conversions
- Local privacy: No external uploads, everything runs locally
| Role | Tool | Notes |
|---|---|---|
| Raster images (PNG, JPEG, WebP, GIF, TIFF, ICO, …) | ImageMagick | Rupant resolves magick (IM7) or convert (IM6) on PATH. |
| Video & audio | FFmpeg (+ ffprobe when available) | Progress uses -progress pipe:1 when possible, with stderr time= as fallback if the pipe never reports out_time_ms. |
| Documents (PDF, DOCX, Markdown, …) | Pandoc | In-app progress for Pandoc is coarse (stderr activity), not page-accurate. |
| PDF engine (used by Pandoc) | Typst / tectonic / pdflatex / xelatex / lualatex / wkhtmltopdf | Rupant auto-detects the first available engine in that order. Typst and tectonic are preferred as they install without a full LaTeX distribution. A bundled engine placed next to the Rupant executable (or in bin/) is also detected automatically. |
The desktop Settings → Tools & diagnostics panel probes all of the above and suggests install commands when something is missing. If no PDF engine is found, Markdown-to-PDF conversions will warn before running.
- Pause (desktop queue): Applies between queued jobs. A job that is already running is not frozen mid-encode.
- Cancel: The per-job context is cancelled; the backend process is stopped via
exec.CommandContext(e.g. FFmpeg/ImageMagick/Pandoc).
┌─────────────────────────────────────────┐
│ CLI │ Desktop │ Server │ (thin wrappers)
├─────────────────────────────────────────┤
│ Core Engine (single source) │
│ - Job model │
│ - Process(Job) function │
│ - Worker pool │
├─────────────────────────────────────────┤
│ Adapters: imagemagick, ffmpeg, pandoc │
└─────────────────────────────────────────┘
Rupant/
├── adapters/
│ ├── ffmpeg/ # Video & audio (not still images)
│ ├── imagemagick/ # Raster images
│ └── pandoc/ # Documents; pdf_engine.go handles auto-detection
├── cmd/rupant/ # CLI entrypoint
├── core/ # Engine, job model, worker pool
├── desktop/ # Wails app (Go + Svelte)
│ ├── scripts/ # Build helpers (e.g. fetch-typst.ps1)
│ └── src/lib/ # Svelte components & stores
├── pkg/
│ ├── cli/ # Cobra commands
│ ├── config/ # Persistent settings
│ └── convertpaths/ # Output path rules (shared with desktop)
└── README.md
All interfaces route conversion through core.Engine.Process(ctx, job).
- CLI:
cmd/rupant/main.go→pkg/cli→core.Engine.Process() - Desktop: UI → Wails →
desktop/app.go→core.Engine.Process()
Both interfaces call the same core engine and share output-path rules (pkg/convertpaths). This table is for advanced users choosing between the GUI and rupant on the command line.
| Capability | Desktop (Wails) | CLI (rupant) |
|---|---|---|
| Single-file conversion | Yes | rupant convert <file> --to <fmt> |
| Many files / queue | Staged queue, Run batch | rupant batch "<glob>" --to <fmt> |
| Parallel jobs | maxConcurrentJobs (1–8) |
-j / --workers on batch (1–8) |
| Re-encode / shrink media (same container) | Compress tab | rupant compress <file> |
| Output folder strategies | Settings + per-run | --out-strategy, --out-dir, --last-used-dir, --naming |
| Explicit output file | Per job in UI | -o / --output |
| Validate readable input (no write) | Per-file validate in UI | --validate on convert, batch, compress |
| Per-job progress | Progress bars + history | Log lines only |
| Pause queue | Yes (between jobs) | Not applicable |
| Conversion history | Persisted in app data | Not applicable |
| System tray / notifications | Optional | Not applicable |
| Dependency diagnostics | Settings → Tools & diagnostics | Install tools; errors at run time |
| Browse supported formats | Format picker | rupant formats |
| Video HW encoder preference | Per setting (nvenc, …) |
--video-hw-encoder |
| Max video bitrate (compress) | UI field | --max-video-bitrate |
| Windows context menu / Send to | Optional at install (wails build -nsis) or Settings → Files (per-user, no admin) |
Not applicable |
| Scripting / CI | Not applicable | Natural fit |
# Single file (output next to input by default)
rupant convert photo.png --to jpg
# Explicit output path (overrides folder strategy)
rupant convert doc.md --to pdf -o out/paper.pdf
# Dry-run: verify the backend can read the input (no output file)
rupant convert clip.mp4 --to mp3 --validate
# Batch (parallelism via worker pool)
rupant batch "*.wav" --to mp3 --workers 4Shared / path behaviour (same semantics as the desktop app when you do not pass -o):
| Flag | Purpose |
|---|---|
--out-strategy |
same_as_input (default), desktop_rupant, last_used, custom |
--out-dir |
Required for custom; folder for converted files |
--last-used-dir |
Folder when using last_used (falls back if empty) |
--naming |
On clash: suffix, timestamp, overwrite |
--batch-paths |
Batch only: flatten or preserve (relative paths under a common root; needs absolute paths) |
--validate |
Check readability only; no conversion |
--workers / -j |
Max concurrent conversions in batch (1–8, default 3) |
--quality |
low / medium / high (where the adapter supports it) |
Global flags apply to subcommands that define them; run rupant convert -h / rupant batch -h for the full list.
# CLI
go run cmd/rupant/main.go convert test.pdf --to docx
# Desktop
cd desktop && wails dev
# Desktop release build
cd desktop && wails build
# Windows installer (NSIS). Requires makensis on PATH.
# Optional component: "Windows shortcut menus" adds * shell → "Convert with Rupant" and a Send to → Rupant shortcut (off by default).
cd desktop && wails build -nsisfunc (e *Engine) Process(ctx context.Context, job Job) (OperationResult, error)type Converter interface {
CanConvert(format string) bool
Convert(ctx context.Context, job Job) OperationResult
GetSupportedFormats() []string
}CLI/UI layers parse input and call Process; they do not embed conversion logic.
- Core: Go 1.22+
- CLI: Cobra
- Desktop: Wails + Svelte + Tailwind
- Tools: ImageMagick, FFmpeg, Pandoc
- Install Go, Node.js, Wails CLI, and the external tools above (put
magick/convert,ffmpeg,pandoconPATHwhere applicable). - For Markdown → PDF, install a PDF engine. Typst is the lightest option:
- Windows:
winget install Typst.Typst(or rundesktop/scripts/fetch-typst.ps1) - macOS:
brew install typst - Linux:
cargo install typst-clior your distro's package
- Windows:
- Build CLI:
go build -o bin/rupant cmd/rupant/main.go - Desktop:
cd desktop && npm install && wails dev
go test ./core/... ./adapters/... ./pkg/...
./bin/rupant convert test.pdf --to docxKeep CLI/UI thin; route everything through the core engine and adapters. Stay local-first.
MIT License — see LICENSE for details.