Files
tutti/docs/architecture/desktop-update-admission.md
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

4.8 KiB

Desktop Update Admission

@tutti-os/desktop-update-admission is the shared desktop admission boundary for Tutti Desktop and TSH Desktop. It contains a Go daemon core for policy transport, scheduling, validation, and feature caching, plus TypeScript contracts, Electron presentation, updater ownership, preload APIs, and React UI.

Ownership

The background daemon (tuttid or desktopd) owns:

  • the production or development policy checker
  • the authoritative request identity supplied by its Electron parent
  • the proactive startup request
  • the 3-second startup and 10-second foreground request timeouts
  • foreground throttling and request single-flight
  • policy-response validation and fail-open classification
  • exact-identity feature-availability persistence
  • local snapshot, startup-wait, and refresh APIs

Electron owns:

  • resolving the installed or explicitly mocked current version once
  • injecting that same identity into the daemon and updater
  • sending foreground and retry lifecycle signals to the daemon
  • the startup gate and forced-upgrade window
  • mandatory updater ownership and minimum-target validation
  • renderer IPC and an in-memory projection of the daemon feature snapshot

Product adapters own endpoint selection, the concrete updater, download URLs, window assets, and the authenticated local-daemon transport. They do not perform policy HTTP requests or persist feature policy.

Dependency direction

Electron bootstrap
  -> starts tuttid / desktopd with one immutable desktop identity
  -> daemon proactively checks the policy service
  -> Electron reads the completed startup snapshot over authenticated local HTTP
  -> shared Electron controller applies the startup gate and updater flow

resume / focus / retry
  -> Electron sends a local refresh trigger
  -> daemon applies throttling, timeout, validation, and remote transport
  -> Electron consumes the returned snapshot

business renderer
  -> trusted preload IPC
  -> in-memory feature projection
  -> daemon-owned remote/cache snapshot

Tutti's production remote chain is Electron -> tuttid -> daemon HTTP client -> policy service. TSH's chain is Electron -> desktopd -> control-plane client -> policy service. A business renderer never reads environment variables or accesses either remote endpoint.

Lifecycle

The daemon starts its initial policy request during construction, before the local API is consumed. GET /v1/desktop-update-admission/startup waits for that initial request and returns its completed snapshot; it never initiates the remote request. A failed, malformed, or timed-out request is represented as failedOpen, so Electron opens the application.

POST /v1/desktop-update-admission/refresh accepts only foreground or retry. Foreground requests are throttled for 30 minutes by default. Retry bypasses that interval. Concurrent refreshes share the active request. Electron retains only the one-prompt-per-process presentation rule.

An upgradeRequired decision opens the isolated admission window. The forced flow acquires the updater lease, captures normal configuration, prepares a channel-matched update, validates the target against the returned minimum, downloads it, validates again, and requests installation. A later allowed policy releases the gate and restores normal updater configuration.

Feature availability

Feature availability is independent from the minimum-version decision. A valid remote envelope atomically replaces the daemon cache. A missing or invalid feature envelope retains the previous feature snapshot while a valid minimum version decision remains usable. A valid empty list explicitly clears the cache.

The cache lives under the daemon state directory, is accepted only for the exact product, platform, architecture, and current version, and stores no minimum version or admission decision. Remote policy is never restored from disk. Electron and renderer code only receive the daemon's immutable remote, cache, or empty projection.

Development boundary

Packaged daemons ignore all DESKTOP_UPDATE_ADMISSION_* variables. For an unpackaged client, Electron resolves only currentVersion and updater simulation fields. It injects the current version into both the daemon identity and updater. The daemon independently owns policy, feature, timeout scenario, sequence, and foreground-interval parsing.

With in-process transport, the daemon evaluates the local policy scenario. With loopback transport, the standalone mock server exclusively owns policy, minimum-version, feature-key, sequence, and named-policy fields; the client daemon receives only the loopback URL and sends the real HTTP request.

Invalid enabled configurations fail daemon startup. The mock server binds only to 127.0.0.1. Simulated installation ends in an explicit development state and never invokes the production installer or restart path.