Skip to content

Host Settings

The JetWhale host's behavior is configured from its Settings screen, opened from the gear icon in the drawer.

Settings are organized into four sections, each holding one or more pages:

SectionPages
GeneralAppearance, Application
ConnectionDebug Server, SSL Certificate, ADB Support
AI AgentsMCP Server, Permissions, Activity
PluginsInstalled Plugins, Add Plugins, Security

Window size and position

The host remembers its window size and position across launches automatically (when the window is in normal floating state — maximized/fullscreen state is not persisted). There is nothing to configure.

General

Appearance

SettingDescription
LanguageUI language of the host: English or Japanese.
ThemeColor scheme: builtin:dynamic, builtin:light, or builtin:dark.

Application

Everything about this install of the host.

Maintenance

  • Application Data Directory — shows the host's app-data path (normally ~/.jetwhale/) with a shortcut to open it in your file manager.
  • View Application Logs — opens the built-in log viewer.

Updates

  • Current Version — the running host version.
  • Check for updates on startup (notify only) — toggle the automatic check. Updates are never applied automatically.
  • Check for Updates — check immediately. When one is found you can Install and Relaunch (on the platforms that support in-app updates) or Open Download Page.

Connection

Debug Server

The Settings → Connection → Debug Server page configures the WebSocket server debuggee apps connect to.

SettingDefaultDescription
Debug Server Port5080The plain ws port. Must match the port your app dials in startJetWhale { connection { endpoints { ... } } }, unless it discovers the host — discovery reads the port from what the host advertises.
Enable WSSonWhether the secure wss connector is exposed at all.
WSS Port5443The wss port, matched the same way: your app's wss(host, port) when it names one, or the advertised port when it discovers the host.

Changing any of the three reveals a single Apply button, which confirms before restarting the server — restarting disconnects every session. All three are applied together, because the server only reads them when it binds, so one restart covers the whole change.

The status line above them shows the running ports, e.g. Running on port 5080 (WSS: 5443) when wss is active, and offers Retry if the server failed to bind.

Each port has to be in 1..65535 — the wss one too, even while Enable WSS is off — and while wss is enabled the two cannot be the same. Apply stays disabled until both hold. The wss port is stored even while Enable WSS is off, so switching wss back on brings back the port you last picked.

Also settable outside the UI

The same settings are available at launch with --wss-port, and to an AI agent through jetwhale.updateSettings (wssPort / wssEnabled). Before connecting over wss, generate a certificate under SSL Certificate.

SSL certificates

To let agents connect over wss, the host serves TLS using a locally-issued certificate. Each entry is a self-contained local PKI: a root CA plus a localhost server certificate signed by it. The host serves wss with the server certificate; the agent trusts the CA.

From the SSL Certificate section you can:

  • Add Certificate — generate a new CA + server certificate and mark it active.
  • Set Active — switch which certificate the server uses. Multiple certificates can coexist so a certificate can be rotated without immediately invalidating apps still pinning the previous one.
  • Delete — remove a certificate. If the active one is deleted, the first remaining certificate becomes active.
  • Show Details — view the CA certificate in PEM form and Copy to Clipboard, to paste into an agent's ssl { trustCertificate(pem = "...") }.

TLS material is stored under ~/.jetwhale/ssl with owner-only permissions (the keystore holds the CA private key), and the generated CA carries name constraints limiting it to local/private addresses (localhost, loopback, and the RFC 1918 / link-local ranges). If you install the CA into an OS trust store — for example on the Windows WinHttp path — prefer the current-user store over the machine-wide store.

Certificate changes apply immediately

Certificate changes (generate, activate, delete) take effect at once: the host hot-swaps only the wss listener onto the new certificate while the plain ws listener keeps running untouched. Connected wss clients drop and reconnect against the new certificate; plain-ws sessions are unaffected.

LAN exposure

  • Plain ws listens on localhost only — it is reachable from a device only via ADB reverse forwarding, so its traffic never leaves the machine.
  • wss listens on all interfaces, so physical devices on the same network (e.g. an iPhone) can connect. The channel is encrypted and clients pin the local CA, so exposure is limited to the encrypted endpoint.
  • This machine's current LAN IP addresses are embedded as Subject Alternative Names in the server certificate at generation time. If your machine's IP changes, regenerate the certificate so LAN clients still pass hostname verification.

ADB support

The Settings → Connection → ADB Support page holds the Android port-forwarding toggle, plus where the host found the adb it needs for it:

SettingDefaultDescription
Automatically wire ADB port to host PC portonRuns adb reverse for Android devices as they connect. Inactive on machines without adb. Read only when the debug server starts, so a change lands on the next server restart. See ADB Auto Port Mapping.

Under Health Check, ADB Executable Path shows where JetWhale found adb (see How adb is found), or ADB command not found.

AI Agents

MCP Server

The Settings → AI Agents → MCP Server page configures the built-in MCP server for AI agents.

SettingDefaultDescription
MCP Server Port7080Port the MCP server's SSE endpoint listens on, bound to localhost.

Below it are copy-ready connection snippets — a claude mcp add command and a JSON config block, both carrying the port the MCP server is currently running on — plus Open setup guide, which links to the MCP Server page.

Changing the MCP server port here restarts the MCP server immediately (after a confirmation). An agent changing it through jetwhale.updateSettings only persists the value — restarting would drop the agent's own connection — so that change takes effect on the next host start.

MCP Permissions

The Settings → AI Agents → Permissions page is a checkbox tree deciding what an AI agent may do, from read-only observation through to restarting the debug server. See MCP Server → Permissions.

AI Activity

The Settings → AI Agents → Activity page decides how the host window behaves while an agent is working.

SettingDefaultDescription
Follow the plugin an AI agent operatesOnWhile an agent drives a plugin over MCP, the main window switches to that plugin so you can watch it work.

Only a tool call that names a plugin moves the window; host-level calls (navigation, settings, status) leave it alone, and so does a plugin already popped out into its own window — it is visible where it is. While the setting is on and an agent is connected, a banner above the plugin offers Stop following, which turns the setting off without a trip back to this page; while a call is moving the window it also names the tool running. The banner stays up between calls on purpose, so the plugin under it keeps its place through a burst of operations instead of jumping at every one.

Plugins

The Installed Plugins page lists what is loaded, Add Plugins is where new ones come from, and Security holds the plugin trust settings.

Installed plugins live in ~/.jetwhale/plugins/. There are three ways to install one:

  • Official Plugins — one-click install from the official catalog, no coordinates needed. The catalog currently holds the Network Inspector, the Nav3 Navigator and the Compose Semantics Inspector. The artifact version matching the running host is fetched from Maven Central, falling back to the matching snapshot build when the release is not published yet (snapshot hosts fetch their matching snapshot directly).
  • Install from Maven — enter the plugin's group:artifact:version and pick a repository preset (Maven Central, Central Snapshots, Google, JitPack) or a custom URL. Pasting a plain coordinate line (optionally with @https://your.repo/url), a Gradle dependency line, or a Maven <dependency> block fills the fields automatically. The host downloads the plugin jar and the external dependencies it declares (stored in ~/.jetwhale/plugins/libs/).
  • Add Plugin from File — pick a locally built fat-jar, or drop one into ~/.jetwhale/plugins/ yourself (or run ./gradlew installPlugin from a plugin project).

Jars that cannot be loaded (built for a different JetWhale version, missing dependencies, or not valid plugin jars) are listed under Incompatible Plugins, with the concrete failure reason shown per jar.

See Developing Plugins for building your own.

Plugin trust

Plugin jars are arbitrary code running inside the host process, so JetWhale only loads jars you have explicitly approved. Approvals are recorded in ~/.jetwhale/trusted-plugins.json, with each jar pinned to the SHA-256 hash of its content at approval time. On startup:

  • Jars whose current content still matches their pinned hash are loaded.
  • Jars that were never approved, or whose content changed since approval, are not loaded and appear in the Unverified Plugins section of the settings screen for review.

Installing a plugin through the file picker, the Maven dialog, or the official catalog counts as approval; jars dropped into the directory by anything else must be approved manually. Revoking trust unloads the plugin immediately.

Registry signing (opt-in)

The trust registry can additionally be protected by an HMAC-SHA256 signature. Whether it is signed is defined by one fact: does a signing key exist in your OS credential store? There is no on/off flag kept on disk — the Sign plugin trust registry toggle in the plugin settings screen creates that key (on) or deletes it (off). The key lives only in the credential store, never in the app data directory. This is off by default (no key).

  • Off (default — no key): JetWhale does not sign the registry, and on startup the only credential-store interaction is a prompt-free check that no key exists — you are never prompted. The registry is read and written unsigned. The SHA-256 content pinning above still detects a swapped-out jar, but trusted-plugins.json itself is not tamper-protected.
  • On (key present): enabling it provisions a key and re-signs the current registry; from then on the registry is signed on every write and verified on every launch (which reads the key back). Once a key exists, a registry whose signature is missing or does not match is rejected wholesale and every plugin is treated as untrusted — so rewriting trusted-plugins.json, or stripping its signature, cannot forge an approval. If the credential store is unavailable (e.g. a headless Linux session with no keyring), JetWhale logs a warning and loads the registry unverified.

Where the key is kept — and whether you're prompted — depends on the platform:

  • macOS — the login Keychain. Reading the key on each launch shows a Keychain access prompt; choose Always Allow to suppress it on later launches. A plain Allow re-prompts every launch, and a re-signed/updated app build can invalidate the grant and ask again.
  • Windows — encrypted with DPAPI under your user account. Access is transparent — no prompt.
  • Linux — the Secret Service (GNOME Keyring / KWallet). Whether a prompt appears depends on your keyring setup; a headless session with no keyring falls back to unsigned.

Threat model

Plugin trust is an entry-side defense: it stops JetWhale from executing jars you never vouched for, and the SHA-256 pinning detects jars swapped out after approval regardless of this setting. With registry signing off (the default), an attacker who can write to ~/.jetwhale can forge an approval by editing trusted-plugins.json directly. Turning signing on genuinely raises the bar to compromising the OS credential store: a file-writing attacker cannot forge an accepted registry (with a key present, any unsigned or re-signed file is rejected), and cannot even turn signing back off, because deleting the key requires credential-store access — not just a file write. What stays outside scope is an attacker who can already reach the credential store, or who runs code as you and modifies JetWhale itself; protecting against full control of your user account is not a goal of this mechanism.

Command-line options

Overriding the ports at startup

Each port can also be chosen on the command line, which is handy when several hosts have to run side by side (for example one per checkout of the app you are debugging) and would otherwise fight over the same defaults:

OptionOverrides
--server-port <port>Debug Server Port
--wss-port <port>WSS Port
--mcp-server-port <port>MCP Server Port

An option that is not passed keeps using the saved setting. --wss-port only picks the port: wss is still served only when it is enabled in the settings.

An override applies to that launch only and is never written back, but for as long as it is in force it is the port the host reports — the settings screen shows it, so the screen and the running server never disagree. Changing a port on that screen afterwards wins: the new value is saved and the override for that port is retired for the rest of the session. Applying the Debug Server page retires the --server-port and --wss-port overrides together, since it writes both ports at once.

Pass them to the runnable uber jar:

bash
java -jar jetwhale-host-<version>-<osArch>.jar --server-port 5081 --mcp-server-port 7081

When you launch the host from a plugin project with runJetWhale (or runJetWhaleHot), pass them with --args:

bash
./gradlew :myPlugin:runJetWhale --args="--server-port 5081 --mcp-server-port 7081"

Other options

OptionDefaultWhat it does
--plugin-dir <path>Also load the jars in this directory, on top of ~/.jetwhale/plugins/. Repeatable. Not trust-gated — naming the directory on the command line is the approval — and not managed: these jars do not appear as installed plugins and cannot be uninstalled or revoked from the UI.
--log-level <level>the host's configured levelMinimum level the host's own logging emits: DEBUG, INFO, WARN or ERROR. Lower it when diagnosing a plugin that will not load, then read the result in the log viewer.
--mcp-allow-all-permissionsoffAllows every MCP tool for that process only — see MCP Server → Lifting every permission for one launch.
--headlessoffRuns without the application window — see Headless mode below.

Ports are validated at parse time (they must be in 1..65535), so a typo is reported immediately rather than as a bind failure later. Unrecognized arguments are ignored.

Headless mode

--headless runs the host without opening its window. The debug server, the MCP server and plugin instances all come up as usual, so a CI job or an AI agent can drive plugins on a machine with no display.

bash
./gradlew :myPlugin:runJetWhale \
  --args="--headless --server-port 5081 --wss-port 5444 --mcp-server-port 7081"

The host prints the ports it bound and then JetWhale headless: ready; wait for that line before connecting. If a port cannot be bound the process exits non-zero rather than staying up unable to serve. SIGTERM and SIGINT stop it gracefully.

Plugins do not need a window to work. Plugin state is fed by the messages their agent sends, and a plugin's Compose scene is created on demand by whichever MCP tool asks for it, so screenshot and the interaction tools render exactly as they do with a window.

Three differences are worth knowing before you script against it:

  • jetwhale.navigate does not work. It moves the main window's back stack, so with no window it always reports applied: false after its confirmation timeout. Tools that address a plugin directly are unaffected.
  • Coordinates differ from a windowed run. With no window reporting a density, scenes render at density 1.0, so the same element sits at different coordinates than it would on a HiDPI display. Always read coordinates from jetwhale.getAccessibilityTree in the same run rather than carrying them over from a windowed session.
  • A fresh app-data directory enables no plugins. Enable them with jetwhale.setPluginEnabled over MCP, or point -Djetwhale.appDataDir at a directory where they already are. Approving a new plugin jar is still a settings-UI action (the Approve button under Security), so headless runs load already-trusted jars and the dev-plugins directory.

Headless is a little lighter than a windowed run — it starts a few hundred milliseconds sooner and uses roughly 15% less memory — but that is not the reason to use it. Use it because a windowed host cannot start at all where there is no display.

Plugin developers use a sandbox, not ~/.jetwhale

runJetWhale / runJetWhaleHot also set -Djetwhale.appDataDir and -Djetwhale.devPluginsDir, so the whole app-data directory described on this page is redirected into a per-project sandbox. See Developing Plugins → Isolated sandbox environment.

Released under the Apache License 2.0.