Documentation

Siegu docs

Guides, sync & sharing, configuration, and security — straight from the open-source project, updated with every release.

Getting started

About Siegu

Siegu (pronounced see-goo) is a privacy-first, local-only media manager for your photo and video library. It scans the folders you choose, builds a searchable library entirely on your machine, and never uploads your media anywhere. Every AI feature runs on-device via ONNX Runtime — no cloud, no accounts, no telemetry.

What Siegu can do

  • Organize — Automatic scanning, EXIF metadata extraction, thumbnails, video indexing, and a map view with heatmaps.
  • Semantic search — Describe a photo in your own words ("sunset at the beach") and CLIP finds it. Text search also matches file names, recognized text, object tags, captions, and locations.
  • Discover — The search dropdown shows rails at a glance: Best shots, Favorites, Recent, People, Locations, Tags, Papers & screenshots, Cameras, and Months, plus one-tap filters (Favorites, Videos, Faces, Papers, NSFW, Surprise me).
  • People — Automatic face detection and grouping. Name people, merge duplicates, and jump to every photo of a person.
  • Analyze — On-device models enrich each photo with captions (BLIP), objects (YOLO), recognized text (OCR), aesthetics scores, depth maps (MiDaS), and audio transcription for videos (Whisper).
  • Sync — Encrypted peer-to-peer sync between your own devices over the network (WebRTC), with mesh networking via LAN discovery or 6-word mnemonic phrases.

Privacy model

Everything happens on your computer. There are no accounts, no analytics, no cloud uploads. AI models are downloaded once from public sources (e.g. HuggingFace) and then run locally forever after. See docs/security.md for details.

Device Support

Siegu is verified against the platforms below. Which device you use also decides how the on-device AI models are accelerated (via ONNX Runtime):

PlatformDevicesAI acceleration
WindowsAny x64 Windows 10/11 PC or laptopDirectML (NVIDIA/AMD/Intel GPU)
Windows on ARMSnapdragon X / Arm64 laptopsCPU
macOSApple Silicon Macs (M1–M4: MacBook Air/Pro, iMac, Mac mini, Mac Studio, Mac Pro)CoreML (GPU + Neural Engine)
Linuxx86_64 desktops/laptops (Ubuntu, Debian, Fedora, Arch, …)CPU
Linux ARM64Raspberry Pi 4/5, ARM single-board computersCPU
Androidarm64 (arm64-v8a) phones and tablets, ~2017 and newerCPU
iOS / iPadOSiPhone and iPad (arm64, iOS 12+)CPU (CoreML is used on macOS)

Not supported: Intel Macs, 32-bit platforms, and Alpine Linux (musl). Android x86_64 (arm64 emulator images also work) and the iOS Simulator are supported for development; production Android builds target arm64-v8a.

Sync works between any two of the supported devices — e.g. an Android phone and a Windows PC — over your own network.

Mesh synchronization

Siegu syncs your media library between devices over encrypted peer-to-peer connections — no cloud, no accounts.

How it works

┌─────────────┐     WebRTC DTLS      ┌─────────────┐
│   Device A   │ ◄──────────────────► │   Device B   │
│  (Host)      │    encrypted P2P     │  (Joiner)    │
└──────┬───────┘                      └──────┬───────┘
       │                                      │
       └────────── Signaling ────────────────┘
                  (WebSocket)
  1. Host starts a signaling server + mDNS broadcast
  2. Joiner discovers the host (mDNS, QR code, or mnemonic phrase)
  3. Devices exchange WebRTC SDP/ICE via the signaling channel
  4. A direct DTLS-encrypted data channel is established
  5. Devices exchange manifests (lists of photo IDs) and transfer only missing files

Connectivity Methods

LAN (Local Network)

Both devices on the same local network:

  1. Host: Open the Connect panel → tap Host. A QR code and 4-word mnemonic are displayed.
  2. Joiner: Open the Connect panel → tap Join. Devices appear automatically via mDNS. Alternatively, scan the QR code or type the mnemonic.

Remote (WAN)

For devices on different networks:

  1. Deploy the Go signaling server (see below)
  2. Host enters the signaling server URL
  3. Joiner connects using the same room ID

Signaling Server

LAN mode runs a built-in signaling server (embedded in the app). For remote connections, a standalone signaling server (siegu-signal, written in Rust) is available.

Self-hosting

docker compose up -d

The server runs on port 8080 with these env vars:

VariableDefaultDescription
PORT8080Listen address
SIEGU_SIGNAL_TOKEN(unset)If set, every join/room request must include this token

Pre-built image

ghcr.io/denzyldick/siegu-signal:latest

Sync Protocol

Once connected, the sync protocol transfers only what's missing:

  1. Manifest exchange: Both sides share lists of photo IDs and their sync_needed flags
  2. File transfer: Missing files are chunked into 64KB blocks over the WebRTC data channel
  3. Metadata sync: AI results (captions, scores) are propagated as lightweight metadata updates
  4. Storage quota: Configurable via max_storage_mb config key (default: unlimited)

Delta sync

Only photos with sync_needed = 1 are transferred. After a full sync, subsequent connections only transfer new/changed files.

Tips

  • Both devices must have the AI models downloaded to sync AI metadata
  • Storage quota is enforced per-device — configure on each device separately
  • For best performance, keep devices on the same LAN
  • The signaling server never sees your files — only encrypted handshake data

Collection sharing

Siegu allows you to share individual collections (albums) with others in a read-only, view-only mode.

How It Works

You (Host)                           Friend (Guest)
     │                                    │
     │  1. Open a collection              │
     │     → tap "Share" in menu          │
     │     → see sharing info             │
     │                                    │
     │  2. Share via signalling server    │
     │     → connection coordinated       │
     │     → WebRTC data channel opens    │
     │                                    │
     │  3. Friend views in browser        │
     │     → opens share link             │
     │     → sees ONLY your collection    │
     │     → photos stream, not downloaded│
     │                                    │
     │  4. Session ends                   │
     │     → all data cleared             │

Sharing Requirements

Collection sharing requires a signalling server to coordinate the secure WebRTC connection between your device and the viewer's browser.

Hosted Service

The easiest option: siegu.io/connect

  • No setup required
  • Works across networks (not just LAN)
  • Secure, encrypted connections

Self-Hosted

Run your own signalling server. See SIGNALLING.md for setup instructions.

Over the internet / mobile data

Sharing across networks (including guests on cellular data) depends on the two devices finding a path to each other. When the networks block a direct connection (symmetric NAT, carrier-grade NAT on mobile data), a TURN relay carries the encrypted stream. You have two ways to provide one:

  • Free, DIY: run a TURN relay on a machine you own and port-forward UDP/3478 — see NAT Traversal & TURN.
  • Pro: the hosted siegu.io relay includes TURN, so sharing works from any network with zero router configuration.

Desktop App (local, for now)

The desktop app's Share Collection action (… → Share Collection) starts a local signalling + web server and generates a browser share link (http://127.0.0.1:PORT/#CODE.TOKEN.ALBUM). Opening it loads the view-only web client and shows only that collection; granting/copying/stopping happens right in the dialog. For now the link works on the same computer — cross-network sharing goes through a signalling server (see Remote & hosted and the single base-domain switch in src/services/appConfig.ts).

What the Viewer Can See

  • Only the shared collection — the viewer cannot access other photos or collections
  • Read-only — no editing, deleting, or modifying capabilities
  • Streaming only — photos are not downloaded to the viewer's device

What the Viewer Cannot See

  • Other collections or the full library
  • File system paths or device information
  • AI analysis results (unless included in the stream)
  • Any metadata beyond what's in the shared photos

Privacy & Security

Data Flow

  1. Photos stream directly from your device to the viewer via WebRTC
  2. No photos are stored anywhere in between
  3. The signalling server only coordinates the connection (relays encrypted SDP/ICE data)
  4. If no direct path exists, a TURN relay forwards the encrypted stream between the devices — it never stores or reads the content

Session Security

  • Sessions auto-expire after 30 minutes
  • All data is cleared from the viewer's browser when the session ends
  • Blob URLs are revoked to prevent cached access
  • The URL hash is cleared to prevent re-loading

Anti-Crawling

  • robots.txt blocks all search engines
  • Meta tags prevent indexing of shared content
  • No server-side caching of shared photos

Mobile Photo Viewer

When viewing shared photos on mobile:

  • Swipe left/right — navigate between photos
  • Swipe up — jump to previous time period
  • Swipe down — jump to next time period
  • Double-tap — toggle favorite (if connected to your library)

Troubleshooting

"Session ended" immediately

  • The signalling server may not be running
  • Check that the URL is correct (includes CODE.TOKEN in the hash)

Photos not loading

  • Ensure both devices can reach the signalling server
  • Check firewall settings for WebRTC (STUN/TURN ports)
  • Try a different network if on restrictive WiFi
  • On mobile data, a TURN relay is usually required — see NAT Traversal & TURN

Viewer sees "Access denied"

  • The collection may not exist or the link may be expired
  • Ask the host to generate a new share link

Architecture

Host Device (Tauri)
  │
  ├── Embedded web server (serves webclient/dist/)
  ├── Embedded signalling server (LAN mode)
  │
  └── WebRTC data channel
        │
        └── Guest Browser
              ├── webclient/index.html
              ├── WebRTC peer connection
              └── Gallery + Preview UI

Web client (view-only)

The web client is a standalone, zero-dependency browser app that lets someone view photos shared from a Siegu host — without installing anything.

How It Works

Host (Tauri app)                    Guest (Browser)
     │                                    │
     │  1. Host runs `siegu-cli web`          │
     │     → starts signalling server     │
     │     → starts embedded web server   │
     │     → prints share URL             │
     │                                    │
     │  2. Guest opens URL ───────────────│
     │     → WebSocket to /ws             │
     │     → WebRTC offer/answer          │
     │     → Data channel established     │
     │                                    │
     │  3. Guest browses gallery          │
     │     ← thumbnails stream via DC     │
     │     ← full-res on demand           │
     │                                    │
     │  4. Session ends                   │
     │     → all blob URLs revoked        │
     │     → all data cleared from memory │
     │     → URL hash wiped               │

Running Locally

Prerequisites

  • Node.js 18+
  • A running Siegu host (Tauri app with siegu-cli web active)

Development Server

cd webclient
npm install
npm run dev
# → http://localhost:5173

The dev server proxies WebSocket connections to the host's signalling server. Open the share URL printed by siegu-cli web in your browser.

Build for Production

cd webclient
npm run build
# → dist/

The dist/ folder is served by the host's embedded web server when running siegu-cli web.

Docker (Quick Test)

Test everything with one command:

docker compose up
# → http://localhost:8080

This starts both the signalling server and web client on port 8080. Nginx serves the web client and proxies WebSocket connections to the internal signalling server.

URL Format

The share URL contains all connection info in the hash fragment:

http://HOST:PORT/#CODE.TOKEN
http://HOST:PORT/#CODE.TOKEN.ALBUM_ID
  • CODE: Room code for the signalling server
  • TOKEN: Authentication token
  • ALBUM_ID (optional): If present, the viewer enters album-scoped mode (can only see photos in that album)

Security Features

No Persistence

  • Photos stream via WebRTC data channel — nothing is downloaded to disk
  • Blob URLs are revoked when the preview dialog closes
  • All blob URLs are revoked when the session ends

Session Timeout

  • Sessions auto-expire after 30 minutes
  • A countdown timer is displayed in the header
  • When expired, all data is wiped and the page shows "Session ended"

Tab Visibility

  • If the browser tab is hidden for 5 minutes, the session is destroyed
  • Prevents abandoned sessions from staying active

Anti-Crawling

  • robots.txt blocks all crawlers: User-agent: * Disallow: /
  • Meta tags: noindex, nofollow, noarchive, nosnippet, noimageindex
  • Referrer-Policy: no-referrer

Destructor

On session end (disconnect, timeout, page close), the web client:

  1. Revokes all blob URLs (URL.revokeObjectURL)
  2. Clears all in-memory caches
  3. Closes WebRTC peer connection and data channel
  4. Wipes the gallery DOM
  5. Replaces the URL hash (prevents re-loading the session link)

Mobile Gestures

The web client is designed for mobile browsers:

  • Tap a thumbnail to open the full-res preview
  • Swipe left/right in the preview to navigate between photos
  • Close the preview with the × button

Keyboard Shortcuts

None — the web client is intentionally minimal.

Architecture

FilePurpose
index.htmlEntry point, minimal HTML shell
src/main.tsAll logic: signalling, WebRTC, gallery rendering, preview
src/lib.tsPure utilities: parseHash, inferMime, assembleChunks
src/style.cssDark theme, grid layout, responsive design
public/robots.txtBlocks search engine crawlers

Dependencies

Zero runtime dependencies. The web client is vanilla TypeScript bundled with Vite.

Configuration

Config is stored in the app's SQLite database (siegu.db) as key-value pairs.

Config file location

PlatformPath
Linux~/.config/io.denzyl.siegu/siegu.db
macOS~/Library/Application Support/io.denzyl.siegu/siegu.db
Windows%APPDATA%\io.denzyl.siegu\siegu.db
Android/data/data/io.denzyl.siegu/files/siegu.db
iOS~/Library/Application Support/io.denzyl.siegu/siegu.db

Override with --config-dir flag on CLI commands.

All Config Keys

KeyTypeDefaultDescription
themestringsystemlight, dark, or system
languagestringenUI language code
scan_threadsinteger4Parallel scan threads (1–32)
indexing_modestringimmediateimmediate, idle, or manual
tierstringfreeFeature tier: free or paid
sync_pathstringCustom sync download directory
auto_scanstringEnable automatic scanning (true/false)
sync_enabledstringEnable mesh sync (true/false)
max_storage_mbinteger0 (unlimited)Max storage for synced files (1–1,000,000)
model_enabled_clipstringEnable CLIP model (true/false)
model_enabled_facestringEnable face detection, recognition and grouping
model_enabled_ocrstringEnable OCR
model_enabled_nsfwstringEnable NSFW detection
model_enabled_aestheticsstringEnable aesthetics scoring
model_enabled_yolostringEnable object detection
model_enabled_blipstringEnable image captioning
model_enabled_arcfacestringLegacy alias of model_enabled_face (kept in sync by the app)
model_enabled_midasstringEnable depth estimation
model_enabled_whisperstringEnable audio transcription
model_enabled_samstringEnable SAM segmentation
model_enabled_superresstringEnable super-resolution
last_scan_completedstringTimestamp of last scan (read-only)

CLI Usage

# View all config
siegu-cli config get

# Get a specific key
siegu-cli config get-key theme

# Set a value
siegu-cli config set theme dark

# List all valid keys
siegu-cli config keys

Config key validation: keys are whitelisted, values have type/range checking. Invalid values are rejected with a descriptive error.

Network / TURN (host environment variables)

The host reads these from its environment when creating WebRTC connections, so guests behind restrictive networks (mobile data, carrier-grade NAT) can still connect through a relay. See NAT Traversal & TURN for the full setup.

VariableDefaultDescription
SIEGU_TURN_URLSNoneComma-separated TURN URLs, e.g. turn:home.example.com:3478
SIEGU_TURN_USERNAMENoneTURN username (only needed if the relay has auth)
SIEGU_TURN_CREDENTIALNoneTURN password/credential

Security & privacy

Core Principles

  • Local-First AI: All 9 AI models run on-device via ONNX Runtime. No data leaves your computer.
  • Zero Telemetry: No analytics, crash reporting, or usage tracking.
  • No Accounts: No user accounts, no cloud sync, no sign-up.

Data Protection

Model Downloads

  • Model files are downloaded from HuggingFace and other sources over HTTPS
  • SHA-256 hash verification ensures file integrity
  • Files with mismatched hashes are deleted automatically

Peer-to-Peer Sync

  • DTLS Encryption: WebRTC data channels are encrypted with DTLS in transit
  • Zero-Knowledge Signaling: The signaling server relays only encrypted SDP/ICE data — it never sees files, metadata, or manifests
  • No Persistent Server: LAN mode uses a local signaling server; remote mode uses a minimal relay that has no access to content

File Handling

  • Filename Sanitization: Synced filenames are sanitized to prevent path traversal and control character injection
  • Temp Landing: Received files land in sync_temp/ first, are verified, then moved to the library
  • Storage Quota: Configurable limit prevents disk exhaustion

Application Security

Graceful Shutdown

ShutdownCoordinator signals all background tasks (scan, sync, ML) before exit, preventing data corruption.

Transaction Safety

  • All ML batch writes use BEGIN / COMMIT / ROLLBACK
  • Periodically runs VACUUM for database maintenance

Config Validation

  • All 22 config keys are whitelisted (unknown keys rejected)
  • Values are type-checked with range constraints (e.g., scan_threads: 1–32)
  • Invalid values are rejected with a descriptive error

Scan Deduplication

  • ScanGuard prevents concurrent scan operations
  • Each scan skips paths already in the database

Vulnerability Reporting

Report security issues to the project maintainer via GitHub Issues or direct contact. Do not file public issues for critical vulnerabilities.

Command line (siegu-cli)

The siegu-cli binary provides headless access to all core features.

Install

cargo install --path crates/siegu-cli

Global Flags

FlagDescription
--config-dir <path>Override config directory (default: OS-appropriate path)

Commands

siegu-cli scan

Scan directories for media files and import into the library.

# Scan a specific folder (and add it to watched directories)
siegu-cli scan /path/to/photos

# Scan all configured watched directories
siegu-cli scan

siegu-cli analyze

Run ML analysis on photos.

# Analyze all unprocessed photos
siegu-cli analyze all

# Analyze a single photo by ID
siegu-cli analyze photo <photo-id>

# Run a specific model on all photos
siegu-cli analyze model <model-id>

Model IDs: clip, face, ocr, nsfw, aesthetics, yolo, blip, midas, whisper (ultraface/arcface are accepted as aliases of face)

All three forms accept --headless, which prints progress lines and an E2E summary instead of showing the interactive TUI (used by CI scripts).

siegu-cli models

Manage AI model files.

# List all models with download status
siegu-cli models list

# Download all models
siegu-cli models download

# Download specific models
siegu-cli models download clip face nsfw

# Show disk usage per model
siegu-cli models usage

siegu-cli config

Manage configuration.

# View all config
siegu-cli config get

# Get a specific key
siegu-cli config get-key theme

# Set a config value
siegu-cli config set theme dark

# List valid config keys
siegu-cli config keys

siegu-cli mesh

Peer-to-peer mesh synchronization.

# Host a LAN sync session (starts a signaling server + mDNS)
siegu-cli mesh host

# Host on a specific port (default 0 = pick a free port)
siegu-cli mesh host --port 9090

# Host against an existing signaling server, joining/creating a room
siegu-cli mesh host --server ws://192.168.1.100:8080 --room myroom

# Join a mesh room (room ID is positional; URL goes in --server)
siegu-cli mesh join myroom
siegu-cli mesh join myroom --server ws://192.168.1.100:8080

# When joining a --server host with a pre-agreed room, create the WebRTC offer
siegu-cli mesh join myroom --server ws://192.168.1.100:8080 --initiator

# Show session status / disconnect / storage quota usage
siegu-cli mesh status
siegu-cli mesh disconnect
siegu-cli mesh quota

# Browse a peer's library view-only and verify the manifest (#9, e2e helper)
siegu-cli mesh browse myroom --server ws://192.168.1.100:8080

# Browse in album-share mode for a specific album (#16)
siegu-cli mesh browse myroom --album <album-id>

# Send a single RPC command to the peer and print its reply (#19, e2e helper)
siegu-cli mesh rpc myroom list_files --server ws://192.168.1.100:8080
siegu-cli mesh rpc myroom toggle_favorite '{"id": "<photo-id>"}'

# Seed a manual album with the first N photos (prints ALBUM ID, e2e helper)
siegu-cli mesh seed-album --name "Shared" --take-first 5

Availability and defaults:

  • host--server connects to an existing signaling server instead of starting a local one; --room names the room (required with --server). --share-mode &lt;ro|rw&gt; sets the permission level for connected peers (default ro): ro allows browsing only, rw also allows favorites/trash mutations.
  • join--initiator makes this peer create the WebRTC offer (needed when joining a --server host).
  • browse, rpc, and seed-album are e2e test helpers; they print greppable VIEWONLY, RPC RESULT, and ALBUM ID markers used by scripts/e2e-view-only.sh.
  • All mesh subcommands accept -c/--config to point at a config directory.

Remote sync via mesh

There is no separate siegu-cli sync command. Remote (non-LAN) sync is done with the mesh commands above — host on one machine, join on the other — using a signaling server URL in --server.

siegu-cli serve

Start a standalone LAN signaling server.

siegu-cli serve --port 8080

siegu-cli web

Share this machine's library as a browser gallery (#11, #19). Starts an embedded signaling server plus a small static web server and prints a one-off link. Opening the link is the consent step — anyone holding it can browse the library until the command stops. Nothing is downloaded or written on the viewing device; media streams over the WebRTC data channel on demand.

siegu-cli web
# Open in a browser on this machine:
#   http://127.0.0.1:8787/#<code>.<token>
# Or from another device on this network:
#   http://192.168.1.45:8787/#<code>.<token>

Flags: --port for the static client port (default 8787), --config as usual, and --share-mode &lt;ro|rw&gt; (default ro). The default ro is view-only — guests can browse but not change anything. rw additionally lets guests toggle favorites and trash photos. The browser webclient itself is read-only; the rw mode is consumed by the mesh browse/rpc test helpers.

The web bundle lives in webclient/; build it once with cd webclient &amp;&amp; bun install &amp;&amp; bun run build (npm also works), or point SIEGU_WEB_DIST at a built dist/ directory.

siegu-cli status

Show app overview.

siegu-cli status

Output includes: config directory, database status, photo/video counts, watched folders, config values, model disk usage, available memory.

Looking for the full developer documentation? Browse the GitHub docs.