Complete agent monitoring and Galaxy Control integration
- expose command-monitor conversations and preserve visible agent transcripts - add bounded polling and a dedicated shell interrupt tool - improve direct-provider images, skills, tool history, and usage handling - package and brand Galaxy Control across releases, installers, persistence, and docs
This commit is contained in:
@@ -1,44 +1,44 @@
|
||||
# Summary
|
||||
Warp ships a local control CLI, provisionally named `warpctrl`, that lets agents, developers, and scripts operate running Warp app processes through a typed, allowlisted command surface. `warpctrl` is an Oz-style wrapper script that invokes the existing channel-specific Warp binary in control mode rather than a separate standalone binary.
|
||||
The public catalog contains exactly **84 default-authorized actions** organized around stable user-facing nouns. Close actions flow through Warp's normal close behavior so existing app warnings remain authoritative. `block.list` is intentionally absent from the catalog. Input-staging commands place text in the input buffer but never submit it.
|
||||
All callers are external same-user processes. There is no inside-Warp/outside-Warp distinction, no verified-terminal invocation context, and no authenticated-user identity layer. Security relies on owner-only filesystem discovery, same-user Unix credential broker with kernel peer credentials, short-lived instance-bound exact-action credentials, loopback HTTP transport, and app-side enforcement.
|
||||
Galaxy ships a local control CLI named `galaxyctrl` that lets agents, developers, and scripts operate running Galaxy app processes through a typed, allowlisted command surface. `galaxyctrl` is a thin wrapper that invokes the existing channel-specific Galaxy binary in control mode rather than a separate standalone binary.
|
||||
The public catalog contains exactly **77 default-authorized actions** organized around stable user-facing nouns. Close actions flow through Galaxy's normal close behavior so existing app warnings remain authoritative. `block.list` is intentionally absent from the catalog. Input-staging commands place text in the input buffer but never submit it.
|
||||
All callers are external same-user processes. There is no inside-Galaxy/outside-Galaxy distinction, no verified-terminal invocation context, and no authenticated-user identity layer. Security relies on owner-only filesystem discovery, same-user Unix credential broker with kernel peer credentials, short-lived instance-bound exact-action credentials, loopback HTTP transport, and app-side enforcement.
|
||||
## Problem
|
||||
Warp has rich interactive actions reachable through UI, keybindings, menus, and deeplinks. Agents can use native tools for files, code, shell commands, and MCP calls, but they cannot reliably operate Warp's own product surfaces: arranging workspaces, focusing panes, opening Warp Drive views, presenting settings, or recovering from ambiguous UI state. Developers cannot compose those actions into shell scripts, demos, or automation workflows, and there is no general local protocol for addressing a specific running Warp instance, window, tab, pane, or session.
|
||||
Galaxy has rich interactive actions reachable through UI, keybindings, menus, and deeplinks. Agents can use native tools for files, code, shell commands, and MCP calls, but they cannot reliably operate Galaxy's own product surfaces: arranging workspaces, focusing panes, presenting settings, or recovering from ambiguous UI state. Developers cannot compose those actions into shell scripts, demos, or automation workflows, and there is no general local protocol for addressing a specific running Galaxy instance, window, tab, pane, or session.
|
||||
## Goals
|
||||
- Provide a first-class, scriptable `warpctrl` command for controlling running Warp app processes.
|
||||
- Make Warp's UI and app state available to agents through a typed, permissioned control plane instead of brittle screen automation.
|
||||
- Provide a first-class, scriptable `galaxyctrl` command for controlling running Galaxy app processes.
|
||||
- Make Galaxy's UI and app state available to agents through a typed, permissioned control plane instead of brittle screen automation.
|
||||
- Keep CLI startup lightweight by avoiding GUI-app startup for routine control commands.
|
||||
- Keep the surface allowlisted and finite: exactly 84 named actions, no arbitrary internal dispatch.
|
||||
- Make targeting explicit and deterministic across multiple Warp processes, windows, tabs, panes, and sessions.
|
||||
- Use a simple enabled/disabled Scripting setting rather than multi-mode invocation-context policies.
|
||||
- Keep the surface allowlisted and finite: exactly 77 named actions, no arbitrary internal dispatch.
|
||||
- Make targeting explicit and deterministic across multiple Galaxy processes, windows, tabs, panes, and sessions.
|
||||
- Use a simple enabled/disabled Galaxy Control setting rather than multi-mode invocation-context policies.
|
||||
## Non-goals
|
||||
- Replacing the Oz CLI or mixing cloud-agent management into this CLI.
|
||||
- Replacing the general Galaxy AI CLI or mixing cloud management into this CLI.
|
||||
- Exposing every internal app action, debug action, or privileged state mutation.
|
||||
- Treating the CLI as a general RPC escape hatch into Warp internals.
|
||||
- Treating the CLI as a general RPC escape hatch into Galaxy internals.
|
||||
- Replacing native agent tools for code editing, file operations, shell execution, or MCP calls.
|
||||
- Providing an authenticated-user identity layer, verified-terminal invocation proof, or invocation-context distinction.
|
||||
- Terminal command execution, accepted-command submission, or agent-prompt submission.
|
||||
- Warp Drive data mutations, cloud-backed state mutations, or sharing operations.
|
||||
- Galaxy Drive data mutations, cloud-backed state mutations, or sharing operations.
|
||||
- Local file content reads, writes, or filesystem-content mutations.
|
||||
## Primary user stories
|
||||
1. **Agent workspace orchestration.** An agent inspects current Warp state, creates or reuses an appropriate window/tab layout, splits panes, names and focuses targets, and leaves the workspace in a readable task-shaped state. The agent continues to use native tools for code edits, file I/O, shell execution, and MCP calls.
|
||||
2. **Existing-session debugging and repair.** An agent understands Warp-specific UI and session structure before acting: which instance/window/tab/pane/session is active, whether the relevant pane still exists, which surface is focused, and which selector to use for follow-up actions.
|
||||
3. **Deterministic demos and walkthroughs.** A script puts Warp into a known presentation state: theme, zoom, windows, tabs, panes, focused targets, panels, and surfaces. The walkthrough advances using structured target IDs and recovers from stale or missing targets.
|
||||
4. **Personalization and preference migration.** An agent inspects settings, proposes Warp equivalents from other tools, applies allowlisted changes, and reports unsupported mappings explicitly.
|
||||
1. **Agent workspace orchestration.** An agent inspects current Galaxy state, creates or reuses an appropriate window/tab layout, splits panes, names and focuses targets, and leaves the workspace in a readable task-shaped state. The agent continues to use native tools for code edits, file I/O, shell execution, and MCP calls.
|
||||
2. **Existing-session debugging and repair.** An agent understands Galaxy-specific UI and session structure before acting: which instance/window/tab/pane/session is active, whether the relevant pane still exists, which surface is focused, and which selector to use for follow-up actions.
|
||||
3. **Deterministic demos and walkthroughs.** A script puts Galaxy into a known presentation state: theme, zoom, windows, tabs, panes, focused targets, panels, and surfaces. The walkthrough advances using structured target IDs and recovers from stale or missing targets.
|
||||
4. **Personalization and preference migration.** An agent inspects settings, proposes Galaxy equivalents from other tools, applies allowlisted changes, and reports unsupported mappings explicitly.
|
||||
## Behavior
|
||||
1. The CLI operates only on running local Warp app processes from the same channel as the channel-specific CLI binary. If no compatible same-channel process is available, it exits non-zero with a structured error.
|
||||
2. The CLI exposes only the 84 explicitly allowlisted actions. Unknown, unsupported, or non-allowlisted requests fail with structured errors and are never forwarded to arbitrary internal dispatch.
|
||||
3. Every successful mutating request identifies the Warp process instance, resolved target, and a success payload suitable for JSON output.
|
||||
1. The CLI operates only on running local Galaxy app processes from the same channel as the channel-specific CLI binary. If no compatible same-channel process is available, it exits non-zero with a structured error.
|
||||
2. The CLI exposes only the 77 explicitly allowlisted actions. Unknown, unsupported, or non-allowlisted requests fail with structured errors and are never forwarded to arbitrary internal dispatch.
|
||||
3. Every successful mutating request identifies the Galaxy process instance, resolved target, and a success payload suitable for JSON output.
|
||||
4. Every failure identifies a stable machine-readable error code, a human-readable explanation, and any selector that was ambiguous, missing, stale, or invalid.
|
||||
5. The CLI supports human-readable output by default and JSON output for scripts with stable field names.
|
||||
6. Process discovery and instance selection:
|
||||
- `warpctrl instance list` returns all reachable local Warp app processes from the CLI binary's channel.
|
||||
- `galaxyctrl instance list` returns all reachable local Galaxy app processes from the CLI binary's channel.
|
||||
- Each process has an opaque `instance_id`, channel/build identity, and display metadata.
|
||||
- If exactly one compatible process is available, commands target it implicitly.
|
||||
- If multiple compatible processes are available and no single clearly active instance exists, the CLI fails and asks for an explicit `--instance` selector.
|
||||
- Explicit `--instance` and `--pid` selectors cannot target a process from another channel.
|
||||
7. Target introspection:
|
||||
- `warpctrl window list`, `warpctrl tab list`, `warpctrl pane list`, `warpctrl session list`, `warpctrl app active`.
|
||||
- `galaxyctrl window list`, `galaxyctrl tab list`, `galaxyctrl pane list`, `galaxyctrl session list`, `galaxyctrl app active`.
|
||||
- These return opaque protocol-facing IDs and metadata for subsequent commands.
|
||||
8. The target selector model is hierarchical: instance → window → tab → pane → session. Non-hierarchical selectors (files, surfaces) resolve inside the selected instance.
|
||||
9. Every selector family supports an ergonomic `active` form. For window-scoped mutations, an omitted window selector may fall back to the sole existing window. Zero windows returns `missing_target`; multiple windows without an active one returns `ambiguous_target`.
|
||||
@@ -47,29 +47,29 @@ Warp has rich interactive actions reachable through UI, keybindings, menus, and
|
||||
12. When an explicitly supplied target disappears between discovery and execution, the request fails with `stale_target`. The CLI never silently chooses a different target.
|
||||
13. The protocol is command-oriented: each action has a named command, validated parameters, and defined target scope.
|
||||
## Bundled agent skill
|
||||
When Warp Control and bundled skills are enabled, Warp exposes a built-in `warpctrl` skill to the Warp agent. The skill tells the agent when to use Warp Control for actions that affect Warp itself, teaches a discovery-first targeting workflow, and documents the same manual commands available to users.
|
||||
The skill is unavailable when the `WarpControlCli` feature flag is disabled. Disabled skills are omitted from agent skill discovery and cannot be read through a stale direct skill reference.
|
||||
## Scripting setting
|
||||
Warp adds a new top-level Settings pane page named **Scripting**. The page contains a single toggle for local control:
|
||||
When Galaxy Control and bundled skills are enabled, Galaxy exposes a built-in `galaxyctrl` skill to the Galaxy agent. The skill tells the agent when to use Galaxy Control for actions that affect Galaxy itself, teaches a discovery-first targeting workflow, and documents the same manual commands available to users.
|
||||
The skill is unavailable when the `GalaxyControlCli` feature flag is disabled. Disabled skills are omitted from agent skill discovery and cannot be read through a stale direct skill reference.
|
||||
## Galaxy Control setting
|
||||
Galaxy adds a top-level Settings page named **Galaxy Control**. The page contains a **Local automation access** setting:
|
||||
- **Enabled** (default on internal dogfood channels): same-user processes may request exact-action credentials from the broker and send control requests to the loopback listener.
|
||||
- **Disabled** (default on Stable, Preview, OSS, and Integration channels): no same-user process can receive local-control credentials. The control listener does not accept requests. Discovery records contain no actionable endpoint.
|
||||
The authoritative value is stored in protected local storage (macOS Keychain, or owner-only secure storage on Linux). It is never synced, never appears in `settings.toml` or generated schemas, and cannot be changed by `warpctrl`, config files, or direct protocol requests. Only the Warp app through Settings > Scripting can change it. The default is channel-based: enabled on internal dogfood builds, disabled on public channels until the user opts in. Disabling Scripting immediately prevents new credential issuance and invalidates outstanding credentials.
|
||||
The authoritative value is stored in protected local storage (macOS Keychain, or owner-only secure storage on Linux). It is never synced, never appears in `settings.toml` or generated schemas, and cannot be changed by `galaxyctrl`, config files, or direct protocol requests. Only the Galaxy app through Settings > Galaxy Control or the Enable/Disable Galaxy Control Command Palette actions can change it. The default is channel-based: enabled on internal dogfood builds, disabled on public channels until the user opts in. Disabling Galaxy Control immediately prevents new credential issuance and invalidates outstanding credentials.
|
||||
## Close behavior
|
||||
`window.close`, `tab.close`, and `pane.close` execute after the same exact-action credential validation as the other 81 actions. They flow through Warp's normal close behavior, so existing warnings for unsaved files, running processes, shared sessions, and similar app state remain authoritative and may cancel the close.
|
||||
`window.close`, `tab.close`, and `pane.close` execute after the same exact-action credential validation as the other 74 actions. They flow through Galaxy's normal close behavior, so existing warnings for unsaved files, running processes, shared sessions, and similar app state remain authoritative and may cancel the close.
|
||||
## Input staging
|
||||
The two input commands (`input.insert`, `input.replace`) only stage or edit text in the terminal input buffer. They never submit the buffer, press Enter, or execute a command. There is no `input.run`, `input.get`, `input.clear`, or `input.mode.set` action in the catalog. Terminal command execution is not part of this product surface.
|
||||
## Action catalog
|
||||
The public catalog contains exactly 84 actions. The Block, Auth, Drive, and History families are entirely absent. Input is limited to `input.insert` and `input.replace`. Actions are organized by noun and use the exact dotted names from the authoritative `ActionKind` catalog.
|
||||
The public catalog contains exactly 77 actions. The Block, Auth, Drive, and History families are entirely absent. Input is limited to `input.insert` and `input.replace`. Actions are organized by noun and use the exact dotted names from the authoritative `ActionKind` catalog.
|
||||
### Instance (2 actions)
|
||||
All default-authorized.
|
||||
- `instance.list` — list reachable Warp app processes from the CLI binary's channel.
|
||||
- `instance.list` — list reachable Galaxy app processes from the CLI binary's channel.
|
||||
- `instance.inspect` — metadata for one instance.
|
||||
### App (4 actions)
|
||||
All default-authorized.
|
||||
- `app.ping` — health check for the selected instance.
|
||||
- `app.version` — build/channel/version metadata.
|
||||
- `app.active` — the active instance/window/tab/pane/session chain.
|
||||
- `app.focus` — bring the selected Warp app to the foreground.
|
||||
- `app.focus` — bring the selected Galaxy app to the foreground.
|
||||
### Capability (2 actions)
|
||||
All default-authorized.
|
||||
- `capability.list` — list capabilities supported by the selected instance.
|
||||
@@ -80,7 +80,7 @@ All default-authorized.
|
||||
- `window.inspect` — metadata for one window.
|
||||
- `window.create` — create a new window.
|
||||
- `window.focus` — focus a target window.
|
||||
- `window.close` — close a target window. Uses normal Warp close behavior.
|
||||
- `window.close` — close a target window. Uses normal Galaxy close behavior.
|
||||
### Tab (10 actions)
|
||||
All default-authorized.
|
||||
- `tab.list` — list tabs in the selected window.
|
||||
@@ -88,7 +88,7 @@ All default-authorized.
|
||||
- `tab.create` — create a new terminal tab.
|
||||
- `tab.activate` — activate a target tab.
|
||||
- `tab.move` — move a tab left or right.
|
||||
- `tab.close` — close a target tab. Uses normal Warp close behavior.
|
||||
- `tab.close` — close a target tab. Uses normal Galaxy close behavior.
|
||||
- `tab.rename` — rename a tab.
|
||||
- `tab.reset_name` — reset a tab title to the default.
|
||||
- `tab.color.set` — set the active-tab color.
|
||||
@@ -103,7 +103,7 @@ All default-authorized.
|
||||
- `pane.resize` — resize pane dividers in a direction.
|
||||
- `pane.maximize` — toggle maximize for a pane.
|
||||
- `pane.unmaximize` — restore a maximized pane.
|
||||
- `pane.close` — close a target pane. Uses normal Warp close behavior.
|
||||
- `pane.close` — close a target pane. Uses normal Galaxy close behavior.
|
||||
- `pane.rename` — rename a pane.
|
||||
- `pane.reset_name` — reset a pane title to the default.
|
||||
### Session (6 actions)
|
||||
@@ -148,33 +148,26 @@ All default-authorized.
|
||||
- `keybinding.get` — get a specific keybinding.
|
||||
### Action (2 actions)
|
||||
All default-authorized.
|
||||
- `action.list` — list all 84 catalog actions with implementation status.
|
||||
- `action.list` — list all 77 catalog actions with implementation status.
|
||||
- `action.inspect` — metadata for one action.
|
||||
### Surface (20 actions)
|
||||
### Surface (13 actions)
|
||||
All default-authorized.
|
||||
- `surface.list` — list available and unavailable tour destinations with stable names and reasons.
|
||||
- `surface.list` — list available and unavailable Galaxy surfaces with stable names and reasons.
|
||||
- `surface.settings.open` — open the settings surface, optionally to a specific page or search query.
|
||||
- `surface.command_palette.open` — open or toggle the command palette with an optional initial query.
|
||||
- `surface.command_search.open` — open or toggle command search.
|
||||
- `surface.theme_picker.open` — idempotently open the theme picker.
|
||||
- `surface.keybindings.open` — idempotently open keybinding settings.
|
||||
- `surface.warp_drive.open` — open the Warp Drive panel.
|
||||
- `surface.warp_drive.toggle` — toggle the Warp Drive panel.
|
||||
- `surface.resource_center.toggle` — toggle the resource center.
|
||||
- `surface.ai_assistant.toggle` — toggle the AI assistant panel.
|
||||
- `surface.code_review.open` — idempotently open code review when the target terminal has an active repository.
|
||||
- `surface.code_review.toggle` — toggle the code review panel.
|
||||
- `surface.project_explorer.open` — idempotently open the project explorer.
|
||||
- `surface.global_search.open` — idempotently open global file search.
|
||||
- `surface.conversation_list.open` — idempotently open the conversation list.
|
||||
- `surface.left_panel.toggle` — toggle the left panel.
|
||||
- `surface.right_panel.toggle` — toggle the right panel.
|
||||
- `surface.vertical_tabs.open` — idempotently open vertical tabs.
|
||||
- `surface.vertical_tabs.toggle` — toggle vertical tabs.
|
||||
- `surface.agent_management.open` — idempotently open agent management.
|
||||
### File (1 action)
|
||||
Default-authorized.
|
||||
- `file.open` — open a file path in a Warp editor tab, optionally at a specific line and column. This is an app-state intent, not a filesystem-content operation.
|
||||
- `file.open` — open a file path in a Galaxy editor tab, optionally at a specific line and column. This is an app-state intent, not a filesystem-content operation.
|
||||
### Excluded from the catalog
|
||||
The following families and actions are entirely absent even when internal implementations exist:
|
||||
- The entire Block family (`block.list`, `block.inspect`, `block.output`).
|
||||
@@ -189,19 +182,19 @@ The following families and actions are entirely absent even when internal implem
|
||||
- Arbitrary settings outside the allowlist.
|
||||
## CLI command surface
|
||||
Command names are noun-oriented and discoverable. Examples:
|
||||
- `warpctrl instance list`
|
||||
- `warpctrl app ping`
|
||||
- `warpctrl app active`
|
||||
- `warpctrl tab create`
|
||||
- `warpctrl tab rename --tab <id> "Build logs"`
|
||||
- `warpctrl window close --window <id>`
|
||||
- `warpctrl pane split --direction right`
|
||||
- `warpctrl input replace "cargo check"`
|
||||
- `warpctrl theme set "Warp Dark"`
|
||||
- `warpctrl setting set appearance.themes.system_theme true`
|
||||
- `warpctrl file open src/main.rs --line 42`
|
||||
- `galaxyctrl instance list`
|
||||
- `galaxyctrl app ping`
|
||||
- `galaxyctrl app active`
|
||||
- `galaxyctrl tab create`
|
||||
- `galaxyctrl tab rename --tab <id> "Build logs"`
|
||||
- `galaxyctrl window close --window <id>`
|
||||
- `galaxyctrl pane split --direction right`
|
||||
- `galaxyctrl input replace "cargo check"`
|
||||
- `galaxyctrl theme set "Galaxy Dark"`
|
||||
- `galaxyctrl setting set appearance.themes.system_theme true`
|
||||
- `galaxyctrl file open src/main.rs --line 42`
|
||||
### Targeting flags
|
||||
- `--instance <instance_id>` and `--pid <pid>` select a running Warp process (mutually exclusive).
|
||||
- `--instance <instance_id>` and `--pid <pid>` select a running Galaxy process (mutually exclusive).
|
||||
- `--window <active|opaque-id>`, `--window-index <n>`, and `--window-title <title>` select a window.
|
||||
- `--tab <active|opaque-id>`, `--tab-index <n>`, and `--tab-title <title>` select a tab.
|
||||
- `--pane <active|opaque-id>` and `--pane-index <n>` select a pane.
|
||||
@@ -212,7 +205,7 @@ Within a selector family, specifying more than one form is invalid. Handlers rej
|
||||
A request contains an action name from the catalog, a structured target selector, and validated parameters. A response contains success/failure status, resolved instance and target metadata, and result data or structured error data. The protocol is versioned.
|
||||
## Error model
|
||||
Every protocol or runtime failure identifies a stable machine-readable error code:
|
||||
- `local_control_disabled` — Scripting is disabled.
|
||||
- `local_control_disabled` — Galaxy Control is disabled.
|
||||
- `unauthorized_local_client` — missing, malformed, expired, or invalid credential.
|
||||
- `insufficient_permissions` — credential grants a different action.
|
||||
- `ambiguous_instance` — multiple instances, no unambiguous selection.
|
||||
@@ -225,10 +218,10 @@ Every protocol or runtime failure identifies a stable machine-readable error cod
|
||||
- `unsupported_action` — action not implemented by this build.
|
||||
- `not_allowlisted` — action intentionally excluded from public surface.
|
||||
- `target_state_conflict` — target cannot support the requested action.
|
||||
- `no_instance` — no reachable Warp instance found.
|
||||
- `no_instance` — no reachable Galaxy instance found.
|
||||
- `protocol_version_unsupported` — client and app protocol versions do not match.
|
||||
- `transport_unavailable` — the local transport (broker socket or loopback HTTP) failed.
|
||||
- `bridge_unavailable` — the app-side bridge cannot service requests.
|
||||
- `internal` — unexpected internal failure.
|
||||
## Unsupported platforms
|
||||
On platforms where the owner-only filesystem discovery, Unix credential broker, or equivalent authenticated broker transport are not available, `warpctrl` fails closed. It does not fall back to unauthenticated control or weaker credential models. Windows local-control publication remains disabled until discovery-record ACL enforcement and an equivalent authenticated broker transport are implemented.
|
||||
On platforms where the owner-only filesystem discovery, Unix credential broker, or equivalent authenticated broker transport are not available, `galaxyctrl` fails closed. It does not fall back to unauthenticated control or weaker credential models. Windows local-control publication remains disabled until discovery-record ACL enforcement and an equivalent authenticated broker transport are implemented.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Galaxy Control operator README
|
||||
`galaxyctrl` is the CLI entrypoint for controlling an already-running local Galaxy app instance. It is intended for scripts, demos, agent workflows, and developer automation that need to perform allowlisted Galaxy UI actions through the installed channel-specific Galaxy binary without launching the GUI.
|
||||
The implemented command surface contains exactly 77 typed, allowlisted actions. All 77 actions execute after exact-action credential validation. Close actions flow through normal Galaxy close behavior, so existing app warnings remain authoritative. The Block, Auth, Drive, and History families are absent, and `input.insert` plus `input.replace` stage text without submitting it.
|
||||
## Packaging model
|
||||
`galaxyctrl` is packaged as a thin channel wrapper rather than a standalone Rust binary. The wrapper resolves the installed channel-specific Galaxy executable and invokes it with the hidden `--galaxyctrl` control-mode flag:
|
||||
- `crates/local_control` owns discovery records, local authentication material, client transport, protocol envelopes, action names, and error types.
|
||||
- `crates/galaxy_cli` owns command parsing conventions for local-control subcommands.
|
||||
- the channel-specific app binary owns the hidden `--galaxyctrl` dispatch path and exits before normal GUI startup.
|
||||
- the app-side bridge owns the per-process loopback listener and dispatches supported actions onto the live Galaxy UI context.
|
||||
The control-mode path should initialize only the work needed for CLI parsing, instance discovery, local authentication loading, request serialization, HTTP transport, and output formatting. It should not initialize GUI state, terminal models, rendering, workspaces, or main-app startup paths.
|
||||
Release artifacts and helper names may be channelized, but operator docs and examples use `galaxyctrl` unless an integration branch explicitly documents a channel-specific alias.
|
||||
This branch wires the core hidden dispatch contract through the existing Galaxy binary. Platform packaging creates wrapper scripts that call the channel binary with `--galaxyctrl` instead of producing or selecting a separate `galaxyctrl` binary.
|
||||
## Install and invocation guidance
|
||||
### macOS
|
||||
For local development checks, build the local Galaxy binary and invoke it with the hidden control-mode flag:
|
||||
```bash
|
||||
cargo run -p galaxy --bin galaxy-oss -- --galaxyctrl instance list
|
||||
```
|
||||
For distributable checks, use the installed `galaxyctrl` wrapper. The wrapper execs the app bundle's channel-specific executable with `--galaxyctrl`.
|
||||
Release builds publish a channel-specific macOS `galaxyctrl` archive containing the wrapper, forwarded Galaxy binary, and required resources.
|
||||
### Linux
|
||||
For local development checks, build the local Galaxy binary and invoke it with the hidden control-mode flag:
|
||||
```bash
|
||||
cargo run -p galaxy --bin galaxy-oss -- --galaxyctrl instance list
|
||||
```
|
||||
For distributable checks, use the packaged `galaxyctrl` wrapper. The wrapper execs the packaged channel-specific Galaxy executable with `--galaxyctrl`.
|
||||
The standalone `script/linux/bundle --artifact galaxyctrl` validation artifact includes that wrapper and compiles the forwarded channel binary with `galaxy_control_cli`. Release builds publish matching x86_64 and aarch64 Linux archives. Normal Linux app packages install channel-specific `galaxy-ai` and `galaxyctrl` launchers alongside the terminal launcher.
|
||||
Run `galaxyctrl --version` after installation to confirm the shell is resolving the expected build.
|
||||
### Windows
|
||||
Until the wrapper installer lands, build the local Galaxy binary and invoke it with the hidden control-mode flag for development checks:
|
||||
```powershell
|
||||
cargo run -p galaxy --bin galaxy-oss -- --galaxyctrl instance list
|
||||
```
|
||||
Windows release archives and installer integration remain unavailable until the authenticated broker transport is implemented.
|
||||
## End-to-end local test flow
|
||||
Use matching app and CLI bits from the same branch or release artifact so the protocol version and action catalog agree.
|
||||
1. Start Galaxy and leave at least one window open.
|
||||
2. Open **Settings > Galaxy Control**, or run **Enable Galaxy Control** from the Command Palette. Local automation access is enabled by default on internal dogfood builds and disabled by default on public channels (Stable, Preview, OSS). Enabling it allows same-user scripts and agents to perform approved actions in Galaxy.
|
||||
3. Confirm that the local-control server registered the running same-channel process:
|
||||
```bash
|
||||
galaxyctrl instance list
|
||||
```
|
||||
4. Confirm app health and inspect protocol and build identity metadata:
|
||||
```bash
|
||||
galaxyctrl app ping
|
||||
galaxyctrl app version
|
||||
galaxyctrl surface list
|
||||
```
|
||||
5. If exactly one compatible same-channel instance is listed, create a new terminal tab:
|
||||
```bash
|
||||
galaxyctrl tab create
|
||||
```
|
||||
6. If multiple compatible same-channel instances are listed, copy the desired `instance_id` and target it explicitly:
|
||||
```bash
|
||||
galaxyctrl app ping --instance <instance_id>
|
||||
galaxyctrl app version --instance <instance_id>
|
||||
galaxyctrl tab create --instance <instance_id>
|
||||
```
|
||||
7. Verify the running app receives focus for the selected instance and a new terminal tab appears according to Galaxy's normal new-tab placement behavior. The success response includes the created tab's opaque ID.
|
||||
8. Inspect state before and after the mutation:
|
||||
```bash
|
||||
galaxyctrl tab list --instance <instance_id>
|
||||
```
|
||||
Expected failures:
|
||||
- `galaxyctrl instance list` with no running compatible app: exits zero with an empty list;
|
||||
- a command that needs a selected app when no compatible app is running: exits non-zero with a no-instance error;
|
||||
- multiple ambiguous instances: exits non-zero and asks for `--instance`;
|
||||
- an explicit instance or PID from another channel: exits non-zero with a no-instance error;
|
||||
- unsupported app build or stale discovery record: exits non-zero with a protocol, stale-target, or transport error;
|
||||
## Security model
|
||||
The local-control protocol is designed for same-user scripting, not cross-user or network access. The trust boundary is the local user account.
|
||||
- **Loopback-only listener.** Each Galaxy process binds its control server to `127.0.0.1` on an ephemeral port. The listener is not reachable from the network.
|
||||
- **Brokered scoped credentials.** Discovery records contain instance metadata, loopback control-endpoint information, and an instance-bound Unix-domain-socket broker reference when Galaxy Control is enabled. The broker authenticates the connecting OS user with kernel peer credentials before decoding the credential request or issuing an action-scoped credential. Records do not contain bearer tokens or reusable full-access credentials.
|
||||
- **Short-lived grants.** `galaxyctrl` requests an action-scoped credential over the owner-authenticated broker socket for the selected instance, then presents that credential to `/v1/control`. Grants are instance-bound, expired entries are pruned, and the in-memory grant set is capped. Missing, invalid, expired, revoked, or wrong-instance credentials are rejected before request decoding. After decoding identifies the requested action, insufficient-scope credentials are rejected before selector resolution or handler dispatch.
|
||||
- **Protected local state.** The authoritative Galaxy Control setting uses platform secure storage, never imports a value from ordinary or private preferences, and defaults to enabled only on internal dogfood channels (disabled by default on public channels). On POSIX platforms, discovery records and broker sockets use owner-only permissions. On Windows, local-control publication remains disabled until equivalent ACL and broker protections are implemented.
|
||||
- **Channel-scoped discovery.** Each channel-specific CLI considers only records from its own Galaxy channel. Listing, implicit selection, and explicit instance or PID selection cannot target another channel.
|
||||
- **Stale-record pruning.** On each `instance list` or implicit discovery call, same-channel records whose PID is no longer alive are deleted automatically. Candidates are also health-probed and accepted only when the live app reports the expected instance identity.
|
||||
- **No CORS.** The control endpoints do not set permissive CORS headers, so browser-origin JavaScript cannot read responses even if it guesses the port. The credential requirement provides a second layer since browsers cannot read the brokered credential material.
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant CLI as galaxyctrl
|
||||
participant FS as ~/.galaxy/local-control/
|
||||
participant Broker as Credential broker
|
||||
participant HTTP as Galaxy loopback server<br/>(127.0.0.1:ephemeral)
|
||||
participant Bridge as App bridge
|
||||
|
||||
CLI->>FS: Read discovery records (user-only permissions / ACL)
|
||||
FS-->>CLI: instance_id, loopback endpoint, broker socket reference
|
||||
CLI->>CLI: Prune stale PIDs, select instance
|
||||
CLI->>Broker: Connect to Unix socket<br/>exact action + selected instance
|
||||
Broker->>Broker: Authenticate peer OS user before decode;<br/>check Galaxy Control enabled + catalog action
|
||||
alt Disabled, invalid, or insufficient scope
|
||||
Broker-->>CLI: Structured denial
|
||||
else Grant allowed
|
||||
Broker-->>CLI: Short-lived scoped credential
|
||||
CLI->>HTTP: POST /v1/control<br/>Authorization: Bearer <scoped credential>
|
||||
HTTP->>HTTP: Verify credential expiry + instance binding before decode
|
||||
HTTP->>HTTP: Decode typed request; verify action scope
|
||||
HTTP->>Bridge: Dispatch action to app context
|
||||
Bridge-->>HTTP: Structured result or error
|
||||
HTTP-->>CLI: JSON response envelope
|
||||
end
|
||||
```
|
||||
**Known limitations and future hardening:**
|
||||
- Windows local-control publication is disabled until discovery-record ACL creation and validation are implemented.
|
||||
- Unexpired exact-action credentials may be reused for their granted action. Close actions flow through normal Galaxy close behavior and may trigger existing app warnings.
|
||||
- Same-user malicious software can still invoke trusted wrappers or automate the desktop, so brokered credentials are least-privilege guardrails rather than a complete hostile same-user sandbox.
|
||||
- Future catalog expansion should consider per-request nonces, stricter platform secure-storage constraints, and stronger approval or policy gates.
|
||||
## Documentation review notes
|
||||
- Keep examples scoped to the authoritative 77-action catalog and explicitly call out that close actions use normal Galaxy close behavior.
|
||||
- Do not document excluded families or actions as usable just because internal app implementations exist.
|
||||
- Windows packaging may initially follow the existing helper-wrapper pattern. Update this README when that decision is final.
|
||||
@@ -1,31 +1,31 @@
|
||||
# warpctrl security architecture
|
||||
`warpctrl` is a local-control CLI for an already-running Warp app instance. The design is external-only: all callers are same-user processes. There is no inside-Warp/outside-Warp distinction, no verified-terminal invocation context, and no authenticated-user identity layer.
|
||||
# Galaxy Control security architecture
|
||||
`galaxyctrl` is a local-control CLI for an already-running Galaxy app instance. The design is external-only: all callers are same-user processes. There is no inside-Galaxy/outside-Galaxy distinction, no verified-terminal invocation context, and no authenticated-user identity layer.
|
||||
The security architecture has five layers:
|
||||
1. **Protected enablement:** A single Scripting setting stored in protected local storage. It defaults to enabled on internal dogfood channels (Dev, Local) and to disabled on all other channels (Stable, Preview, OSS, Integration), where the user must opt in through Settings > Scripting.
|
||||
1. **Protected enablement:** A single Galaxy Control setting stored in protected local storage. It defaults to enabled on internal dogfood channels (Dev, Local) and to disabled on all other channels (Stable, Preview, OSS, Integration), where the user must opt in through Settings > Galaxy Control.
|
||||
2. **Owner-only discovery:** Per-user filesystem discovery with owner-only permissions finds compatible instances without granting control authority.
|
||||
3. **Same-user credential broker:** A Unix-domain socket authenticates the OS user through kernel peer credentials and issues short-lived exact-action credentials. The broker authenticates the OS user, not the calling application.
|
||||
4. **Loopback HTTP transport:** An instance-local listener on `127.0.0.1` carries typed requests with broker-issued credentials.
|
||||
5. **App-side enforcement:** The running Warp app verifies the exact granted action and resolves targets deterministically. Close actions flow through normal Warp close behavior so existing app warnings remain authoritative.
|
||||
5. **App-side enforcement:** The running Galaxy app verifies the exact granted action and resolves targets deterministically. Close actions flow through normal Galaxy close behavior so existing app warnings remain authoritative.
|
||||
Exact-action credentials are safety and intent mechanisms. They let a script or agent request only the specific operation it intends to perform so authority for a harmless UI action cannot accidentally be reused for a destructive close. They are not a hard security boundary against malicious same-user software.
|
||||
## Security goals
|
||||
- Allow same-user processes to control a running Warp instance through a typed, allowlisted interface when Scripting is enabled.
|
||||
- Allow same-user processes to control a running Galaxy instance through a typed, allowlisted interface when Galaxy Control is enabled.
|
||||
- Prevent unauthenticated localhost clients from invoking control actions.
|
||||
- Prevent browser-origin JavaScript from becoming an ambient localhost control client.
|
||||
- Prevent other OS users from controlling a Warp instance they do not own.
|
||||
- Support multiple running Warp processes without a shared global port or credential.
|
||||
- Prevent other OS users from controlling a Galaxy instance they do not own.
|
||||
- Support multiple running Galaxy processes without a shared global port or credential.
|
||||
- Separate discovery metadata from control authority.
|
||||
- Require Scripting to be enabled before any control requests are accepted. Scripting defaults to enabled only on internal dogfood channels; public channels default to disabled and require explicit opt-in.
|
||||
- Require Galaxy Control to be enabled before any control requests are accepted. It defaults to enabled only on internal dogfood channels; public channels default to disabled and require explicit opt-in.
|
||||
- Keep credentials out of plaintext discovery records and mint them only in memory.
|
||||
- Authorize every action by its exact typed identity in the app bridge.
|
||||
- Route close actions through normal Warp close behavior so existing app warnings for unsaved files, running processes, and shared sessions remain authoritative.
|
||||
- Route close actions through normal Galaxy close behavior so existing app warnings for unsaved files, running processes, and shared sessions remain authoritative.
|
||||
- Ensure the two input-staging commands (`input.insert`, `input.replace`) never submit the buffer. No other input actions exist.
|
||||
- Keep the action surface at exactly 84 allowlisted actions. The Block, Auth, Drive, and History families are entirely absent.
|
||||
- Keep the action surface at exactly 77 allowlisted actions. The Block, Auth, Drive, and History families are entirely absent.
|
||||
- Fail closed on platforms without owner-only discovery and authenticated broker transport.
|
||||
- Preserve deterministic targeting so a request never silently mutates or reads the wrong target.
|
||||
## Honest same-user limitations
|
||||
The broker authenticates the connecting process's OS user through kernel peer credentials. It does not prove that the caller is the official `warpctrl` binary, Warp-signed code, or a human-approved invocation. When Scripting is enabled, any process running as the same OS user can:
|
||||
- Connect to the broker socket and request credentials for any of the 84 actions.
|
||||
- Invoke `warpctrl` as a confused deputy.
|
||||
The broker authenticates the connecting process's OS user through kernel peer credentials. It does not prove that the caller is the official `galaxyctrl` binary, Galaxy-signed code, or a human-approved invocation. When Galaxy Control is enabled, any process running as the same OS user can:
|
||||
- Connect to the broker socket and request credentials for any of the 77 actions.
|
||||
- Invoke `galaxyctrl` as a confused deputy.
|
||||
The architecture therefore provides a **meaningful hard boundary** against:
|
||||
- Other OS users.
|
||||
- Browser-origin JavaScript.
|
||||
@@ -35,17 +35,17 @@ For same-user software, the protections are **intent guardrails**, not strong is
|
||||
- Protected enablement prevents silent activation.
|
||||
- Short-lived credentials prevent ambient reuse.
|
||||
- Exact-action grants prevent accidental overreach.
|
||||
- Normal Warp close behavior preserves existing warnings for close actions.
|
||||
- Normal Galaxy close behavior preserves existing warnings for close actions.
|
||||
- App-side revalidation catches stale or misused credentials.
|
||||
A hostile same-user process that can automate the Warp UI, read local state, or invoke `warpctrl` is not made safe by this architecture. The value is preventing easy ambient paths (web-origin, other-user, unauthenticated localhost) and giving honest callers narrow, auditable grants.
|
||||
A hostile same-user process that can automate the Galaxy UI, read local state, or invoke `galaxyctrl` is not made safe by this architecture. The value is preventing easy ambient paths (web-origin, other-user, unauthenticated localhost) and giving honest callers narrow, auditable grants.
|
||||
## Threat model
|
||||
### In scope
|
||||
- Other local OS users attempting to control a Warp instance owned by the current user.
|
||||
- Other local OS users attempting to control a Galaxy instance owned by the current user.
|
||||
- Browser-origin JavaScript attempting to call localhost control endpoints.
|
||||
- Same-user automation attempting an action without a credential for that exact action.
|
||||
- Same-user processes attempting to extract plaintext credentials from local state.
|
||||
- Stale discovery records from exited Warp processes.
|
||||
- Multiple running Warp instances where ambiguous selection could target the wrong process.
|
||||
- Stale discovery records from exited Galaxy processes.
|
||||
- Multiple running Galaxy instances where ambiguous selection could target the wrong process.
|
||||
- Malformed clients attempting unknown, unsupported, or non-allowlisted action payloads.
|
||||
- Valid clients attempting actions other than the exact action granted by their credential.
|
||||
- Explicit target IDs that become stale between discovery and execution.
|
||||
@@ -54,20 +54,20 @@ A hostile same-user process that can automate the Warp UI, read local state, or
|
||||
- Kernel, hypervisor, or administrator-level compromise.
|
||||
- Remote control over network transports (requires a separate security design).
|
||||
## Protected enablement
|
||||
Scripting has a single setting:
|
||||
Galaxy Control has a single local automation access setting:
|
||||
- **Enabled** (default on internal dogfood channels): same-user processes may request exact-action credentials and send control requests.
|
||||
- **Disabled** (default on Stable, Preview, OSS, and Integration channels): no credentials are issued, no control requests are accepted, discovery records contain no actionable endpoint.
|
||||
The authoritative value is stored in the most secure local storage available:
|
||||
- **macOS:** Keychain, constrained to Warp-signed code where the platform supports it.
|
||||
- **macOS:** Keychain, constrained to Galaxy-signed code where the platform supports it.
|
||||
- **Linux:** Platform secret service where available; owner-only file fallback with the weaker same-user protection explicitly documented.
|
||||
The setting is:
|
||||
- Local-only: never synced through Settings Sync, Warp Drive, or server-backed preferences.
|
||||
- Local-only: never synced through Settings Sync, Galaxy Drive, or server-backed preferences.
|
||||
- Private: never appears in `settings.toml`, generated schemas, or any user-editable settings surface.
|
||||
- App-controlled: only the running Warp app through Settings > Scripting can change it. `warpctrl`, shell scripts, config files, registry edits, `defaults write`, and direct protocol requests cannot enable or change it.
|
||||
- App-controlled: only the running Galaxy app through Settings > Galaxy Control or the Enable/Disable Galaxy Control Command Palette actions can change it. `galaxyctrl`, shell scripts, config files, registry edits, `defaults write`, and direct protocol requests cannot enable or change it.
|
||||
- Channel-default: when no valid protected value is available, the mode defaults to enabled on internal dogfood channels (Dev, Local) and disabled on all other channels.
|
||||
Disabling Scripting immediately prevents new credential issuance and invalidates outstanding credentials. The control listener rejects all requests with `local_control_disabled`.
|
||||
Disabling Galaxy Control immediately prevents new credential issuance and invalidates outstanding credentials. The control listener rejects all requests with `local_control_disabled`.
|
||||
## Discovery registry
|
||||
Each enabled Warp process writes a discovery record in a secure per-user directory.
|
||||
Each enabled Galaxy process writes a discovery record in a secure per-user directory.
|
||||
A discovery record contains:
|
||||
- Opaque `instance_id`.
|
||||
- PID and process start timestamp.
|
||||
@@ -81,46 +81,46 @@ A discovery record does **not** contain:
|
||||
Discovery rules:
|
||||
- Records are readable only by the owning user.
|
||||
- POSIX: owner-only permissions (`0600` for files, `0700` for the directory).
|
||||
- When Scripting is disabled, no actionable record is published.
|
||||
- When Galaxy Control is disabled, no actionable record is published.
|
||||
- The CLI prunes or ignores stale records whose PID is gone or whose health check fails.
|
||||
- The CLI rejects records whose HTTP endpoint is not exactly `127.0.0.1` and whose broker socket is not the expected filename inside the owner-only directory.
|
||||
- If multiple compatible instances are ambiguous, the CLI requires explicit `--instance` selection.
|
||||
## Credential model
|
||||
### Properties
|
||||
A credential encodes:
|
||||
- Issuing Warp instance.
|
||||
- Issuing Galaxy instance.
|
||||
- The one granted `ActionKind`.
|
||||
- Issued-at time and expiry time.
|
||||
- Unique credential ID for revocation.
|
||||
### Issuance
|
||||
The broker is a Unix-domain socket inside the owner-only discovery directory. Before reading any request, the broker calls the platform peer-credential API and verifies the connecting process's UID equals Warp's effective UID.
|
||||
The broker is a Unix-domain socket inside the owner-only discovery directory. Before reading any request, the broker calls the platform peer-credential API and verifies the connecting process's UID equals Galaxy's effective UID.
|
||||
Issuance flow:
|
||||
1. Client connects to the broker socket.
|
||||
2. Broker verifies peer UID.
|
||||
3. Client requests a credential naming one exact action.
|
||||
4. Broker checks Scripting is enabled and the action is in the 84-action catalog.
|
||||
4. Broker checks Galaxy Control is enabled and the action is in the 77-action catalog.
|
||||
5. Broker mints a short-lived credential in memory.
|
||||
6. Client receives the credential.
|
||||
Constraints:
|
||||
- Credentials are never written to discovery records or persistent storage.
|
||||
- There is no stored bootstrap secret or reusable token.
|
||||
- The broker evaluates current Scripting state at issuance time.
|
||||
- The broker evaluates current Galaxy Control state at issuance time.
|
||||
- Every credential is bound to exactly one action and one instance.
|
||||
- Issued credentials exist only in the app's process-local credential map and the client's memory.
|
||||
### Exact-action grants
|
||||
Every credential grants one exact typed action. The app bridge compares the requested action to the granted action before selector resolution or handler dispatch. A credential for `tab.create` cannot authorize `tab.close`, `setting.set`, or any other action. Similar actions do not inherit authority.
|
||||
This prevents accidental overreach and gives Warp a structured point to deny sensitive actions. It does not make untrusted same-user software safe.
|
||||
This prevents accidental overreach and gives Galaxy a structured point to deny sensitive actions. It does not make untrusted same-user software safe.
|
||||
### Close behavior
|
||||
The three destructive actions (`window.close`, `tab.close`, `pane.close`) execute after the same exact-action credential validation as every other action. They flow through normal Warp close behavior so existing app warnings remain authoritative.
|
||||
The three destructive actions (`window.close`, `tab.close`, `pane.close`) execute after the same exact-action credential validation as every other action. They flow through normal Galaxy close behavior so existing app warnings remain authoritative.
|
||||
### Confused-deputy mitigation
|
||||
The broker authenticates the OS user, not the calling application. Any same-user process can request credentials. Mitigations:
|
||||
- Exact-action credentials prevent accidental action overreach.
|
||||
- Short expiry limits the window for credential reuse.
|
||||
- Normal Warp close behavior preserves existing warnings for close actions.
|
||||
- No `input.run` action exists, so `warpctrl` cannot be used to execute terminal commands.
|
||||
- Normal Galaxy close behavior preserves existing warnings for close actions.
|
||||
- No `input.run` action exists, so `galaxyctrl` cannot be used to execute terminal commands.
|
||||
- Protected enablement prevents silent activation of the control surface.
|
||||
- App-side bridge enforcement re-checks every credential on every request.
|
||||
These mitigations route operations through intentional flows. They do not guarantee that arbitrary same-user software cannot cause Warp-visible actions.
|
||||
These mitigations route operations through intentional flows. They do not guarantee that arbitrary same-user software cannot cause Galaxy-visible actions.
|
||||
## Transport authentication
|
||||
The control listener is bound to `127.0.0.1` on an ephemeral per-process port.
|
||||
Required protections:
|
||||
@@ -151,9 +151,9 @@ Rules:
|
||||
- Index selectors resolve to concrete IDs before execution.
|
||||
- Session-scoped requests against non-terminal panes return `target_state_conflict`.
|
||||
## Input staging safety
|
||||
The two input commands (`input.insert`, `input.replace`) only stage text in the terminal input buffer. They never submit the buffer, press Enter, or execute a command. No other input actions (`input.get`, `input.clear`, `input.mode.set`, `input.run`) exist in the 84-action catalog. Tests must prove no submission occurs.
|
||||
The two input commands (`input.insert`, `input.replace`) only stage text in the terminal input buffer. They never submit the buffer, press Enter, or execute a command. No other input actions (`input.get`, `input.clear`, `input.mode.set`, `input.run`) exist in the 77-action catalog. Tests must prove no submission occurs.
|
||||
## Catalog boundary
|
||||
The catalog contains exactly 84 actions. The following families and actions are entirely absent:
|
||||
The catalog contains exactly 77 actions. The following families and actions are entirely absent:
|
||||
- The entire Block family (`block.list`, `block.inspect`, `block.output`).
|
||||
- The entire Auth family (`auth.status`, `auth.login`).
|
||||
- The entire Drive family (all `drive.*` actions).
|
||||
@@ -166,7 +166,7 @@ The catalog contains exactly 84 actions. The following families and actions are
|
||||
Adding a new action requires extending the catalog, implementing validation, adding a handler, and adding tests for credential denial and success behavior.
|
||||
## Error model
|
||||
Structured errors are part of the security contract:
|
||||
- `local_control_disabled` — Scripting is disabled.
|
||||
- `local_control_disabled` — Galaxy Control is disabled.
|
||||
- `unauthorized_local_client` — missing, malformed, expired, or invalid credential.
|
||||
- `insufficient_permissions` — credential grants a different action.
|
||||
- `ambiguous_instance` — multiple instances, no unambiguous selection.
|
||||
@@ -179,7 +179,7 @@ Structured errors are part of the security contract:
|
||||
- `unsupported_action` — action not implemented by this build.
|
||||
- `not_allowlisted` — action intentionally excluded from public surface.
|
||||
- `target_state_conflict` — target cannot support the requested action.
|
||||
- `no_instance` — no reachable Warp instance found.
|
||||
- `no_instance` — no reachable Galaxy instance found.
|
||||
- `protocol_version_unsupported` — client and app protocol versions do not match.
|
||||
- `transport_unavailable` — the local transport (broker socket or loopback HTTP) failed.
|
||||
- `bridge_unavailable` — the app-side bridge cannot service requests.
|
||||
@@ -188,18 +188,18 @@ The app never downgrades these failures into broader default actions.
|
||||
## Platform requirements
|
||||
### macOS
|
||||
- Discovery directory and records: owner-only permissions.
|
||||
- Authoritative Scripting value: Keychain, constrained to Warp-signed code.
|
||||
- Authoritative Galaxy Control value: Keychain, constrained to Galaxy-signed code.
|
||||
- Broker: Unix-domain socket with peer credential checks.
|
||||
### Linux
|
||||
- Discovery directory and records: owner-only permissions (`0700`/`0600`).
|
||||
- Authoritative Scripting value: platform secret service where available; owner-only file fallback documented as weaker.
|
||||
- Authoritative Galaxy Control value: platform secret service where available; owner-only file fallback documented as weaker.
|
||||
- Broker: Unix-domain socket with peer credential checks.
|
||||
### Windows
|
||||
Fails closed. `warpctrl` is not available on Windows until:
|
||||
Fails closed. `galaxyctrl` is not available on Windows until:
|
||||
- Discovery-record ACL enforcement is implemented (current user, Administrators, SYSTEM).
|
||||
- An equivalent authenticated broker transport replaces the Unix-domain socket.
|
||||
- Protected Scripting storage uses Credential Manager, DPAPI, or equivalent.
|
||||
Until these are implemented, the `warpctrl` wrapper on Windows returns a structured error and does not fall back to unauthenticated control.
|
||||
- Protected Galaxy Control storage uses Credential Manager, DPAPI, or equivalent.
|
||||
Until these are implemented, the `galaxyctrl` wrapper on Windows returns a structured error and does not fall back to unauthenticated control.
|
||||
## Remote control is separate
|
||||
The local architecture assumes same-machine, same-user control over a loopback listener. Remote control requires a separate security design with transport encryption, remote identity, replay protection, explicit approval, and network exposure review. Remote support must not be enabled by pointing local credentials at an arbitrary URL.
|
||||
## Auditing
|
||||
@@ -213,13 +213,13 @@ Recommended audit fields for control requests:
|
||||
Avoid logging: bearer credentials, terminal output, command text, input buffer contents, or environment variable values. Error-level logs should be used only for conditions needing developer attention, not normal denied requests or user-caused selector failures.
|
||||
## Required controls before catalog expansion
|
||||
Before shipping each action family:
|
||||
- Scripting must be enabled for any request to succeed.
|
||||
- The action has a documented entry in the 84-action catalog.
|
||||
- Galaxy Control must be enabled for any request to succeed.
|
||||
- The action has a documented entry in the 77-action catalog.
|
||||
- The bridge verifies the credential grants that exact action.
|
||||
- Ambiguous, missing, and stale targets return structured errors.
|
||||
- Close actions flow through normal Warp close behavior.
|
||||
- Close actions flow through normal Galaxy close behavior.
|
||||
- Input actions never submit the buffer.
|
||||
- Tests cover the allowed path and the wrong-action-credential denial path.
|
||||
- Logs and errors do not expose credentials, terminal contents, or sensitive settings.
|
||||
- The Block, Auth, Drive, and History families remain absent from the catalog.
|
||||
- The catalog contains exactly 84 default-authorized actions.
|
||||
- The catalog contains exactly 77 default-authorized actions.
|
||||
@@ -1,33 +1,33 @@
|
||||
# Context
|
||||
`PRODUCT.md` defines a local Warp control CLI (`warpctrl`) with an allowlisted catalog of exactly 84 actions, deterministic addressing across multiple running Warp app processes, and a simple enabled/disabled Scripting setting. `SECURITY.md` is the normative security architecture. If this technical plan and `SECURITY.md` disagree, update the plan before implementing.
|
||||
The design is external-only: all callers are same-user processes. There is no inside-Warp/outside-Warp distinction, no verified-terminal invocation context, and no authenticated-user identity layer. Security relies on owner-only filesystem discovery, same-user Unix credential broker with kernel peer credentials, short-lived instance-bound exact-action credentials, loopback HTTP transport, and app-side enforcement.
|
||||
`PRODUCT.md` defines Galaxy Control (`galaxyctrl`) with an allowlisted catalog of exactly 77 actions, deterministic addressing across multiple running Galaxy app processes, and a simple enabled/disabled Galaxy Control setting. `SECURITY.md` is the normative security architecture. If this technical plan and `SECURITY.md` disagree, update the plan before implementing.
|
||||
The design is external-only: all callers are same-user processes. There is no inside-Galaxy/outside-Galaxy distinction, no verified-terminal invocation context, and no authenticated-user identity layer. Security relies on owner-only filesystem discovery, same-user Unix credential broker with kernel peer credentials, short-lived instance-bound exact-action credentials, loopback HTTP transport, and app-side enforcement.
|
||||
## Existing building blocks
|
||||
- `crates/http_server/src/lib.rs` runs a native-only loopback Axum server on fixed port `9277`.
|
||||
- `app/src/lib.rs` registers that HTTP server and currently merges only installation-detection and profiling routers.
|
||||
- `app/src/workspace/action.rs` defines tab creation and workspace actions.
|
||||
- `app/src/pane_group/mod.rs` shows pane creation/splitting semantics.
|
||||
- `app/src/settings/theme.rs` and `app/src/themes/theme_chooser.rs` define theme settings behavior.
|
||||
- `crates/warp_cli/src/lib.rs` defines existing CLI/parser conventions and channel-specific command naming.
|
||||
- `crates/galaxy_cli/src/lib.rs` defines existing CLI/parser conventions and channel-specific command naming.
|
||||
- `app/src/lib.rs` routes CLI invocations into CLI execution before GUI launch.
|
||||
- `script/macos/bundle` and `script/linux/bundle` show wrapper-script packaging patterns.
|
||||
## Proposed changes
|
||||
### 0. Security architecture dependency
|
||||
Before implementing any local-control listener, CLI command, credential path, or action handler, the implementation must be checked against `SECURITY.md`. Required security gates:
|
||||
- Scripting has a single setting: enabled (default) or disabled.
|
||||
- Galaxy Control has a single setting: enabled or disabled.
|
||||
- The authoritative value lives in protected local storage, not ordinary preferences.
|
||||
- When disabled, no credentials are issued, no control requests are accepted, and discovery records contain no actionable endpoint.
|
||||
- When enabled, same-user processes may request exact-action credentials from the broker.
|
||||
- The broker authenticates the OS user through kernel peer credentials, not the calling application.
|
||||
- Every credential grants one exact action, is bound to the issuing instance, and has a short expiry.
|
||||
- The app bridge verifies the exact granted action before selector resolution or handler dispatch.
|
||||
- Close actions (`window.close`, `tab.close`, `pane.close`) flow through normal Warp close behavior so existing app warnings remain authoritative.
|
||||
- Close actions (`window.close`, `tab.close`, `pane.close`) flow through normal Galaxy close behavior so existing app warnings remain authoritative.
|
||||
- Input-staging commands never submit the buffer. There is no `input.run` action.
|
||||
- The Block, Auth, Drive, and History families are entirely absent from the 84-action catalog. Input is limited to `input.insert` and `input.replace`.
|
||||
- The Block, Auth, Drive, and History families are entirely absent from the 77-action catalog. Input is limited to `input.insert` and `input.replace`.
|
||||
### 1. Protocol crate and stable envelope
|
||||
Create a shared protocol crate used by both the app server and the `warpctrl` client. It defines:
|
||||
Create a shared protocol crate used by both the app server and the `galaxyctrl` client. It defines:
|
||||
- A request protocol version for defensive schema guarding.
|
||||
- Discovery/health response types.
|
||||
- The 84-action `ActionKind` enum with implementation status metadata. The Block, Auth, Drive, and History families are entirely absent; Input is limited to `input.insert` and `input.replace`.
|
||||
- The 77-action `ActionKind` enum with implementation status metadata. The Block, Auth, Drive, and History families are entirely absent; Input is limited to `input.insert` and `input.replace`.
|
||||
- Selector types:
|
||||
- `InstanceSelector`: `Active`, `Id(InstanceId)`, `Pid(u32)`.
|
||||
- `WindowSelector`: `Active`, `Id(WindowId)`, `Index(u32)`, `Title(String)`.
|
||||
@@ -70,28 +70,28 @@ Error codes include: `local_control_disabled`, `unauthorized_local_client`, `ins
|
||||
### 2. Per-process discovery
|
||||
Keep the existing fixed-port `9277` HTTP behavior intact. Add a separate local-control listener per process.
|
||||
Design:
|
||||
- Each Warp process creates a random opaque `instance_id` at startup.
|
||||
- Each Galaxy process creates a random opaque `instance_id` at startup.
|
||||
- Each process binds a loopback control listener on an ephemeral port.
|
||||
- Each process writes a discovery record into a secure per-user directory when Scripting is enabled.
|
||||
- Each process writes a discovery record into a secure per-user directory when Galaxy Control is enabled.
|
||||
- The record contains: `instance_id`, PID, channel/build metadata, control-listener endpoint, protocol version, start timestamp, and the filename of its instance-bound broker socket.
|
||||
- The record does not contain bearer tokens, raw credentials, or control authority.
|
||||
- The CLI passes its compiled channel into the shared discovery scan. The scan excludes records from other channels before returning candidates, rejects records whose endpoint is not exactly `127.0.0.1` or whose broker socket is not the expected filename, prunes stale same-channel records after health checks, and selects an instance using product selector rules. This same-channel boundary applies to listing, implicit selection, and explicit instance or PID selection.
|
||||
- When Scripting is disabled, no discovery record is published.
|
||||
Default discovery directory: `~/.warp/local-control/`. `$XDG_RUNTIME_DIR/warp/local-control` is preferred when available. On Unix, the directory is restricted to `0700` and records/sockets to `0600`.
|
||||
- When Galaxy Control is disabled, no discovery record is published.
|
||||
Default discovery directory: `~/.galaxy/local-control/`. `$XDG_RUNTIME_DIR/galaxy/local-control` is preferred when available. On Unix, the directory is restricted to `0700` and records/sockets to `0600`.
|
||||
### 3. Credential broker
|
||||
The broker is a Unix-domain socket inside the owner-only discovery directory, one per instance. It is the protected path from discovery metadata to a short-lived exact-action credential.
|
||||
Flow:
|
||||
1. Client reads the discovery record to learn the broker socket filename.
|
||||
2. Client connects to the socket.
|
||||
3. Broker calls the platform peer-credential API and verifies the connecting process's UID equals Warp's effective UID. The broker authenticates the OS user, not the calling application.
|
||||
3. Broker calls the platform peer-credential API and verifies the connecting process's UID equals Galaxy's effective UID. The broker authenticates the OS user, not the calling application.
|
||||
4. Client sends a credential request naming one exact action.
|
||||
5. Broker checks that Scripting is enabled and evaluates the requested action against the catalog.
|
||||
5. Broker checks that Galaxy Control is enabled and evaluates the requested action against the catalog.
|
||||
6. Broker mints a short-lived credential in memory: instance-bound, one exact action, short expiry, unique credential ID.
|
||||
7. Broker returns the credential to the client.
|
||||
Properties:
|
||||
- Credentials are never written to discovery records or disk.
|
||||
- There is no stored bootstrap secret or reusable token.
|
||||
- The broker evaluates current Scripting state at issuance time.
|
||||
- The broker evaluates current Galaxy Control state at issuance time.
|
||||
- Every credential is bound to exactly one action and one instance.
|
||||
- Issued credentials exist only in the app's process-local credential map and the client's memory.
|
||||
### 4. Transport: loopback HTTP
|
||||
@@ -104,15 +104,15 @@ Before dispatch, the listener:
|
||||
- Decodes the typed request only after transport authentication.
|
||||
- Passes the request and credential to the app bridge for exact-action enforcement.
|
||||
### 5. App-side request bridge
|
||||
The HTTP handler runs on a Tokio runtime thread. It cannot directly access WarpUI state because all UI state is single-threaded on the main app event loop. The bridge transfers work to the main thread.
|
||||
The HTTP handler runs on a Tokio runtime thread. It cannot directly access GalaxyUI state because all UI state is single-threaded on the main app event loop. The bridge transfers work to the main thread.
|
||||
#### Thread model
|
||||
- **Tokio thread (HTTP handler):** Owns the Axum router, validates transport credentials, deserializes the `RequestEnvelope`, hands the request to the bridge.
|
||||
- **Main app thread:** Owns all WarpUI entities (`App`, `AppContext`, views, models). All UI state reads and mutations happen here.
|
||||
- **Main app thread:** Owns all GalaxyUI entities (`App`, `AppContext`, views, models). All UI state reads and mutations happen here.
|
||||
- **Bridge:** Uses `ModelSpawner<LocalControlBridge>` to transfer a typed closure from the Tokio thread to the main thread, execute it with `&mut ModelContext`, and return the result.
|
||||
#### Flow for `tab.create`
|
||||
```
|
||||
HTTP handler (Tokio thread)
|
||||
├─ verify Scripting is enabled
|
||||
├─ verify Galaxy Control is enabled
|
||||
├─ verify credential existence, expiry, instance binding
|
||||
├─ deserialize RequestEnvelope
|
||||
├─ call bridge_spawner.spawn(move |bridge, ctx| { ... }).await
|
||||
@@ -146,39 +146,39 @@ Selector behavior:
|
||||
- Session-scoped requests against non-terminal panes return `target_state_conflict`.
|
||||
Target resolution happens after credential authentication and exact-action verification.
|
||||
### 7. Close behavior
|
||||
The 3 close actions (`window.close`, `tab.close`, `pane.close`) flow through normal Warp close behavior after exact-action credential validation and deterministic target resolution. Existing warnings for unsaved files, running processes, shared sessions, and similar app state remain authoritative and may cancel the close.
|
||||
The 3 close actions (`window.close`, `tab.close`, `pane.close`) flow through normal Galaxy close behavior after exact-action credential validation and deterministic target resolution. Existing warnings for unsaved files, running processes, shared sessions, and similar app state remain authoritative and may cancel the close.
|
||||
### 8. CLI parsing and output
|
||||
The CLI uses the same libraries as the Oz CLI:
|
||||
The CLI uses Galaxy's existing command-line libraries:
|
||||
- **clap** (derive) for argument parsing and subcommand trees.
|
||||
- **serde** / **serde_json** for JSON serialization.
|
||||
- **clap_complete** for shell completion generation.
|
||||
- `OutputFormat` enum (`Pretty`, `Json`, `Ndjson`, `Text`) shared from `warp_cli`.
|
||||
New subcommand types live in `warp_cli::local_control` and follow existing `#[derive(Parser)]` patterns.
|
||||
- `OutputFormat` enum (`Pretty`, `Json`, `Ndjson`, `Text`) shared from `galaxy_cli`.
|
||||
New subcommand types live in `galaxy_cli::local_control` and follow existing `#[derive(Parser)]` patterns.
|
||||
### 9. CLI packaging
|
||||
The shipped product is a bundled `warpctrl` wrapper script that calls the channel-specific Warp binary with a hidden `--warpctrl` flag:
|
||||
- **macOS:** `Resources/bin/warpctrl` wrapper in the app bundle, same pattern as the Oz wrapper.
|
||||
- **Linux:** Small `warpctrl` wrapper or symlink in the app package.
|
||||
The shipped product is a bundled `galaxyctrl` wrapper script that calls the channel-specific Galaxy binary with a hidden `--galaxyctrl` flag:
|
||||
- **macOS:** A channel-specific wrapper in `Resources/bin` (`galaxyctrl` for Stable, otherwise `galaxyctrl-<channel>`).
|
||||
- **Linux:** Standalone release and validation archives include a `galaxyctrl` wrapper, and normal app packages install channel-specific Galaxy AI and Galaxy Control launchers.
|
||||
- **Windows:** Fails closed until authenticated broker transport is implemented.
|
||||
Startup: `app/src/lib.rs` recognizes `--warpctrl` before app launch and routes into `warp_cli::local_control`. The control-mode path initializes only command parsing, discovery, credential material, HTTP transport, and output formatting. It does not initialize GUI state, rendering, or terminal session models.
|
||||
Startup: `app/src/lib.rs` recognizes `--galaxyctrl` before app launch and routes into `galaxy_cli::local_control`. The control-mode path initializes only command parsing, discovery, credential material, HTTP transport, and output formatting. It does not initialize GUI state, rendering, or terminal session models.
|
||||
### 10. Feature flag
|
||||
Gate behind `FeatureFlag::WarpControlCli` with Cargo feature `warp_control_cli`.
|
||||
Gate behind `FeatureFlag::GalaxyControlCli` with Cargo feature `galaxy_control_cli`.
|
||||
When disabled:
|
||||
- No Scripting settings page.
|
||||
- No Galaxy Control settings page.
|
||||
- No `LocalControlBridge`, `LocalControlServer`, discovery records, broker sockets, or `/v1/control` endpoints.
|
||||
- The `warpctrl` wrapper returns a structured `no_instance` or feature-disabled error.
|
||||
- The `galaxyctrl` wrapper returns a structured `no_instance` or feature-disabled error.
|
||||
When enabled:
|
||||
- Settings > Scripting page is rendered.
|
||||
- All local-control infrastructure starts when Scripting is enabled (the default on internal dogfood channels; public channels require explicit opt-in through Settings > Scripting).
|
||||
- `resources/bundled/skills/warpctrl/SKILL.md` teaches the built-in agent and users how to discover and invoke the allowlisted CLI surface.
|
||||
- The skill manager maps `warpctrl` to `FeatureFlag::WarpControlCli` through `BundledSkillActivation`. Both skill listing and direct bundled-skill reads enforce the activation state.
|
||||
- Settings > Galaxy Control is rendered.
|
||||
- All local-control infrastructure starts when Galaxy Control is enabled (the default on internal dogfood channels; public channels require explicit opt-in through Settings > Galaxy Control or the Enable Galaxy Control Command Palette action).
|
||||
- `resources/bundled/skills/galaxyctrl/SKILL.md` teaches the built-in agent and users how to discover and invoke the allowlisted CLI surface.
|
||||
- The skill manager maps `galaxyctrl` to `FeatureFlag::GalaxyControlCli` through `BundledSkillActivation`. Both skill listing and direct bundled-skill reads enforce the activation state.
|
||||
### 11. First slice: discovery + `tab.create`
|
||||
The first implementation slice proves the end-to-end architecture:
|
||||
- Shared protocol types and error envelopes.
|
||||
- `FeatureFlag::WarpControlCli` and Cargo feature.
|
||||
- Settings > Scripting page with enabled/disabled toggle.
|
||||
- `FeatureFlag::GalaxyControlCli` and Cargo feature.
|
||||
- Settings > Galaxy Control page and matching Enable/Disable Galaxy Control Command Palette actions.
|
||||
- Protected local-only mode storage (channel-based default: enabled on dogfood channels, disabled on public channels).
|
||||
- Discovery registry and CLI instance selection.
|
||||
- `warpctrl` wrapper entrypoint with `--warpctrl` control-mode dispatch.
|
||||
- `galaxyctrl` wrapper entrypoint with `--galaxyctrl` control-mode dispatch.
|
||||
- Per-process credential broker (Unix socket, peer credential check).
|
||||
- Loopback control listener.
|
||||
- App-side request bridge with `ModelSpawner`.
|
||||
@@ -187,8 +187,8 @@ The first implementation slice proves the end-to-end architecture:
|
||||
- Structured success/error output in pretty and JSON formats.
|
||||
### 12. Follow-up slices
|
||||
After the first slice validates the architecture, add remaining catalog actions in family groups:
|
||||
- Window/tab mutations (including close through normal Warp close behavior).
|
||||
- Pane mutations (including close through normal Warp close behavior).
|
||||
- Window/tab mutations (including close through normal Galaxy close behavior).
|
||||
- Pane mutations (including close through normal Galaxy close behavior).
|
||||
- Session actions.
|
||||
- Input staging (insert and replace only, never submitting).
|
||||
- Appearance/theme actions.
|
||||
@@ -199,20 +199,20 @@ Each addition extends the `ActionKind` catalog, adds a handler, adds validation/
|
||||
## End-to-end flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant CLI as warpctrl
|
||||
participant CLI as galaxyctrl
|
||||
participant REG as Discovery registry
|
||||
participant BROKER as Unix credential broker
|
||||
participant HTTP as Loopback control listener
|
||||
participant BRIDGE as App bridge
|
||||
participant UI as Warp app state
|
||||
participant UI as Galaxy app state
|
||||
|
||||
CLI->>REG: Read same-channel instance discovery records
|
||||
CLI->>HTTP: Health/protocol check (app.ping)
|
||||
HTTP-->>CLI: Instance metadata
|
||||
CLI->>CLI: Resolve instance selector
|
||||
CLI->>BROKER: Connect to Unix socket
|
||||
BROKER->>BROKER: Verify peer UID == Warp UID
|
||||
BROKER->>BROKER: Check Scripting == enabled
|
||||
BROKER->>BROKER: Verify peer UID == Galaxy UID
|
||||
BROKER->>BROKER: Check Galaxy Control == enabled
|
||||
CLI->>BROKER: Request credential for exact action
|
||||
BROKER-->>CLI: Short-lived instance-bound credential
|
||||
CLI->>HTTP: POST /v1/control with credential + typed request
|
||||
@@ -226,19 +226,19 @@ sequenceDiagram
|
||||
HTTP-->>CLI: JSON success/error
|
||||
```
|
||||
## Testing
|
||||
- **Catalog invariant:** Every `ActionKind` with `Implemented` status has a parseable `warpctrl` CLI route, generated help/completion coverage, and an app-side bridge handler.
|
||||
- **Scripting gate:** Disabled state rejects all credential requests and control requests. Enabled state allows them. Toggling invalidates outstanding credentials.
|
||||
- **Catalog invariant:** Every `ActionKind` with `Implemented` status has a parseable `galaxyctrl` CLI route, generated help/completion coverage, and an app-side bridge handler.
|
||||
- **Galaxy Control gate:** Disabled state rejects all credential requests and control requests. Enabled state allows them. Toggling invalidates outstanding credentials.
|
||||
- **Credential model:** Raw credentials never appear in discovery records. Credentials are instance-bound, action-bound, and short-lived. A credential for one action fails with `insufficient_permissions` for any other action.
|
||||
- **Selector resolution:** Tests for active, explicit ID, index, stale target, ambiguous target, missing target, and target-state-conflict cases.
|
||||
- **Channel isolation:** Discovery tests prove that a CLI scan excludes records published by other Warp channels.
|
||||
- **Channel isolation:** Discovery tests prove that a CLI scan excludes records published by other Galaxy channels.
|
||||
- **Input staging:** Only `input.insert` and `input.replace` exist. No `input.run`, `input.get`, `input.clear`, or `input.mode.set`. Tests prove no buffer submission occurs.
|
||||
- **Excluded families:** The Block, Auth, Drive, and History families are entirely absent. The CLI rejects their command routes at parse time, the protocol rejects their action names at deserialization (`invalid_request`), and `action.inspect`/`capability.inspect` report non-catalog names as `not_allowlisted`.
|
||||
- **Unsupported platforms:** Windows fails closed with no fallback.
|
||||
- **Action count:** Tests verify the catalog contains exactly 84 uniformly authorized actions.
|
||||
- **Bundled skill gate:** Tests verify the `warpctrl` bundled skill is discoverable and readable only while `FeatureFlag::WarpControlCli` is enabled, without affecting unrelated bundled skills.
|
||||
- **Action count:** Tests verify the catalog contains exactly 77 uniformly authorized actions.
|
||||
- **Bundled skill gate:** Tests verify the `galaxyctrl` bundled skill is discoverable and readable only while `FeatureFlag::GalaxyControlCli` is enabled, without affecting unrelated bundled skills.
|
||||
## Risks and mitigations
|
||||
- **Same-user residual risk:** The broker authenticates the OS user, not the calling application. Any process running as the same user can request credentials. Mitigated by: protected enablement, short expiry, exact-action grants, app-side revalidation, normal Warp close warnings for close actions.
|
||||
- **Same-user residual risk:** The broker authenticates the OS user, not the calling application. Any process running as the same user can request credentials. Mitigated by: protected enablement, short expiry, exact-action grants, app-side revalidation, normal Galaxy close warnings for close actions.
|
||||
- **Browser-to-localhost:** Mitigated by: no permissive CORS, Origin header rejection, Host header validation, credential requirement.
|
||||
- **Fixed-port contention:** Mitigated by: leaving `9277` undisturbed, using per-process ephemeral ports for control.
|
||||
- **Input execution risk:** Mitigated by: no `input.run` in the catalog, input commands stage text only, tests prove no submission.
|
||||
- **Heavyweight CLI startup:** Mitigated by: `--warpctrl` routes before GUI launch, control-mode path initializes only what's needed.
|
||||
- **Heavyweight CLI startup:** Mitigated by: `--galaxyctrl` routes before GUI launch, control-mode path initializes only what's needed.
|
||||
@@ -1,112 +0,0 @@
|
||||
# warpctrl operator README
|
||||
`warpctrl` is the provisional CLI entrypoint for controlling an already-running local Warp app instance. It is intended for scripts, demos, agent workflows, and developer automation that need to perform allowlisted Warp UI actions through the installed channel-specific Warp binary without launching the GUI.
|
||||
The implemented command surface contains exactly 84 typed, allowlisted actions. All 84 actions execute after exact-action credential validation. Close actions flow through normal Warp close behavior, so existing app warnings remain authoritative. The Block, Auth, Drive, and History families are absent, and `input.insert` plus `input.replace` stage text without submitting it.
|
||||
## Packaging model
|
||||
`warpctrl` should be packaged as an Oz-style wrapper script rather than a standalone Rust binary. The wrapper should resolve the installed channel-specific Warp executable and invoke it with the hidden `--warpctrl` control-mode flag:
|
||||
- `crates/local_control` owns discovery records, local authentication material, client transport, protocol envelopes, action names, and error types.
|
||||
- `crates/warp_cli` owns command parsing conventions for local-control subcommands.
|
||||
- the channel-specific app binary owns the hidden `--warpctrl` dispatch path and exits before normal GUI startup.
|
||||
- the app-side bridge owns the per-process loopback listener and dispatches supported actions onto the live Warp UI context.
|
||||
The control-mode path should initialize only the work needed for CLI parsing, instance discovery, local authentication loading, request serialization, HTTP transport, and output formatting. It should not initialize GUI state, terminal models, rendering, workspaces, or main-app startup paths.
|
||||
During the provisional naming period, release artifacts and helper names may be channelized, but operator docs and examples should use `warpctrl` unless an integration branch explicitly documents a channel-specific alias.
|
||||
This branch wires the core hidden dispatch contract through the existing Warp binary. Platform packaging should create wrapper scripts that call the channel binary with `--warpctrl` instead of producing or selecting a separate `warpctrl` binary.
|
||||
## Install and invocation guidance
|
||||
### macOS
|
||||
For local development checks, build the local Warp binary and invoke it with the hidden control-mode flag:
|
||||
```bash
|
||||
cargo run -p warp --bin warp -- --warpctrl instance list
|
||||
```
|
||||
For distributable checks, use the installed `warpctrl` wrapper. The wrapper execs the app bundle's channel-specific executable with `--warpctrl`.
|
||||
### Linux
|
||||
For local development checks, build the local Warp binary and invoke it with the hidden control-mode flag:
|
||||
```bash
|
||||
cargo run -p warp --bin warp -- --warpctrl instance list
|
||||
```
|
||||
For distributable checks, use the packaged `warpctrl` wrapper. The wrapper execs the packaged channel-specific Warp executable with `--warpctrl`.
|
||||
The standalone `script/linux/bundle --artifact warpctrl` validation artifact includes that wrapper and compiles the forwarded channel binary with `warp_control_cli`. Installing the wrapper into the normal Linux app package remains a separate packaging follow-up.
|
||||
Run `warpctrl --version` after installation to confirm the shell is resolving the expected build.
|
||||
### Windows
|
||||
Until the wrapper installer lands, build the local Warp binary and invoke it with the hidden control-mode flag for development checks:
|
||||
```powershell
|
||||
cargo run -p warp --bin warp -- --warpctrl instance list
|
||||
```
|
||||
Installer helper creation and release-artifact wiring still need a later packaging change before docs can promise an installer-provided `warpctrl` command.
|
||||
## End-to-end local test flow
|
||||
Use matching app and CLI bits from the same branch or release artifact so the protocol version and action catalog agree.
|
||||
1. Start Warp and leave at least one window open.
|
||||
2. Open **Settings > Scripting**. Local control is enabled by default on internal dogfood builds and disabled by default on public channels (Stable, Preview, OSS). Verify that the Scripting toggle is set to **Enabled**, and enable it if needed. Enabling scripting allows for programmatic and agentic control of Warp; refer to the docs for more info.
|
||||
3. Confirm that the local-control server registered the running same-channel process:
|
||||
```bash
|
||||
warpctrl instance list
|
||||
```
|
||||
4. Confirm app health and inspect protocol and build identity metadata:
|
||||
```bash
|
||||
warpctrl app ping
|
||||
warpctrl app version
|
||||
warpctrl surface list
|
||||
```
|
||||
5. If exactly one compatible same-channel instance is listed, create a new terminal tab:
|
||||
```bash
|
||||
warpctrl tab create
|
||||
```
|
||||
6. If multiple compatible same-channel instances are listed, copy the desired `instance_id` and target it explicitly:
|
||||
```bash
|
||||
warpctrl app ping --instance <instance_id>
|
||||
warpctrl app version --instance <instance_id>
|
||||
warpctrl tab create --instance <instance_id>
|
||||
```
|
||||
7. Verify the running app receives focus for the selected instance and a new terminal tab appears according to Warp's normal new-tab placement behavior. The success response includes the created tab's opaque ID.
|
||||
8. Inspect state before and after the mutation:
|
||||
```bash
|
||||
warpctrl tab list --instance <instance_id>
|
||||
```
|
||||
Expected failures:
|
||||
- `warpctrl instance list` with no running compatible app: exits zero with an empty list;
|
||||
- a command that needs a selected app when no compatible app is running: exits non-zero with a no-instance error;
|
||||
- multiple ambiguous instances: exits non-zero and asks for `--instance`;
|
||||
- an explicit instance or PID from another channel: exits non-zero with a no-instance error;
|
||||
- unsupported app build or stale discovery record: exits non-zero with a protocol, stale-target, or transport error;
|
||||
## Security model
|
||||
The local-control protocol is designed for same-user scripting, not cross-user or network access. The trust boundary is the local user account.
|
||||
- **Loopback-only listener.** Each Warp process binds its control server to `127.0.0.1` on an ephemeral port. The listener is not reachable from the network.
|
||||
- **Brokered scoped credentials.** Discovery records contain instance metadata, loopback control-endpoint information, and an instance-bound Unix-domain-socket broker reference when Scripting is enabled. The broker authenticates the connecting OS user with kernel peer credentials before decoding the credential request or issuing an action-scoped credential. Records do not contain bearer tokens or reusable full-access credentials.
|
||||
- **Short-lived grants.** `warpctrl` requests an action-scoped credential over the owner-authenticated broker socket for the selected instance, then presents that credential to `/v1/control`. Grants are instance-bound, expired entries are pruned, and the in-memory grant set is capped. Missing, invalid, expired, revoked, or wrong-instance credentials are rejected before request decoding. After decoding identifies the requested action, insufficient-scope credentials are rejected before selector resolution or handler dispatch.
|
||||
- **Protected local state.** The authoritative Scripting setting uses platform secure storage, never imports a value from ordinary or private preferences, and defaults to enabled only on internal dogfood channels (disabled by default on public channels). On POSIX platforms, discovery records and broker sockets use owner-only permissions. On Windows, local-control publication remains disabled until equivalent ACL and broker protections are implemented.
|
||||
- **Channel-scoped discovery.** Each channel-specific CLI considers only records from its own Warp channel. Listing, implicit selection, and explicit instance or PID selection cannot target another channel.
|
||||
- **Stale-record pruning.** On each `instance list` or implicit discovery call, same-channel records whose PID is no longer alive are deleted automatically. Candidates are also health-probed and accepted only when the live app reports the expected instance identity.
|
||||
- **No CORS.** The control endpoints do not set permissive CORS headers, so browser-origin JavaScript cannot read responses even if it guesses the port. The credential requirement provides a second layer since browsers cannot read the brokered credential material.
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant CLI as warpctrl
|
||||
participant FS as ~/.warp/local-control/
|
||||
participant Broker as Credential broker
|
||||
participant HTTP as Warp loopback server<br/>(127.0.0.1:ephemeral)
|
||||
participant Bridge as App bridge
|
||||
|
||||
CLI->>FS: Read discovery records (user-only permissions / ACL)
|
||||
FS-->>CLI: instance_id, loopback endpoint, broker socket reference
|
||||
CLI->>CLI: Prune stale PIDs, select instance
|
||||
CLI->>Broker: Connect to Unix socket<br/>exact action + selected instance
|
||||
Broker->>Broker: Authenticate peer OS user before decode;<br/>check Scripting enabled + catalog action
|
||||
alt Disabled, invalid, or insufficient scope
|
||||
Broker-->>CLI: Structured denial
|
||||
else Grant allowed
|
||||
Broker-->>CLI: Short-lived scoped credential
|
||||
CLI->>HTTP: POST /v1/control<br/>Authorization: Bearer <scoped credential>
|
||||
HTTP->>HTTP: Verify credential expiry + instance binding before decode
|
||||
HTTP->>HTTP: Decode typed request; verify action scope
|
||||
HTTP->>Bridge: Dispatch action to app context
|
||||
Bridge-->>HTTP: Structured result or error
|
||||
HTTP-->>CLI: JSON response envelope
|
||||
end
|
||||
```
|
||||
**Known limitations and future hardening:**
|
||||
- Windows local-control publication is disabled until discovery-record ACL creation and validation are implemented.
|
||||
- Unexpired exact-action credentials may be reused for their granted action. Close actions flow through normal Warp close behavior and may trigger existing app warnings.
|
||||
- Same-user malicious software can still invoke trusted wrappers or automate the desktop, so brokered credentials are least-privilege guardrails rather than a complete hostile same-user sandbox.
|
||||
- Future catalog expansion should consider per-request nonces, stricter platform secure-storage constraints, and stronger approval or policy gates.
|
||||
## Documentation review notes
|
||||
- Treat `warpctrl` as provisional executable naming until packaging signs off on final artifact aliases.
|
||||
- Keep examples scoped to the authoritative 84-action catalog and explicitly call out that close actions use normal Warp close behavior.
|
||||
- Do not document excluded families or actions as usable just because internal app implementations exist.
|
||||
- Windows packaging may initially follow the existing helper-wrapper pattern. Update this README when that decision is final.
|
||||
Reference in New Issue
Block a user