[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-95872":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":15,"stars7d":15,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":15,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":25,"readmeContent":26,"aiSummary":27,"trendingCount":15,"starSnapshotCount":15,"syncStatus":28,"lastSyncTime":29,"discoverSource":30},95872,"superlocal","R44VC0RP\u002Fsuperlocal","R44VC0RP","An email client & provider gateway so you can build your best email experience. ","https:\u002F\u002Fsuper.local",null,"TypeScript",207,9,1,0,46,3,false,"main",true,[22,23,24],"email","fast","local-first","2026-09-21 02:04:28","# Superlocal\n\nDesktop-focused email, backed by a provider-agnostic Inbox SDK. Bring your mailboxes into one unified inbox while keeping their identities, credentials, and provider capabilities separate.\n\n![Unified inbox showing the included fictional mailboxes](docs\u002Fscreenshots\u002Funified-inbox.png)\n\n## Run locally\n\nRequires **Bun 1.4+**.\n\n1. Install dependencies:\n   ```sh\n   bun --no-env-file install\n   ```\n2. Start the app:\n   ```sh\n    bun --no-env-file run start\n   ```\n3. Open **http:\u002F\u002Flocalhost:5178**.\n\nThe first run starts two fictional mailboxes through the real Inbox SDK. No provider credentials, OAuth setup, OpenCan, or machine-specific services are needed. **Ctrl-C stops both the client and local host.**\n\n`start` builds and serves the optimized client locally. Use `bun --no-env-file run dev` for hot-reloading development; development-mode React diagnostics add overhead on large mailboxes. Both commands keep the same local-only host, sessions, and provider configuration.\n\n## Docker with persistent storage\n\nRun `docker compose up -d --build --wait`, then open **http:\u002F\u002Flocalhost:5178**.\nThe image contains the built app, not your configuration or mail. On first run,\nthe app creates a fictional installation in the named volume **`superlocal-state`**:\n\n```text\n\u002Fpersist\u002Fsuperlocal.local.json\n\u002Fpersist\u002Fdata\u002Fmock\u002F             # Fictional mail, databases and generated keys\n\u002Fpersist\u002Fdata\u002Freal\u002F             # Created when real mode is selected\n```\n\nEach mode keeps `host.sqlite`, its mail database(s), `runtime-secrets.json` and\nSQLite journals together. Configure real providers in the retained config as\ndescribed below; **never replace its instance ID or keys during an update**.\nGoogle OAuth client secrets can be supplied through the same explicit environment\nvariables used locally. They are runtime inputs, not image build arguments.\n\nAfter updating the checkout, run `docker compose up -d --build --wait` again.\nCompose replaces the app container and reattaches the same volume. A hosting\nplatform's Git-triggered redeploy must likewise retain this volume at `\u002Fpersist`;\nthe GitHub workflow below publishes images but does not restart a remote host.\n`docker compose down` retains the volume; **do not use `down -v` or delete the\nvolume when updating**. Back up config, databases and keys together with the app\nstopped. Existing Mac installations are not imported automatically.\n\nSet `SUPERLOCAL_DOCKER_PORT` to use another local port and `SUPERLOCAL_VOLUME_NAME`\nonly for a deliberately separate installation. Do not run two app instances on\nthe same volume. Named volumes are initialized for the image's non-root `bun`\nuser; a bind-mount replacement must be owned by that user's UID\u002FGID (1000:1000),\nwith private directories and `0600` config\u002Fkey files.\n\nOnly the web port is published, on host loopback. The backend stays inside the\ncontainer. Browser-local settings\nand recovery copies remain in the browser; this volume preserves server state.\n\n### Published images\n\nPushes to `main` build and publish **Linux AMD64 and ARM64** images at\n`ghcr.io\u002Fr44vc0rp\u002Fsuperlocal:latest`. The workflow can also be run manually on\n`main`. It uses GitHub's built-in `GITHUB_TOKEN` with package-write permission;\nno Docker Hub account or registry password is needed. Images also receive a\n`sha-\u003Cfull-commit-sha>` tag, and the workflow records the digest for pinned deploys.\n\nTo run or update the published image without building it locally:\n\n```sh\nSUPERLOCAL_IMAGE=ghcr.io\u002Fr44vc0rp\u002Fsuperlocal:latest \\\n  docker compose up -d --no-build --pull always --wait\n```\n\nThe same `superlocal-state` volume is retained. Set `SUPERLOCAL_IMAGE` to a\ncommit-specific tag or digest to select a particular version; database migrations\nmay still limit downgrades. Other container hosts can use the same image and mount\n`\u002Fpersist` without Compose. Server restarts\u002Fwebhooks remain host-specific.\n\nGHCR packages initially default to private, even for a public repository. The\npackage owner must set its visibility to public once for anonymous pulls. A\nprivate package instead requires a GitHub token with `read:packages` on the\ndeployment host. This workflow never includes runtime mail, keys or config in\nthe image.\n\n### Restricted Google access\n\nGoogle is the application login provider for restricted installations. It reuses\nthe same Google OAuth client ID and secret configured for Gmail, even when the\nGmail mailbox provider is disabled. Existing local\u002Fdemo installations retain\ntheir explicit `loopback` mode; adding Google credentials alone does not change\ntheir access policy.\n\nTo enable the gate, set these runtime values (or set `auth.method`,\n`auth.allowedEmails` and `web.origin` in the retained configuration):\n\n```sh\nSUPERLOCAL_AUTH_METHOD=google\nSUPERLOCAL_AUTH_ALLOWED_EMAILS=you@example.com,teammate@example.com\nSUPERLOCAL_WEB_ORIGIN=https:\u002F\u002Fmail.example.com\nSUPERLOCAL_GOOGLE_CLIENT_ID=your-google-web-client-id\nSUPERLOCAL_GOOGLE_CLIENT_SECRET=your-google-web-client-secret\n```\n\nFor a real remote mailbox setup, also select `mode: \"real\"` and enable the desired\nproviders in the retained config, as described under **Connect real providers**.\nMock mode remains an offline demo, not a connection to real mail services.\n\nRegister **both** redirect URIs on that Google OAuth web client:\n\n```text\nhttps:\u002F\u002Fmail.example.com\u002Fapi\u002Fauth\u002Fcallback\u002Fgoogle      # Application login\nhttps:\u002F\u002Fmail.example.com\u002Fv1\u002Foauth\u002Fgoogle\u002Fcallback     # Optional Gmail connection\n```\n\nApplication login requests only identity\u002Fprofile scopes and checks Google's\nverified email against the exact allowlist. An empty list denies everyone;\nwildcards and whole-domain entries are not supported. Case and surrounding\nwhitespace normalize, but plus tags and Gmail dots are not rewritten. Restart\nafter changing the list: removed users' existing sessions then fail the access\ncheck. Sign out is available in the sidebar footer.\n\n**Each approved person has a private account on the installation.** Their first\nGoogle sign-in starts with no connected mailboxes. They can connect multiple\nmailboxes across the enabled providers; connections, mail, drafts and settings\nare scoped to that person. There are no shared-mailbox or team roles. Google\nlogin does not connect Gmail or grant mailbox scopes. Login provider tokens are\nnot retained after identity verification, and mailbox credentials stay in the SDK.\n\nBrowser-held preferences, draft recovery and issue reports are also user-scoped.\nSwitching the signed-in person loads a fresh application context; stale tabs and\nrequests cannot operate under the next person's session. Existing unscoped local\nbrowser data and legacy loopback mail are not imported or exposed to Google\nusers. Configure a fresh remote installation and reconnect the desired providers;\nthis feature does not migrate an existing local installation.\n\nBetter Auth stores login identities, durable private-owner bindings and sessions\nin `auth.sqlite` beside the mode's other databases. Its secret is derived from the\nretained runtime session key, so `\u002Fpersist` also preserves login state and user\nownership across redeploys. Missing Google\ncredentials fail closed in Google mode; it never falls back to local access.\nOnly the login shell\u002Fassets, auth flow and health check are public. Mail,\nsettings, attachments, authenticated images and event streams require an\napproved session. Use HTTPS for a public origin; `loopback` mode still rejects\npublic origins and production startup. TLS\u002Freverse-proxy setup remains external.\nLogin starts share a conservative installation-wide limit of 20 per minute;\nforwarded client-IP headers are not trusted. For public exposure, apply per-client\nlimits at the trusted reverse proxy so one caller cannot exhaust that shared\nbudget. Removing an email denies its existing sessions while it is off the list;\nre-adding it can restore those sessions until their absolute expiry or sign-out.\n\n## One inbox, separate mailboxes\n\n- **Unified by default.** Every added mailbox joins the unified view. Choose a smaller selection in **Settings → Mailboxes** when you want one.\n- **Keyboard-first navigation.** **Ctrl+0** opens Unified inbox; **Ctrl+1–9** open your ordered pinned mailboxes.\n- **Many mailboxes, one view.** Search and bulk-select provider mailboxes instead of creating hundreds of permanent tabs. Adding views triggers one initial sync per source, not one per domain.\n- **Source-aware actions.** Overlapping views share one canonical message. Replies retain the correct sender; Done and snooze remain local mailbox workflows rather than upstream labels.\n- **Isolated email rendering.** Received HTML stays in a script-disabled iframe. The SDK sanitizes content and serves eligible remote media through authenticated routes; image settings and known-tracker blocking remain authoritative.\n\n\u003Cdetails>\n\u003Csummary>See the reader and mailbox settings\u003C\u002Fsummary>\n\n### Reading a conversation\n\n![A conversation from the fictional mock inbox](docs\u002Fscreenshots\u002Fconversation.png)\n\n### Choosing mailboxes and shortcuts\n\n![Per-user unified inbox selection and pinned mailbox shortcuts](docs\u002Fscreenshots\u002Fmailbox-settings.png)\n\n\u003C\u002Fdetails>\n\nThe screenshots use only the included fictional mock data, at a slightly enlarged viewing scale.\n\n## Providers and roadmap\n\nThe goal is **ready-to-connect providers out of the box**: choose a service, complete its authorization, and select mailboxes in the same UI. You will still need the provider account, required permissions, and any domain\u002FDNS setup that service requires.\n\n| Provider | Current state | Direction |\n| --- | --- | --- |\n| Mock mailboxes | Included and offline | Keep the full app usable immediately, without personal credentials. |\n| Gmail | Implemented; host OAuth client configuration required | Make authorization, reconnects, and multi-account setup simpler. |\n| [Inbound.new](https:\u002F\u002Finbound.new) | Implemented; API-key onboarding and domain\u002Faddress views | Improve large-domain onboarding, import progress, and recovery within the provider's sync limits. |\n| iCloud \u002F IMAP | In progress | Email + app-specific-password setup for iCloud, secure IMAP\u002FSMTP presets, and end-to-end provider qualification. |\n| [Resend](https:\u002F\u002Fresend.com\u002Fdocs\u002Fdashboard\u002Freceiving\u002Fintroduction) | Planned | Add a sending\u002Freceiving adapter using received-email APIs, attachment retrieval, and verified webhook ingestion. |\n| [Cloudflare Email Service](https:\u002F\u002Fdevelopers.cloudflare.com\u002Femail-service\u002F) | Planned | Bridge Email Routing \u002F Email Workers into durable SDK mail storage, and integrate supported sending APIs or SMTP. |\n\nResend and Cloudflare support email workflows, but that does not automatically give them traditional IMAP folders or native read flags. Their adapters should expose real capabilities while the SDK supplies reusable local workflows.\n\n### What we are working toward\n\n1. **Less setup outside the app.** Provider presets, guided authorization, connection checks, and useful reconnect errors instead of routine configuration-file editing.\n2. **More interchangeable providers.** Finish IMAP\u002FiCloud support, then add Resend and Cloudflare without introducing separate frontend mail APIs.\n3. **Better large-inbox progress.** Keep cached mail usable while imports run, show meaningful progress and partial failures, and extend paging as mailbox collections grow.\n4. **Personalized Important \u002F Other.** Add optional per-user learning from explicit corrections, with reversible feedback and conservative handling of uncertain mail.\n\nFor current unified-inbox decisions, import limits, and open product questions, see [unified-inbox-decisions.md](unified-inbox-decisions.md). Apple's separate OAuth opportunity is recorded in [apple-provider.ind](apple-provider.ind).\n\n## Architecture\n\n```text\nClient → Inbox SDK APIs → provider adapters\n```\n\n| Package | Responsibility |\n| --- | --- |\n| `apps\u002Fweb` | React client: inbox views, reading, composing, and settings. |\n| `apps\u002Flocal-host` | Application sessions, provider onboarding, runtime paths, and connection policy. |\n| `packages\u002Finbox-sdk` | Normalized mail contracts, SQLite storage, encrypted credentials, queries, jobs, drafts, events, and provider translation. |\n| `apps\u002Fmock-api` | A fictional upstream and real `InboxProvider` implementation, not a fake SDK HTTP layer. |\n\nThe SDK currently runs on Bun and SQLite. The host supports explicit local-only sessions or allowlisted Google sign-in with private per-user mailboxes and settings. Shared-mailbox and team roles are not implemented.\n\n## Connect real providers\n\nThe current Gmail and Inbound connectors use the local host configuration:\n\n1. Stop the app and open the generated, git-ignored `superlocal.local.json`.\n2. Set `mode` to `real` and enable `providers.gmail.enabled` and\u002For `providers.inbound.enabled`.\n3. For Gmail, configure the host's Google OAuth web client. Register **`http:\u002F\u002Flocalhost:5178\u002Fv1\u002Foauth\u002Fgoogle\u002Fcallback`** for the default local setup. Google does not accept `.local` redirect domains; use the configured localhost origin for local authorization.\n4. Restart the app and open **Settings → Add Accounts**. Inbound takes an API key, then offers discovered mailboxes. Gmail uses the host-managed OAuth flow.\n5. Use **Settings → Mailboxes** to choose unified inclusion and pinned shortcuts.\n\nProvider credentials are submitted to the host, encrypted per connection by the SDK, and not returned by the mail APIs. Mock and real modes stay separate. The Gmail OAuth defaults reference `SUPERLOCAL_GOOGLE_CLIENT_ID` and `SUPERLOCAL_GOOGLE_CLIENT_SECRET`; export them explicitly, or configure the corresponding `providers.gmail.oauth` values as private strings or `{ \"env\": \"YOUR_VARIABLE_NAME\" }` references. No SDK `.env` file or unrelated ambient credentials are imported.\n\nReal connections default to normal mail access (`allowProviderWrites.real: true`). For an optional read-only host, set it to `false`; Gmail can use `https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fgmail.readonly` with `openid` and `email` rather than modify\u002Fsend scopes. Reauthorize old read-only grants before sending or modifying mail. Provider capabilities and OAuth permissions still determine which native actions are available.\n\n\u003Cdetails>\n\u003Csummary>Runtime storage and advanced local configuration\u003C\u002Fsummary>\n\nKeep the generated `instanceId` with its data. Runtime databases, encryption keys, and session keys live **outside the checkout**:\n\n| Platform | Default location |\n| --- | --- |\n| macOS | `~\u002FLibrary\u002FApplication Support\u002Fsuperlocal\u002F\u003CinstanceId>\u002F` |\n| Linux | `$XDG_DATA_HOME\u002Fsuperlocal\u002F\u003CinstanceId>\u002F`, defaulting to `~\u002F.local\u002Fshare` |\n| Windows | `%LOCALAPPDATA%\u002Fsuperlocal\u002F\u003CinstanceId>\u002F` |\n\n`dataDir` can point to another private directory. Back up the whole instance together: missing or mismatched keys fail closed and are never silently regenerated. Old pilot databases and `.env.local` credentials are not imported automatically.\n\nThe launcher serves the client on port **5178** and the SDK host on **8790**. `web.port`, `backend.port`, `web.origin`, and `web.allowedOrigins` control the local addresses. A null `web.origin` uses `http:\u002F\u002Flocalhost:\u003Cweb.port>`. An existing OpenCan setup can expose `https:\u002F\u002Fsuper.local`; Superlocal does not install or manage OpenCan.\n\nFor an isolated run, set `SUPERLOCAL_CONFIG` to a new configuration path and `SUPERLOCAL_DATA_DIR` to a separate private directory. The configuration's parent directory must exist. `SUPERLOCAL_WEB_PORT`, `SUPERLOCAL_API_PORT`, and `SUPERLOCAL_WEB_ORIGIN` override addresses. `bun --no-env-file run dev:host` starts only the backend.\n\n\u003C\u002Fdetails>\n\n## Development\n\nFrom the repository root:\n\n```sh\nbun --no-env-file run test:web\nINBOX_TEST_LIVE=false bun --no-env-file run test:api\nbun --no-env-file run typecheck\nbun --no-env-file run build\n```\n\nLive-provider qualification is separate from deterministic tests. Never commit credentials, private configuration, runtime databases, real mail, or private diagnostic captures.\n","Superlocal 是一款面向桌面的本地优先电子邮件客户端，通过 provider-agnostic 的 Inbox SDK 实现多邮箱统一收件箱管理。核心功能包括：支持任意邮件服务商（如 Gmail、Outlook）的独立接入与身份隔离，本地运行、离线可用，所有凭证与数据默认保留在用户设备；提供虚构邮箱开箱即用体验，同时支持真实邮箱配置（OAuth 集成）。技术上基于 TypeScript 构建，依赖 Bun 运行时，采用 SQLite 本地存储，强调隐私性与可控性。适用于注重数据主权、需整合多个工作\u002F个人邮箱但不愿依赖云端邮件聚合服务的技术用户及隐私敏感型办公场景。",2,"2026-09-05 02:30:06","CREATED_QUERY"]