Getting Started
Web interface
The browser uses the same interactive controller and kernel as the TUI. It
starts in English with the approved dark layout. The workspace is fixed to the
canonical directory where latch starts; selecting a conversation never changes
that directory. Web support is included in Linux and Windows binaries since
v0.3.0, and Apple Silicon macOS binaries since v0.3.3.
Start locally¶
cd /path/to/project
latch --web
# Choose another port if 6006 is occupied:
latch --web --web-port 6007
The server binds only 127.0.0.1. It prints an access URL and opens the default browser with xdg-open on Linux,
open on macOS, or the Windows URL handler on Windows.
If no desktop browser is available, open the printed URL manually. Assets are
embedded in the binary: Python, Node, npm, and a separate frontend service are
not runtime requirements. Linux requires bwrap; Windows uses the embedded AppContainer runner. macOS uses Seatbelt. All three platforms
require Git and ripgrep for the corresponding tools. On Windows, use a
focused user-owned NTFS checkout. Ordinary source files use native file brokers
without ACL changes or WRITE_DAC; runtime bootstrap and scratch grants still
use journaled recovery. Starting the browser does not prove the workspace is
executable. See Windows qualification.
Config/provider/model/effort/mode/attachment overrides remain available. Resume
with latch --web --resume --latest or --resume --session <ID>. A bare
--resume opens the browser session picker without a terminal prompt. Only
sessions in the launch workspace can be selected. CLI --attach images are
retained through the browser picker and applied once to the first selected or
created session. Stop the server with Ctrl+C
in the launching terminal, or Settings → General → Stop Web server.
Manage conversation history¶
The sidebar searches conversation prompts, UUIDs, models and modes. Hover a row for its UUID, model and full update date. Choose Manage, select individual conversations or Select visible, then Delete selected. Selections survive search changes; the confirmation dialog lists the complete selection, including rows hidden by the current filter. Cancel leaves history unchanged. Failures stay in the dialog for retry. Batches are limited to 100 conversations.
Switch to another conversation before deleting the current one. The actor
serializes deletion with activation and rejects any batch containing the current
session. POST /api/sessions/delete accepts ids and instance_id, uses the
existing authentication and same-origin protections, and validates the whole
batch against the host workspace before committing. Invalid, unavailable or
out-of-workspace selections cause no partial deletion. Other tabs refresh their
history through the existing change notifications.
Deletion removes conversations from history and all normal resume selectors, including CLI UUIDs and prefixes. It persists across restarts. This is logical history deletion: events, child sessions, group projections, search records and artifacts remain on disk to preserve workspace validation and durable provenance. It does not reclaim disk space or erase stored content. Controllers already open in another process may finish recording events without restoring the history row.
The TUI uses the same kernel operation through /resume or latch --resume:
Ctrl+Space marks rows, Delete selects the highlighted row or all marked rows,
y confirms, and n/Esc cancels. Tab changes workspace scope; marks remain
selected across scope and search changes. The preview lists the deletion targets.
First-run setup¶
Startup resolves the configuration on the Latch host: --config <PATH> when
specified, otherwise ~/.latch/config.toml (an existing legacy XDG installation
continues to be read in place). A default interactive launch without that file
opens setup even if an OpenAI key is present in the environment. Legacy OpenAI
fallback values are not shown as a configured browser account. Missing
credentials also open provider settings. Replacing the active provider’s missing
credential unlocks the current session immediately when it resolves. Explicit provider/model/effort launch
overrides retain their existing behavior.
The setup dialog shows the actual configuration destination. Choose a provider,
enter an API key or reference a host environment variable, then choose a default
model, enabled models and reasoning effort from the shared catalog. All catalog
models are enabled by default using the provider's one credential. /model refresh
updates supported service catalogs; /model all clears intentional or legacy
local subsets without changing the active profile. Service entitlements and
quotas still apply. Custom
endpoints expose base URL, model ID and transport. API keys are stored privately
on the host; they are never returned to the browser. Saving uses the same
controller and validation as the TUI. On subsequent starts, Latch loads the saved
configuration automatically.
ChatGPT subscription is a separate setup choice: it starts /login instead
of asking for an API key. A transient dialog shows OpenAI's device verification
link, one-time code and cancellation control. Refresh/reconnect restores a
pending challenge; completion/cancellation removes it from browser metadata.
Tokens never cross HTTP/SSE to the browser and no authentication material enters
durable history. /logout removes local tokens while keeping configuration and
history. Plan eligibility/usage limits apply, without automatic API billing.
See ChatGPT sign-in.
Save feedback stays in the dialog. A rejected save retains the form for retry; a successful save clears the key and displays the updated provider. Availability refreshes update the current model list automatically. Unresolved models open the transport editor. Existing provider/model overrides, credentials, defaults, pricing and effort mappings remain editable; provider removal requires an explicit confirmation.
Activity¶
The left sidebar shows request preparation, waiting for model activity, provider-reported reasoning, response text, tool preparation/execution, pending approval, cancellation and the final turn outcome. It also shows elapsed phase time and time since the latest activity. Timers update while no text arrives. After 30 seconds without activity it offers a Stop reminder, without declaring that the model failed or is thinking. Reasoning text is never sent to this panel. A resumed unfinished turn is marked interrupted; replay does not restart it.
The TUI uses the same activity reducer in its sidebar and refreshes once per second during silence. The browser also receives a five-second connection heartbeat. A connection heartbeat does not reset model/tool activity timers or prove model progress. A finished turn is distinct from verified task completion, which remains in Overview. Closing the browser or losing the tunnel does not cancel execution.
Chat tables¶
Assistant replies, live streamed text and agent reports render Markdown tables with header cells and left/center/right column alignment. Escaped pipes and pipes inside inline code stay inside their cells. Wide tables scroll horizontally inside the transcript instead of widening the page; the scroll region supports keyboard focus. Markdown-looking tables inside fenced code remain code. All cell text is escaped before formatting.
SSH forwarding¶
On the remote Linux or Windows host (with an SSH server):
cd /path/to/project
latch --web --ssh 6006
--ssh <REMOTE_WEB_PORT> selects the remote listener port and suppresses
browser launch. Latch prints tunnel instructions; it does not start an SSH
client. On your local machine:
ssh -N -L 6006:127.0.0.1:6006 user@remote-host
# A different local port is also supported:
ssh -N -L 7000:127.0.0.1:6006 user@remote-host
Open http://localhost:7000 for the second example and enter the token printed
by the remote Latch process. Alternatively, append /#token=<TOKEN> to that
local address. All API/media/stream URLs are relative to the browser address.
Do not put the token in the SSH command. --ssh and --web-port require
--web, accept ports 1–65535, and cannot be used together. An occupied port is
an error, not an automatic port change.
Tools, configuration, credentials and model requests execute on the remote host. Image uploads originate in the local browser and are ingested into the active remote session's artifact store. Losing the tunnel or closing the page leaves an active turn running; reopening the page recovers its current state.
Everyday controls¶
- Enter sends; Shift+Enter inserts a line break. Send additional instructions while a turn runs, or use Stop to cancel. Up/Down at the composer boundary recalls prompt history. Ctrl/⌘+K starts a new conversation when idle.
- Select Ask/Plan/Work, a configured model and its supported reasoning effort. Missing initial configuration opens provider settings. Settings exposes providers, credentials, model overrides, safety and approval resolution. Configuration results appear in the settings dialog and conversation; HTTP acceptance alone is not reported as a successful save.
- Review pending approvals including the tool, complete arguments, reason and capabilities. Allow once/Deny resolve the kernel's pending request. A decision for an already resolved request is rejected.
- Tool output can be expanded, copied or inspected raw. Task details show
kernel task/evidence/usage records, change ownership, child agents and groups.
Workspace diff, checkpoint, undo, context inspection and context reset use
the existing kernel operations.
/helpopens the shared command catalog. - Attach PNG/JPEG/WebP files through the button, drag/drop, or
/attachwithout a path. Browser attachments do not accept server filesystem paths. The kernel validates format, structure, dimensions and the 5 MiB limit. Pending images survive browser refresh while the server runs; sent images are durable session media. A detached draft does not delete an already referenced artifact.
New/resume and profile/settings changes wait until the active turn finishes or is cancelled. Every connected tab controls the same active session. A stale tab must reload after another tab switches sessions. The server restart invalidates its authentication; use the new printed token. Unsent drafts, browser appearance, transient stream deltas and UI notices are not durable session history.
Transport and trust¶
The Linux/Windows adapter is crates/latch-cli/src/web/: mod.rs owns startup and
host lifetime, actor.rs serializes actions/session ownership, http.rs owns
routes/authentication/SSE, and state.rs maintains a disposable projection.
The host reuses one database connection for session listing and selection;
reading the session list does not reopen the database or rebuild group
projections alongside an active writer.
web/app/ contains the production assets; web/prototype/ remains a separate
fixture-based design example. latch-ui owns shared contracts, catalogs and
semantic reducers; cli/interactive.rs owns the shared session controller.
The public page serves an explicit embedded asset allowlist, never the workspace. API access requires an HttpOnly, SameSite=Strict cookie obtained by exchanging a random startup token. The token is removed from the URL fragment immediately. Host and Origin checks allow only the same loopback browser origin, including a different forwarded local port. There is no CORS or public listener option. Responses disable caching and apply a content security policy. Assistant/tool text is escaped before rendering; model output cannot inject HTML or scripts. Credentials are written through the existing configuration flow and are never returned by bootstrap/catalog responses.
Browser input is a typed latch-ui::Input request, scoped to an active session.
A command carries an instance ID, UUID and server-issued timestamp. Replays with
the same UUID/payload return the existing acceptance result; changed payloads,
wrong instances and requests older than five minutes are rejected. The bounded
command receipt cache refuses new commands if full, rather than forgetting a
still-valid receipt. These receipts coordinate transport, not durable completion.
The kernel still owns policy, validation, permissions, execution and persistence.
SSE sends an initial authoritative snapshot, small sequence-change signals, and connection heartbeats. The browser coalesces signals and fetches current state. A bounded broadcast ring resnapshots lagging observers; reconnect always starts with a full snapshot, regardless of Last-Event-ID. Sequence IDs are per server instance and are never SQLite event cursors. Durable transcript/sidebar state is reduced from kernel events on resume; transient assistant deltas belong only to the live server. Disconnecting an observer does not disconnect the controller. Streaming output is relayed in order so a full output channel cannot silently lose durable events.
Computer use remains a future plugin. This transport already carries validated session images and real permission requests; it grants no desktop-control capability and does not change the extension protocol.
Verification¶
cargo test -p latch-cli --test cli web_transport --locked
cargo test -p latch-ui -p latch-tui --locked
bash scripts/release-gate.sh
The Linux real-binary tests use an isolated loopback mock provider and state store, covering authentication/origin/Host checks, forwarded authority, embedded assets, command retries, durable resume, configuration errors, approvals, busy session switches, image validation/media boundaries, steering/reconnect, cancellation, shutdown, default-path setup/restart and argument conflicts. No live provider credential is needed. Native Windows qualification and its remaining gaps are tracked in Windows diagnostics.
Optional frontend regression checks use production assets with an isolated transport fixture in Chromium (Node 22+; no npm packages or provider calls):
node scripts/test_web_markdown.mjs
node scripts/test_web_setup.mjs /path/to/chromium
node scripts/test_web_sessions.mjs /path/to/chromium
This checks first-run and configured startup, credential modes, rejected/pending saves, model selection, live refresh, preserved drafts, custom models and mobile layout, session selection across filters, deletion confirmation/cancel/retry and current-session protection, plus chat table alignment, escaping, streaming and scrolling. The Markdown parser checks need only Node; browser layout checks need Chromium. Both are development-only dependencies.
TUI control parity¶
| TUI function | Connected browser control |
|---|---|
| Submit, steer, cancel | Composer, additional instructions, Stop |
| Resume and prompt history | Workspace-scoped conversation list, Up/Down |
| Mode, model, reasoning effort | Composer mode selector, model/effort dialog |
| Provider setup and model discovery | Settings → Providers, Refresh availability, advanced model edits |
| Safety and permission resolver | Settings → Safety |
| Pending human permission | Complete request details, Allow once/Deny |
| Attach/list/detach images | Attachment button/drop, draft chips, /attachments, /detach |
| Semantic/raw tool output | Expandable tool cards, raw inspection, copy |
/diff, /checkpoint, /undo |
Changes panel and slash commands |
/context, /compact |
Overview actions and slash commands |
| Canonical task, evidence, usage/cost | Overview and expandable recorded state |
Child agents, /group |
Agents panel and shared group overview |
/help, /raw, /sidebar, /quit |
Command catalog, raw transcript toggle, details toggle, server shutdown |
The browser implements its own focus, layout and shortcuts. The shared catalog, controller and semantic reducers determine actions and displayed kernel records; there are no fixture-driven success paths in production assets.