{"id":47603023,"url":"https://github.com/paperkite-hq/stork","last_synced_at":"2026-04-06T10:03:01.598Z","repository":{"id":345602567,"uuid":"1186615834","full_name":"paperkite-hq/stork","owner":"paperkite-hq","description":"Self-hosted email client with AES-256 encryption at rest, IMAP sync, and full-text search","archived":false,"fork":false,"pushed_at":"2026-03-28T04:55:30.000Z","size":3915,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-28T05:48:38.974Z","etag":null,"topics":["docker","email","email-client","encryption","imap","privacy","self-hosted","sqlite","typescript","webmail"],"latest_commit_sha":null,"homepage":"https://stork-demo.paperkite.sh","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paperkite-hq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-19T20:15:29.000Z","updated_at":"2026-03-28T04:55:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/paperkite-hq/stork","commit_stats":null,"previous_names":["paperkite-hq/stork"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/paperkite-hq/stork","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fstork","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fstork/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fstork/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fstork/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paperkite-hq","download_url":"https://codeload.github.com/paperkite-hq/stork/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fstork/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","email","email-client","encryption","imap","privacy","self-hosted","sqlite","typescript","webmail"],"created_at":"2026-04-01T18:58:16.025Z","updated_at":"2026-04-06T10:03:01.592Z","avatar_url":"https://github.com/paperkite-hq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/logo.svg\" alt=\"Stork\" width=\"128\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eStork\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/paperkite-hq/stork/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/paperkite-hq/stork/actions/workflows/ci.yml/badge.svg?branch=main\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://paperkite-hq.github.io/stork/\"\u003e\u003cimg src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/paperkite-hq/stork/badges/coverage.json\" alt=\"Coverage\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-AGPL--3.0-blue.svg\" alt=\"License: AGPL-3.0\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n**Self-hosted email client with encrypted local storage, IMAP sync, and full-text search.**\n\n\u003e Self-host the client, not the edge.\n\n**[Try the read-only demo →](https://stork-demo.paperkite.sh)** — no install needed, sample data pre-loaded.\n\n![Stork inbox](docs/screenshots/inbox.png)\n\nStork syncs your email from any IMAP server, stores it locally with **AES-256 encryption at rest**, full-text search, and a modern web interface. Keep using your existing mail server for sending and receiving — Stork handles storage, search, and the UI.\n\n- **Encryption at rest** — AES-256 via SQLCipher. Container boots locked; your password unlocks it.\n- **Sync from any IMAP server** — Mailcow, Dovecot, Fastmail, whatever you've got\n- **Full-text search** — FTS5-powered search across your entire mailbox, instantly\n- **Compose \u0026 send** — reply, reply-all, forward, and compose via your SMTP server\n- **Labels, not folders** — Gmail-style labels replace rigid folder hierarchies ([why?](docs/design-decisions.md))\n- **Multi-identity** — connect multiple email identities; unified inbox shows all messages in one view\n- **Mirror \u0026 Connector modes** — test the waters with your provider as backup, then flip to Connector mode when you're ready to commit\n- **Desktop notifications** — new mail alerts as messages arrive\n- **Recovery key** — 24-word BIP39 mnemonic so a forgotten password doesn't mean lost mail\n- **Single container** — `docker compose up` and you're running. No PHP, no external DB.\n\n## Quick Start\n\n```yaml\n# docker-compose.yml\nservices:\n  stork:\n    image: ghcr.io/paperkite-hq/stork:latest\n    init: true\n    ports:\n      - \"127.0.0.1:3100:3100\"\n    volumes:\n      - stork-data:/app/data\n    restart: unless-stopped\n    mem_swappiness: 0\n    ulimits:\n      core: 0\n    security_opt:\n      - no-new-privileges:true\n\nvolumes:\n  stork-data:\n```\n\n```bash\ndocker compose up -d\n# Open http://localhost:3100\n```\n\nThe setup wizard will guide you through creating a password and connecting your email. See the [Getting Started guide](docs/getting-started.md) for a full walkthrough.\n\n\u003cdetails\u003e\n\u003csummary\u003eDocker run (single command)\u003c/summary\u003e\n\n```bash\ndocker run -d --init \\\n  -p 127.0.0.1:3100:3100 \\\n  -v ~/stork-data:/app/data \\\n  --memory-swappiness=0 \\\n  --ulimit core=0 \\\n  --security-opt no-new-privileges \\\n  --restart unless-stopped \\\n  ghcr.io/paperkite-hq/stork:latest\n```\n\nBinds to localhost only, stores the encrypted database in `~/stork-data`, disables swap/core dumps/privilege escalation.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild from source\u003c/summary\u003e\n\n```bash\ngit clone https://github.com/paperkite-hq/stork.git\ncd stork\ndocker compose up --build\n```\n\u003c/details\u003e\n\n## Screenshots\n\n**Threaded conversations** — messages grouped by thread with expand/collapse, reply, reply-all, and forward:\n\n![Thread view](docs/screenshots/thread.png)\n\n**Compose** — clean compose form with keyboard shortcut to send:\n\n![Compose form](docs/screenshots/compose.png)\n\n## Connector Mode — Your Email, Permanently Yours\n\nMost email clients treat your mail provider as the source of truth. Stork inverts this: **your provider is the delivery edge; Stork is your permanent encrypted archive.**\n\nThe idea is simple. Your inbox is full of sensitive history — receipts, contracts, conversations — and right now it lives on someone else's server indefinitely. Stork lets you take it back.\n\n**Mirror mode** (default): Stork syncs a local encrypted copy while leaving your provider's mailbox intact. Use this while you're evaluating — your provider stays your safety net, and you can fall back to its interface at any time.\n\n**Connector mode**: Once you're confident Stork is right for you, flip the switch. New messages are fetched and deleted from your provider in interleaved batches as they arrive — 100 at a time, so your server starts clearing immediately rather than in one big sweep at the end. Your provider becomes just the delivery pipe — a connector that feeds mail into Stork; Stork holds the only copy, AES-256 encrypted, on your own hardware.\n\n```\n         Mirror mode                    Connector mode\n  ┌──────────────────────┐          ┌──────────────────────┐\n  │  Mail provider       │          │  Mail provider       │\n  │  (holds all mail)    │          │  (transient — 1 hop) │\n  └──────────┬───────────┘          └──────────┬───────────┘\n             │ IMAP sync                        │ IMAP sync + delete\n             ▼                                  ▼\n  ┌──────────────────────┐          ┌──────────────────────┐\n  │  Stork               │          │  Stork               │\n  │  (encrypted copy)    │          │  (only copy, AES-256)│\n  └──────────────────────┘          └──────────────────────┘\n```\n\nThe UI walks you through this choice when you connect your first email, and surfaces an ambient reminder while any identity is still in mirror mode. The [User Guide](docs/user-guide.md) covers both modes in detail. The connector architecture supports any mail source — not just IMAP — so connector mode generalizes naturally as new connectors are added.\n\n## How Stork Compares\n\n| | Stork | Roundcube | Bichon | Mailu |\n|---|---|---|---|---|\n| **What it is** | Email client | Webmail client | Email archiver | Full mail server |\n| **Deployment** | Docker (single container) | PHP + web server + DB | Docker / binary | Docker (multi-container) |\n| **Encryption at rest** | ✅ AES-256 SQLCipher | ❌ | ❌ | ❌ |\n| **Local storage** | ✅ SQLite | ✅ MySQL/PostgreSQL | ✅ EML files + Tantivy | ✅ (full server) |\n| **Full-text search** | ✅ FTS5 (fast, indexed) | ⚠️ basic | ✅ Tantivy | ✅ Solr |\n| **Web UI** | ✅ React | ✅ jQuery | ✅ React | ✅ (Roundcube/Rainloop) |\n| **Label-based org** | ✅ | ❌ (folders only) | ❌ | ❌ (folders only) |\n| **Compose/send** | ✅ SMTP | ✅ | ❌ (read-only) | ✅ (full MTA) |\n| **Recovery key** | ✅ BIP39 mnemonic | ❌ | ❌ | ❌ |\n| **Self-contained** | ✅ (no PHP, no extra DB) | ❌ | ✅ | ❌ (many services) |\n\n**Roundcube** is the most mature option with the deepest plugin ecosystem — better for calendar integration or multi-user shared hosting. **Bichon** is focused on email archiving — better for long-term preservation of large mailboxes. **Mailu** is a complete mail server stack — use it if you need to replace your mail infrastructure entirely. Stork is a client that connects to your existing server.\n\n## Roadmap\n\n- [x] IMAP sync engine (incremental, resumable)\n- [x] SQLite storage with FTS5 full-text search\n- [x] SMTP sending via configured server\n- [x] Web UI — inbox, threads, compose, search\n- [x] Docker single-container deployment\n- [x] Label-based organization (Gmail-style)\n- [x] Encryption at rest (AES-256 via SQLCipher, BIP39 recovery key)\n- [x] Pluggable connector architecture\n- [x] Delete-from-server workflow (connector mode)\n- [x] Multi-identity support (unified inbox)\n- [x] Connector mode transition wizard (clean server, re-label from server)\n- [x] zlib compression for large email bodies and attachments\n- [x] HTML text extraction for full-text search on HTML-only emails\n\n### Planned\n\n- [ ] Filter rules and auto-labeling (server-side, applied on sync)\n- [ ] JMAP connector (pluggable alternative to IMAP)\n- [ ] CalDAV calendar integration (read-only events from email invites)\n- [ ] Attachment deduplication\n\n## Documentation\n\n- **[Getting Started](docs/getting-started.md)** — first launch, encryption setup, adding an email identity\n- **[User Guide](docs/user-guide.md)** — search tips, backups, reverse proxy\n- **[Use Cases](docs/use-cases.md)** — encrypted Gmail backup, Mailcow replacement, VPN access\n- **[Configuration](docs/configuration.md)** — environment variables, Docker options\n- **[API Reference](docs/api.md)** — REST API documentation\n- **[Architecture](docs/architecture.md)** — system design and codebase walkthrough\n- **[Design Decisions](docs/design-decisions.md)** — labels over folders, and why\n- **[Upgrading](docs/upgrading.md)** — migrations, backups, and version upgrades\n- **[FAQ](docs/faq.md)** — common questions about sync, search, and data safety\n- **[Changelog](CHANGELOG.md)** — release history and what's new\n- **[Contributing](CONTRIBUTING.md)** — development setup, running tests\n\n## Development\n\n```bash\nnpm install\nnpm run dev    # Start dev server\nnpm test       # Run tests\nnpm run lint   # Lint\n```\n\n## Tech Stack\n\n[Node.js](https://nodejs.org) 22+ · [Hono](https://hono.dev) · SQLite/[SQLCipher](https://www.zetetic.net/sqlcipher/) · [FTS5](https://www.sqlite.org/fts5.html) · [ImapFlow](https://github.com/postalsys/imapflow) · [Nodemailer](https://nodemailer.com) · React · Tailwind CSS · Vite\n\n## License\n\nAGPL-3.0 — see [LICENSE](LICENSE) for details. [Dual licensing](LICENSING.md) is available for commercial use.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperkite-hq%2Fstork","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaperkite-hq%2Fstork","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperkite-hq%2Fstork/lists"}