UI control channel
The control channel lets a script or an agent look at and steer a running Postguy window. You can read which tabs are open, run a command, switch a panel, or take a screenshot. It is off by default. It exists only when you launch the app with --control FILE. You reach it with postguy-cli ui.
Launch
postguy --workspace ~/postguy-workspace --control /tmp/postguy-control.json
postguy-cli ui --control /tmp/postguy-control.json inspect
- The server binds to
127.0.0.1on an ephemeral port, never to another address. - A fresh random token is generated on every launch (64 hexadecimal characters). It is never logged.
- If the channel cannot start, the app still runs. It reports
control channel disabled: ...on stderr and in the status bar. - On exit the app deletes the control file, but only if it still contains this launch's token.
The control file
{"port": 53211, "token": "<64 hex characters>", "pid": 4242}
| Key | Meaning |
|---|---|
port | The loopback port. |
token | The bearer token for this launch. |
pid | The app's process id. |
Parent folders are created. A stale file is removed first, so a planted symlink is never followed. The new file is created with create_new and mode 0600 on Unix.
Protocol
The protocol is plain HTTP/1.1 over TCP, one request per connection (Connection: close). Every request needs this header:
Authorization: Bearer <token>
| HTTP | postguy-cli ui | Body or query |
|---|---|---|
GET /inspect | inspect | none |
POST /command | command ID | {"id": "<command id>"} |
POST /set | set KEY=VALUE | {"key": "...", "value": ...} |
GET /screenshot?path=<percent-encoded path> | screenshot --out FILE | query path |
POST /click | click X Y | {"x": 1, "y": 2} |
POST /key | key NAME | {"key": "..."} |
POST /type | type TEXT | {"text": "..."} |
Any other method or path is a 400 unknown control endpoint.
Status codes and limits
| Status | When |
|---|---|
200 | Success; the body is the JSON result. |
400 | Bad body, unknown endpoint, or the app refused the request (the body is {"error": "..."}). |
401 | Missing or wrong token: {"error":"missing or invalid control token"}. |
429 | More than 16 concurrent connections. |
503 | The UI queue is unavailable. |
504 | The UI did not answer within 20 seconds. |
- Request bodies are read up to 1 MiB.
- The server waits at most 10 seconds for the request to arrive.
- The
postguy-cli uiclient waits up to 30 seconds for the response. - The client connects to
127.0.0.1only and refuses a control file that has group or other permission bits on Unix.
inspect
inspect returns the state of the window:
{
"tabs": [{"id": 1, "title": "Welcome", "dirty": false}],
"active": 1,
"panel": "welcome",
"sidebar": true,
"console": false,
"right": false,
"twoPane": false,
"status": "",
"consoleRows": 0,
"engine": true
}
| Key | Meaning |
|---|---|
tabs | Open tabs with id, title and the unsaved-changes flag dirty. |
active | The active tab's id. |
panel | The kind of the active tab: request, environment, collection, settings, cookies, trash, runner, history or welcome. |
sidebar, console, right, twoPane | Whether the sidebar, the Console, the code snippet panel and the two-pane layout are on. |
status | The status bar text. |
consoleRows | Number of Console rows. |
engine | Whether the workspace engine is running. |
set
postguy-cli ui set KEY=VALUE parses VALUE as JSON and falls back to a plain string. So console=true sends a boolean, and url=https://example.com sends a string.
| Key | Value |
|---|---|
tab | settings, globals, cookies, trash or runner to open that tab; otherwise the title of an open tab (case-insensitive) to activate it. |
sidebar, console, right, two-pane | Boolean. Shows or hides the panel. |
theme | light, dark or system. Saves the setting. |
url | Sets the active request tab's URL and marks the tab as changed. |
open | A collection id (opens its detail tab) or a request id (opens the request). |
request-tab | params, authorization, headers, body, scripts, tests, settings or docs. |
detail-tab | overview, authorization, variables, scripts, runs or assets. |
An unknown key is refused with this error (400):
unknown key `x`; keys are tab, console, right, two-pane, sidebar, theme, url, open, request-tab, detail-tab
command
postguy-cli ui command ID runs one command from the app's command registry. This is the same table that drives the command palette, the keyboard shortcuts and the menus. It returns {"ok":true,"command":"<id>"}. An unknown id is an error. Shortcuts for these commands are on Keyboard shortcuts.
| Group | Command ids |
|---|---|
| Create | request.new, collection.new, environment.new, import.open, workspace.export |
| Request | request.send, request.cancel |
| Tabs | tab.close, tab.reopen, tab.next, tab.previous |
| View | view.sidebar, view.console, view.right, view.two-pane, console.clear, view.zoom-in, view.zoom-out, view.zoom-reset, view.fullscreen |
| Go | settings.open, globals.open, cookies.open, runner.open, trash.open |
| Edit | edit.undo, edit.redo, edit.cut, edit.copy, edit.paste, edit.delete |
| Window | window.minimize, window.zoom, window.fill, window.center, window.previous-size, window.close |
| Window placement | window.left-half, window.right-half, window.top-half, window.bottom-half, window.top-left, window.top-right, window.bottom-left, window.bottom-right, window.left-third, window.center-third, window.right-third, window.left-two-thirds, window.right-two-thirds |
| App and help | app.quit, help.docs, help.github |
window.close and app.quit close the app. help.docs and help.github open your browser. Commands act on the live window exactly as the matching menu item or shortcut would.screenshot
postguy-cli ui --control /tmp/postguy-control.json screenshot --out shots/home.png --root /tmp/work
The app captures the next frame, writes it as a PNG (mode 0600 on Unix, parent folders created) and replies {"ok":true,"path":"..."}.
- Only one screenshot can be in progress: a second request gets
a screenshot is already in progress. - If the windowing system does not deliver the frame within 10 seconds, the reply is
the screenshot did not arrive from the windowing system. --root DIRis applied by the CLI, to the--outpath only. A path outside the root is refused with the usualis outside the allowed directorymessage.- The app itself writes to whatever path it receives, so a direct HTTP client is not confined.
- Without
--root,--outis used as given.
click, key and type
The CLI accepts click X Y, key NAME and type TEXT (X and Y are numbers) and forwards them. The app refuses all three with a 400:
synthetic clicks are not supported; use `command` or `set`
synthetic keys are not supported; use `command` or `set`
synthetic typing is not supported; use `command` or `set`
postguy-cli ui exits 2 with ui: {"error":"..."}. The reason is that safe egui has no supported way to inject raw pointer and keyboard events into a running window. Use command and set instead. This is the first entry in Known gaps. f.ui-control is partial in Feature status.
Safety summary
- Off unless
--control FILEis given. - Loopback only, token on every request, token file owner-only on Unix, fresh token per launch.
- The channel never touches the network beyond
127.0.0.1. postguy-cli uinever guesses a token and never connects to another address.