Ctrl K
ComunidadGuías…

docs/src/desktop-app.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Desktop App

Moltis Desktop is a native client written in Rust with
[gpui](https://gpui.rs). It **connects to a running moltis daemon** instead of
embedding one: quit the app and the daemon keeps answering your channels, cron
jobs and other clients. It can connect to the daemon on this computer or to
remote daemons.

```mermaid
flowchart LR
    App[Moltis Desktop] -- "WSS /ws/chat + API key" --> Local[(moltis on this computer)]
    App -- "HTTPS (LAN, Tailscale)" --> Remote[(moltis on another machine)]
    App -- "ssh -L → 127.0.0.1:port" --> Tunnel[(moltis behind SSH)]
```

## Installing

Each release attaches `moltis-desktop-<version>-macos.app.zip`: a universal
(Apple silicon and Intel), signed and notarized app. It **includes the moltis
daemon**, so nothing else needs to be installed: unzip it, move
**Moltis Desktop** to Applications and open it. The first time, the app
installs its daemon as a background service and connects.

For Linux, each release attaches `moltis-desktop-<version>-x86_64.AppImage` and
`moltis-desktop-<version>-aarch64.AppImage`, also with the daemon inside:

```bash
chmod +x moltis-desktop-*.AppImage
./moltis-desktop-*.AppImage
```

It runs on X11 and Wayland and needs a Vulkan driver, `libxkbcommon` and
`fontconfig`, which desktop distributions install by default. An AppImage is
mounted at a new temporary path on every launch, so the app copies its daemon
to `~/.moltis/bin/moltis` and runs the service from there.

## Building it

The app lives in `apps/desktop`, a separate Cargo workspace:

```bash
just desktop-run              # debug build and run (no bundle, no daemon)
just desktop-open-macos       # build the app with its daemon (release), open it
just desktop-package-macos    # only build it, in apps/desktop/target/bundle
just desktop-package-linux    # build the AppImage, in apps/desktop/target/bundle
```

`scripts/package-desktop-macos.sh` builds both binaries and signs them. Its
environment variables choose a universal build (`DESKTOP_UNIVERSAL=1`), the
signing identity (`DESKTOP_SIGN_IDENTITY`), the daemon's features, or no
daemon at all (`DESKTOP_DAEMON=0`); see the script's header.
`scripts/package-desktop-linux.sh` takes the same daemon variables, plus
`DESKTOP_TARGET` and `APPIMAGETOOL`.

On Linux, building needs the development packages of gpui's backends, for
example on Debian or Ubuntu:

```bash
sudo apt-get install libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev \
  libx11-xcb-dev libxcb1-dev libfontconfig-dev libfreetype-dev libvulkan-dev
```

## Connecting

On start the app connects to the last server you used, or to the daemon
configured on this computer (found through `moltis.toml`, honouring
`MOLTIS_CONFIG_DIR`). On a clean installation, **This computer** uses the
default local configuration, and when no daemon answers the app asks **Where
should Moltis run?**:

- **On this computer** installs moltis as a background service and connects
  as soon as it is up.
- **On another computer** opens the **Add a server** form, to connect over
  HTTPS or an SSH tunnel. Nothing is installed locally, so you can use the app
  with a remote daemon only.

Once a service is installed, the app starts it again without asking when it
finds it stopped. It starts the service as installed (`moltis service
restart`), so a daemon you installed with Homebrew or cargo keeps its binary
and options. The app only reinstalls a service that points at a copy of the
app that was moved or deleted. The **Servers** screen (`⌘⇧S`, or click the server name at
the bottom of the sidebar) lists:

- **This computer**: the local daemon, run as a background service
  (`moltis service install`: a launchd agent on macOS, a systemd user unit on
  Linux, or a small supervisor script where systemd is unavailable). It uses
  the `moltis` binary shipped inside the app, or else the one on your `PATH`.
  The service keeps running after the app quits and starts at login;
  `moltis service uninstall` removes it.
  - If you move or delete the app, the service is left pointing at a path
    that no longer exists; the next launch reinstalls it from the app's new
    location. A daemon you installed another way (Homebrew, cargo) is never
    replaced.
  - After an update, the running daemon may be older than the one inside the
    app. A banner offers **Restart daemon**; the app never restarts it
    unasked, since it may be busy.
- **Saved servers**: added by URL (direct HTTPS) or as an SSH tunnel.
- **Nearby**: daemons advertising `_moltis._tcp` over Bonjour/mDNS.

### Direct (HTTPS)

Enter `https://host:port`. Moltis daemons serve a certificate from their own
certificate authority, so the first connection shows the certificate's SHA-256
fingerprint. Compare it with the daemon's before trusting it; the app then
pins it. If the certificate later changes, the app stops and asks again.

Plain `http://` is only accepted for `localhost`: credentials are never sent
unencrypted to another machine.

### SSH tunnel

Enter an SSH destination (`user@host` or an alias from `~/.ssh/config`) and
the daemon's port on that machine. The app runs your system `ssh` (so your
config, agent and hardware keys work as in a terminal) with a local port
forward to the remote loopback address. The daemon does not need to listen on
the network.

If SSH exits during a network outage, the app reopens the tunnel automatically
with retry backoff. **Retry now** also recreates a stopped tunnel.

```admonish note
Before a daemon's first-run setup is complete, connections from its own
loopback address are trusted without credentials. A tunnel lands on that
address, so anyone who can SSH into the machine can use such a daemon.
Finish the setup (set a password) on any daemon you reach over SSH.
```

## Signing in

The first time a daemon asks for credentials, sign in with its password. The
app exchanges it for a scoped API key (`operator.read`, `operator.write`,
`operator.approvals`, never `operator.admin`) labelled
"Moltis Desktop (hostname)", stores the key in the system keychain and forgets
the password. Revoke it any time in the web UI under **Settings → Security**.

Daemons secured only with passkeys: create an API key in the web UI and paste
it into the app.

**File → Sign Out of This Server** deletes the stored key.

## Chatting

- Sessions of the daemon appear in the sidebar, grouped into **Today** and
  **Earlier**; a filled dot marks one that is replying. **New chat** (`⌘N`)
  starts one; **Scheduled** and **Skills** open those pages of the web UI.
- The footer shows the owner's name (from the daemon's user profile) and the
  server; click it to switch servers.
- Replies stream in with native markdown, syntax-highlighted code, collapsible
  reasoning, tool cards (click to see output) and token/speed details.
- After reconnecting, the app reloads the open conversation; other cached
  conversations reload when selected. Live events received while history is
  loading are reconciled with the snapshot.
- The message box keeps your draft until the daemon accepts it. Offline or
  failed submissions retain the text, and edits made while sending are kept.
- Under the message box: the context in use against the model's window
  (`20k / 200k`, from `chat.context`) and the model picker. Stop a reply with
  the stop button or `⌘.`.
- When a command needs approval, an approval card appears above the message
  box. If the window is in the background, a notification is posted; short,
  single-line commands can be approved from it, anything else opens the app
  so you can review the full command.

## Web UI

Settings, channels, providers, scheduled jobs and logs open in your browser
from the **Web** menu (`⌘,` opens settings).

## Protocol

The app speaks the WebSocket RPC protocol v4 through the `moltis-client`
crate, which is also what `moltis-ctl` uses. Typed payloads (chat events,
sessions, models, approvals) are defined in `moltis-protocol` and shared with
the daemon.

## Troubleshooting

- Logs: run with `MOLTIS_DESKTOP_LOG=debug`.
- Screenshots for bug reports: build with `--features capture` and set
  `MOLTIS_DESKTOP_CAPTURE=/path/to/shot.png`.