# Control protocol Shape stroke defaults are drivable with `ui.set {shapeStroke: {width, align, cap, join, miterLimit, dashes, dashOffset, opacity}}`; partial fields merge, null disables the stroke. Values use the existing `shape.edit` names and units. Colour uses `tools.setColors`. `ui.inspect` exposes `toolOptions.shape_stroke`, `stroke_width`, and `strokeEditor`. Stroke Options applies valid UI edits immediately; `strokeEditor` is the open editor state, not an uncommitted document preview. Escape/outside clicks close it without reverting edits. See [Shape stroke options](shape-strokes.md) for limits, preview and persistence behavior. The desktop app listens on `127.0.0.1:` (loopback only). Start it with a token file so the credential is not exposed in the process command line: ```sh photocraft --control 7878 --control-token-file /private/path/photocraft-control.token \ --automation-read-root /work/project --automation-write-root /work/project ``` If the file does not exist, PhotoCraft creates it with a fresh 256-bit token. On Unix the new file is mode `0600`; on Windows, protect it with an appropriate user-only ACL. An existing file is reused. If neither a token nor token file is configured, PhotoCraft generates a token for that launch and writes it to standard error. `PHOTOCRAFT_CONTROL_TOKEN` and `PHOTOCRAFT_CONTROL_TOKEN_FILE` are the environment-variable equivalents. A flag wins over its variable and an empty variable counts as unset; with `--control` (or `PHOTOCRAFT_CONTROL_PORT`), a token together with a token file, from flags or variables, is a usage error (exit status 2). Without it no token is read, so the pair is ignored. The first request on every TCP connection must authenticate. No other method is dispatched before this succeeds. There are two ways; a refused frame gets `{"ok": false, "error": "authentication required"}` and the connection is closed. **Challenge authentication** (what `photocraft-cli mcp --bridge` uses) never sends the token, and the server proves it holds the token before the client reveals anything derived from it, so a process that took the port (the app is not running yet, or has exited) learns nothing: ```json {"id": 1, "method": "auth.challenge", "params": {"clientNonce": "<64 hex: 32 fresh random bytes>"}} {"id": 1, "ok": true, "result": {"serverNonce": "<64 hex>", "serverProof": "<64 hex>"}} {"id": 2, "method": "auth", "params": {"proof": "<64 hex>"}} {"id": 2, "ok": true, "result": {"authenticated": true}} ``` Both proofs are lowercase hex HMAC-SHA256, keyed by the token's 64 lowercase hex characters (as ASCII), over `