Skip to content

About

SupportOS+ — local-first support desk (Rust/egui): Help Scout sync, knowledge base, semantic search, SLA reports, local AI

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

SupportOS+

Independent Rust/egui desktop reimplementation of supportos. Local-first. No telemetry. No cloud AI.

SupportOS+ is a ground-up rewrite of the SupportOS support-desk platform in pure Rust, trading the original TypeScript/React stack for a single-language workspace that compiles to a fast, self-contained desktop binary. Everything the original does — unified inbox, real-time conversation sync, semantic docs search, SLA reporting — is rebuilt on an embedded, zero-service architecture that never leaves your machine.

Repository: https://github.com/kimpearce888/SupportOS-Plus · Releases: https://github.com/kimpearce888/SupportOS-Plus/releases

Naming layers (applied where each is allowed): SupportOS+ is the display name — window title, desktop menu entry, release title. SupportOS-Plus is the product identifier — repository, window/app ID, artifact base name. supportos-plus is the Debian package name and, since 1.0.0, also the installed command and internal crate identifier. (Pre-1.0.0 builds shipped the command supportos-plusplus; see the upgrade note below.)


Install

Install-ready Linux packages are published on the Releases page, built and verified by CI from the tagged source commit. Both packages carry the same release binary, including the Landlock + seccomp parse sandbox.

Package File Target
.deb supportos-plus_<version>_amd64.deb Ubuntu 22.04 LTS / 24.04 LTS, Debian 12+
.AppImage SupportOS-Plus-<version>-x86_64.AppImage any modern x86_64 desktop (glibc >= 2.35)

System requirements: x86_64 (amd64) Linux with glibc 2.35 or newer, a desktop session (X11 or Wayland) and a GL or Vulkan driver (Mesa software rendering works on GPU-less machines). ~200 MB free disk space. No compilation, no development tools, no Docker, no external services, no manual configuration.

.deb

sudo apt install ./supportos-plus_<version>_amd64.deb

APT downloads and resolves every declared dependency automatically — the package's Depends set covers the full X11/GL/Wayland client-library surface the GUI loads at runtime. Launch SupportOS+ from the desktop application menu, or run supportos-plus.

AppImage

chmod +x SupportOS-Plus-<version>-x86_64.AppImage
./SupportOS-Plus-<version>-x86_64.AppImage

Runs without installation; the type-2 AppImage bundles the binary, icons and desktop entry (AppImageLauncher integrates it into the menu). Only the host display server and GL/Vulkan driver are used from the system.

First run initializes itself: the SQLite database is created (all migrations applied) under ~/.local/share/supportos-plus/ (override with SPP_DATA_DIR), background workers start, a loopback listener serves Help Scout webhooks and the OAuth callback on 127.0.0.1, and the shell boots empty — no sample, demo or simulated data of any kind — until Help Scout credentials are configured. No setup steps.

Connecting your Help Scout account (updated in 1.2.0): open Settings → Help Scout and fill in the credentials form — client ID, client secret, webhook secret (and, optionally, the Docs API key) from your Help Scout custom app. The values are stored encrypted in the local secrets envelope. Then:

  1. On your Help Scout profile create a custom app and set its callback URL to exactly http://localhost:3000/oauth/callback (shown in the connection card; change the port with HELPSCOUT_REDIRECT_URI — the registered URL and the setting must match).
  2. Save the credentials in the form and restart SupportOS+.
  3. That is all. Since 1.2.0 the app signs in with the stored client credentials automatically (no token dance required) and immediately starts the initial sync — conversations, customers, threads, ratings and the rest start arriving on the first boot. The browser OAuth flow and the in-page "connect with client credentials" button remain available if you prefer an explicit connect.

Every failure along the way — a rejected login, a rate limit, a failed resource sync, a rejected webhook — is recorded and shown on the Diagnostics page (see below) instead of vanishing into stdout logs.

Environment variables (HELPSCOUT_CLIENT_ID, HELPSCOUT_CLIENT_SECRET, HELPSCOUT_WEBHOOK_SECRET, HELPSCOUT_DOCS_API_KEY, HELPSCOUT_REDIRECT_URI) still work and override the stored values per field — useful for CI and headless setups. If the callback listener cannot bind its port (another program is using it), the connect button says so instead of leaving a dead browser tab.

The remaining environment knobs (audit §4: none of these were documented before):

Variable Default Purpose
SPP_LOOPBACK_PORT OS-assigned Pin the localhost OAuth/webhook listener port — register the SAME port in the Help Scout app's redirect URI
LOG_LEVEL info tracing filter level
LMSTUDIO_BASE_URL http://127.0.0.1:1234 Local LLM server (copilot, embeddings)
LMSTUDIO_CHAT_MODEL / LMSTUDIO_EMBEDDING_MODEL server default Model overrides
LMSTUDIO_TIMEOUT_MS / LMSTUDIO_CONCURRENCY 120000 / 2 Request budget
QDRANT_URL / QDRANT_ENABLED http://127.0.0.1:6333 / true Vector store endpoint
SYNC_INTERVAL_MINUTES 1 Incremental-sync quiet ceiling
SYNC_API_CONCURRENCY 2 Parallel Help Scout requests during sync

Working in real time (updated in 1.3.0): the local mirror polls Help Scout every minute by default (configurable in Settings), streams conversations into the inbox while the initial sync is still running (live progress on Sync Health), and applies what you do immediately — replies, notes, status changes and assignments are dispatched to Help Scout within seconds, with a per-conversation status line that says "delivering…", "delivered ✓" or the exact failure. A "Sync now" button sits on the Inbox header, every conversation has its own refresh, and Dashboard / Reports / Customers / Organizations / Knowledge all carry reload buttons that re-pull real data rather than repaint a stale cache. (True push via webhooks is impossible for a localhost desktop app — Help Scout needs a publicly reachable URL — so fast polling plus everywhere-refresh is the honest real-time design.)

The Copilot (new in 1.3.0): open the spark icon on any conversation and the panel reads the entire thread locally — no configuration, no AI service: what the customer needs (extractive summary), their open questions, intent / urgency / sentiment / escalation signals, the customer's history, similar past issues with the resolution that worked and why they matched, relevant Help Center articles, and a suggested reply assembled from that evidence (every line traces to a real past resolution or article — nothing is invented) that inserts into the composer for editing. Install LM Studio and set a chat model in Settings to add generative drafts on top; the local engine keeps working without it.

Help Center sync (fixed in 1.3.0): Help Scout's Docs API is used at its real v1 endpoints (the previous build called a /v2/ that does not exist, so articles never arrived). Collections, categories and full article contents sync on the initial pull, on every incremental pass and via the "Sync Help Center now" button. The Docs API uses a separate key from the Mailbox API (Help Scout → Profile → Security & Access → Docs API); the Knowledge page says exactly that when the key is missing instead of showing a silently empty tab.

Diagnostics & reports (new in 1.2.0): the Diagnostics page shows every recorded error, warning and info event — sync failures with the failing resource and reason, automatic sign-ins, OAuth callback outcomes, webhook rejections, background job failures — with level filters and a clear button, plus the outcome of every recent sync run (records processed, errors, state). If "nothing is syncing", the answer is on this page.

Resetting workspace data (updated in 1.2.0): Settings → Help Scout → Workspace data deletes every synced record (conversations, customers, threads, ratings, knowledge base, incidents, reports, AI records — plus anything left over from the demo dataset that shipped before 1.2.0), resets sync state and the vector index in one click after a two-step confirmation, and arms a fresh first sync for the next start. Credentials, application settings, business hours, automation rules, saved views/replies and custom object types are kept.

Upgrading from 0.2.x: the first 1.0.0 launch moves your existing data from the old ~/.local/share/supportos-plusplus/ directory to the new location (and renames the database inside it) automatically — one atomic directory rename, nothing lost, no manual steps. Downgrading back to 0.2.x afterwards is not supported.

Checksums: every release ships SHA256SUMS.txt covering both packages; sha256sum -c SHA256SUMS.txt after download.

What's inside (from the source)

A single Cargo workspace, split by concern:

Crate Role
crates/catalog Shared type catalog re-exported by every other crate
crates/core Pure-Rust business core — SQLite persistence, sync engine, Help Scout provider (real), AI providers, sqlite-vec vector store, loopback listener, diagnostics event log. No UI deps.
crates/app-shell Native desktop shell (eframe/egui) — 23 pages render in-process against core, no HTTP hop
crates/xtask Developer entry point — dev / lint / verify-icons / package / deb / db ops (migrate/backup/restore/healthcheck/vector-count)

The 23 shell pages: Shell, Dashboard, Inbox, Customers, Knowledge, Reports, Operations, AI Center, Automation, Connectors, Custom Objects, Incidents, Issue Radar, Notifications, Onboarding, Organizations, Outreach, Search, Settings, Support Graph, Sync Health, Diagnostics, Live Chats.

Key design decisions

  • Native egui shell. The Phase 4 cutover replaced the Leptos/WASM frontend and the Tauri 2 webview shell with a single eframe/egui desktop app — no browser engine, no CSP plumbing, no wasm target. Pages query the core directly through the DbHandle facade (writer lane + 2 WAL reader connections behind it); live updates flow over the in-process EventBus and wake the render loop on demand.
  • sqlite-vec vector index (decision D4). The chunk count sits far below the 100k gate, so dense vectors live in a vec0 SQLite table behind the VectorStore trait — the store stays swappable, and there is no embedded qdrant engine to compile or babysit.
  • Loopback listener. A minimal 127.0.0.1 HTTP listener serves exactly two routes: POST /webhook (Help Scout event ingress, HMAC-SHA1 verified) and GET /oauth/callback (single-use state, code exchange) — the only inbound network surface of the desktop app.
  • SQLite everywhere. rusqlite (bundled) for tickets, conversations and docs; business-minutes SLA math with full IANA timezone parity (chrono-tz).
  • One crypto module. All encrypted artifacts (.sosync sync bundles, settings exports) share core::crypto: scrypt key derivation + AES-256-GCM sealing, written once and tested.
  • Local AI adapters. LM Studio, Ollama and generic OpenAI-compatible endpoints via reqwest with SSE streaming — pointed at localhost services only.
  • Hardened input handling. ammonia HTML sanitization, bounded NFA regex (no ReDoS surface), SSRF-guarded URL parsing for connectors, and a Landlock + seccomp sandbox around pdf/docx parsing — compiled into the shipped packages (kernels without Landlock degrade to seccomp-only, logged).
  • Small release binaries. LTO + opt-level = "s" + stripped symbols.

Building from source

Requires the pinned Rust toolchain (see rust-toolchain.toml) and no build-time system packages — the egui shell needs no webkit and no wasm target. At runtime a machine needs the usual Linux desktop graphics stack: an X11 (or Wayland) session, libxkbcommon-x11, and a GL or Vulkan implementation (Mesa; software rendering via llvmpipe/lavapipe works fine on GPU-less and headless setups).

cargo xtask dev
cargo xtask lint
cargo xtask package   # AppImage → target/appimage/SupportOS-Plus-x86_64.AppImage
cargo xtask deb       # .deb     → target/deb/supportos-plus_<version>_amd64.deb
cargo test            # unit tests (crypto round-trips, HMAC vectors, SLA math, page models)

cargo xtask works because .cargo/config.toml aliases it to cargo run --locked -p supportos-plus-xtask -- (audit §4: the README cited the alias long before it existed).

xtask package stages the AppDir (release binary, .desktop entry, hicolor icons, AppRun) and runs appimagetool — located via APPIMAGETOOL, cached under target/tools/, or downloaded from the official continuous release. The AppImage bundles the X11 keyboard libraries (libxkbcommon, libxkbcommon-x11, libxcb-xkb) from the build host so it also opens windows on GUI-less systems, so the build host must have libxkbcommon0 libxkbcommon-x11-0 libxcb-xkb1 installed (every desktop machine has them; CI installs them explicitly). xtask deb stages the same payload for dpkg and packs it with dpkg-deb --root-owner-group. The Tauri-era msi/nsis/dmg/app targets are gone; the two Linux packages are the complete packaging surface.

Verification

  • CI (every push to main): fmt, clippy (-D warnings), the full workspace test suite, the sandbox-feature tests, cargo-deny advisories/licenses, plus an AppImage build, a .deb build, and boot-chain smoke runs of both packages (SPP_SMOKE=1 verifies logging, single-instance lock, migrations, boot services, background workers and the loopback listener).
  • Release pipeline (every tag): builds both packages on the ubuntu:22.04 glibc 2.35 baseline, installs the .deb through APT on clean ubuntu:22.04 / ubuntu:24.04 / debian:12 containers (dependency resolution, desktop entry + icons, boot smoke, GUI launch under Xvfb using only the declared dependencies, reinstall, uninstall with user data preserved, purge), launches the AppImage under Xvfb, then publishes the release with SHA-256 checksums and live-verified download links. Nothing is published unless every step passes.

License

MIT

About

SupportOS+ — local-first support desk (Rust/egui): Help Scout sync, knowledge base, semantic search, SLA reports, local AI

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages