openframe-saas-desktop README

The project's README from github.com/flamingo-stack/openframe-saas-desktop. As one markdown file: README.md.

OpenFrame Console

Native desktop shell for OpenFrame — a Tauri 2 application that embeds the openframe-oss-frontend static export and adds what a browser tab cannot do: OS notifications that keep arriving while the app sits in the tray, a session that survives long idle, and a system-browser OAuth flow.

No UI code lives in this repo. The interface is the frontend's static export, staged into www/ at build time and embedded in the binary. What is here is the Rust shell around it.

Supported: macOS, Windows. Linux builds, but has neither an OS-notification backend nor any URL-scheme registration — so notifications never arrive and the OAuth callback has no way back into the app, leaving sign-in unable to complete.

Requirements

Quick start

npm install
FRONTEND_REF=main npm run build:web   # clone + build openframe-oss-frontend, stage into www/
npm run dev         # tauri dev

npm run dev stages a placeholder bundle when www/ is missing — the generate_context! macro embeds www/ at compile time, so the crate cannot build without one. The placeholder is enough to compile and run the shell, never enough to use the app.

Run the app through npm run dev or a built .app/.exe. The bare target/debug binary is not registered with the OS, so tray icons, notifications and URL-scheme activation behave differently or not at all. Sign-in is one of them: the OAuth callback comes back through the OS on openframe-console://, which macOS routes only to a bundled .app it has seen (launch it once from /Applications) and Windows only to an install that has written its HKCU key.

Building

The Makefile is the entry point; the npm scripts are the primitives it calls. npm run build on its own is a raw tauri build that skips web staging and bakes no host.

make lint     # rustfmt --check + clippy -D warnings
make test     # cargo test
make build OPENFRAME_SHARED_HOST_URL=https://auth.example.com FRONTEND_REF=1.0.100
Variable Purpose
OPENFRAME_SHARED_HOST_URL Required for build. Baked into the binary; see Configuration.
OPENFRAME_VERSION App version, applied via tauri build --config. Defaults to tauri.conf.json.
TARGET Rust target triple for cross-compilation.
BUNDLES Bundle subset, e.g. BUNDLES=app for an unsigned macOS .app.
FRONTEND_DIR Use an existing frontend checkout instead of cloning. No git operations are run against it — this is the local dev loop.
FRONTEND_REF Required unless FRONTEND_DIR is set. Frontend git tag or branch to build; no default, so a build never silently tracks main. A release uses the frontend image tag prod runs (see Releasing).
FRONTEND_REPO Frontend origin, if not the public repo.

Code signing and notarization are deliberately not in the Makefile — they run against the produced artifact, which is why tauri.conf.json sets certificateThumbprint: null.

Releasing

release.yml on workflow_dispatch takes a mandatory frontend_tag: the openframe-oss-frontend image tag prod runs, openframe-saas.frontend.image.tag in manifests/tenant/values-prod.yaml of openframe-saas-tenant. The pipeline resolves the frontend git tag of the same name (the frontend release workflow creates a GitHub release for every released image) and builds the static export from that commit. Pushes to main build the rolling latest prerelease from frontend main, the same source as the dev environment's latest image.

Configuration

The shell knows exactly one configured URL: the shared auth host. There is no tenant host to configure — the frontend's sign-in screen discovers the tenant from the user's email, and the shell is told that tenant's origin once login succeeds.

The shared host is baked in at build time and can be overridden per install by shared_host in config.json, which lives in the OS app-config directory:

OS Path
macOS ~/Library/Application Support/com.openframe.console/config.json
Windows %APPDATA%\com.openframe.console\config.json
Linux ~/.config/com.openframe.console/config.json

Renamed from OpenFrame Desktop (2026-08-26), identifier included: com.openframe.desktop → com.openframe.console. The identifier keys the paths above, the log directory, the macOS notification authorization, the launchd label and the Windows AUMID, so a pre-rename install comes back signed out (its tokens.json stays behind at the old path) and re-prompts for notification permission. That was acceptable because the app had only ever been distributed to test users.

Two leftovers are not inert, and a renamed build deletes both on first run. The login registration is named after the identifier, so an abandoned one keeps launching the old build every login — autostart::remove_legacy_registration clears the macOS/Linux file and the Windows Run value written under OpenFrame Desktop. On Windows the old openframe-desktop:// scheme key still points at the old exe, so a toast left in the Action Center from before the rename would start the build it replaced — remove_legacy_url_scheme clears that and the stale AppUserModelId key. Both can be removed once no machine still has a pre-rename build on it. Nothing else is migrated: the old config directory is left where it is, and installed-agent rows written before the rename stay filed under the old AGENT_TYPE.

Start at Login is reset by the rename, not carried over. autostart_configured lived in the old config directory, so the reconcile sees a renamed install as a first launch and applies the default — which re-enables start-at-login for anyone who had turned it off on a pre-rename build. It cannot be inferred from what the new identity can see: an opted-out pre-rename install and a fresh install both present as "no registration and no record". The old config.json would separate them, but reading it is the migration this rename deliberately does not do. The user's own toggle is authoritative from that point on.

{
  "shared_host": "https://auth.example.com",
  "learned_host": "https://acme.example.com"
}

learned_host is written by the shell after login; it is not meant to be edited. Tokens live next to it in tokens.json (owner-readable only, plaintext — keychain storage is a known gap).

Other keys, all optional:

Key Purpose
self_update_enabled false disables the startup and background update checks.
update_manifest_url Overrides the updater manifest endpoint.
autostart_enforced Pins Start at Login on (true) or off (false) for a managed install, and takes the toggle away from the user. Absent leaves the choice to them.
autostart_configured Shell-written. Records that the start-at-login default has been applied, so it is applied once rather than re-asserted over a user who turned it off.
autostart_show_window_next_start Shell-written, cleared on the next launch. Set by an update that restarts while a window is open, so the relaunch shows one even in a login session.

Gateway prerequisite: CORS must allow the shell's origins — tauri://localhost (macOS/Linux) and http://tauri.localhost (Windows) — including exposing the Access-Token and Refresh-Token response headers. Without it the UI renders and every data call fails.

What the shell owns

See docs/auth-and-notifications.md for how those work, and docs/architecture.md for the shell itself — windows, the bundle pipeline, and the security model.

Layout

Makefile              build entry point
scripts/
  build-web.sh        clone/build the frontend export → www/
  make-placeholder-web.mjs
src-tauri/
  src/lib.rs          windows, tray, config, NativeAuth commands
  src/tokens.rs       token custody, refresh, rotation events
  src/nats.rs         NATS-over-WebSocket connection lifecycle
  src/notifications.rs  subscription, dispatch, click delivery
  src/notification_actions.rs  what a notification button does, and its session gate
  src/chat_api.rs     the REST calls a button completes without a window
  src/macos_un.rs     UNUserNotificationCenter backend
  src/windows_toast.rs  toast XML + the button-argument codec
  src/windows_activator.rs  COM activator the toast buttons activate
  src/autostart.rs    start-at-login registration and the headless start
  src/updater.rs      silent startup update, background poll, update commands
www/                  staged frontend bundle (generated, git-ignored)

openframe-saas-desktop (flamingo-stack/openframe-saas-desktop)

Searching Database…

Что это?OpenFrame Console — десктопная оболочка на Tauri 2 для SaaS-платформы OpenFrame. Она оборачивает статический веб-экспорт openframe-oss-frontend и добавляет возможности уровня ОС, недоступные вкладке браузера: уведомления в трее, постоянные сессии и вход через OAuth в системном браузере.
Это полноценное приложение или просто обёртка над веб-фронтендом?Кода интерфейса в этом репозитории нет — интерфейс это статический экспорт фронтенда, который на этапе сборки размещается в www/ и встраивается в бинарник. Здесь только оболочка на Rust/Tauri, добавляющая нативную интеграцию с ОС вокруг этого встроенного веб-UI.
Как запустить это для разработки?Запустите `npm run dev`: если www/ отсутствует, подставляется заглушка (макрос generatecontext! встраивает www/ на этапе компиляции, так что без неё сборка невозможна). Настоящие иконки трея, уведомления и обработку OAuth-коллбэков нужно проверять на собранном .app/.exe, а не на голом отладочном бинарнике.
Что нужно установить перед сборкой?Rust (stable) с системными требованиями Tauri 2 для вашей платформы, Node.js 20+ и git (экспорт фронтенда клонируется при сборке).
А на Linux оно вообще работает?На Linux оно собирается, но там нет ни бэкенда системных уведомлений, ни регистрации URL-схемы, поэтому уведомления не приходят, а коллбэку входа через OAuth просто некуда доставляться — вход не завершается.
Какие платформы официально поддерживаются?Поддерживаемые цели — macOS и Windows. Linux компилируется, но в нём нет бэкенда уведомлений и обработки URL-схемы, которые нужны, чтобы вход и уведомления реально работали.
Где хранятся мои токены входа и безопасно ли это?Токены записываются в tokens.json рядом с файлом конфигурации; читать их может только владелец, но хранятся они в открытом виде — сам README называет хранение в keychain известной недоработкой.
Как именно устроен вход через OAuth?Открывается браузер по умолчанию, чтобы переиспользовать уже существующую там SSO-сессию, а не запрашивать учётные данные заново. Ответ возвращается через URL-схему openframe-console://auth, а обмен токенами оболочка выполняет нативно: заголовки ответа, в которых они передаются, невидимы для кросс-доменного запроса из webview.
Что стало с установками 'OpenFrame Desktop' (до переименования)?Идентификатор приложения сменился с com.openframe.desktop на com.openframe.console, поэтому старые установки оказываются разлогиненными, теряют настройку автозапуска (Start-at-Login) и снова запрашивают разрешение на уведомления. При первом запуске после переименования оболочка вычищает устаревшую регистрацию входа и старую URL-схему openframe-desktop://.
Приложение обновляется само?Да — по умолчанию проверка обновлений выполняется при старте и в фоне. Это можно отключить через selfupdateenabled: false или перенаправить на собственный манифест через updatemanifesturl в config.json.
Он запускается автоматически при моём входе в систему?Да, автозапуск при входе регистрируется на пользователя (launchd на macOS, запись Run в HKCU на Windows, XDG autostart на Linux) и включён по умолчанию: с --autostart приложение стартует сразу в трей, не открывая окно.
Size
0.9 MB
Stars
156
Age
2mo
Commits · 90 Days