horizon

Ochiq manbalardan

GPU bilan tezlashtirilgan terminal taxtasi boʻlib, barcha seanslaringizni cheksiz kanvasda joylashtiradi

Mashina tarjimasi · Asl matnni koʻrsatish

GPU-accelerated terminal board that puts all your sessions on an infinite canvas

IlovaKategoriya: Desktop ilovalar · Dasturlash · UtilitalarOpen sourcemaʼlumot tekshirilgan: 16-okt, 2026

Sharh

  • ai-agents
  • browser-automation
  • claude-code
  • codex
  • coding-agents
  • cross-platform
  • desktop-app
  • egui
  • gemini-cli
  • grok-build
  • infinite-canvas
  • kilo-code

Oʻrnatish

Buyruqlar faqat koʻrsatiladi, marketpleys ularni bajarmaydi. Rasmiy hujjatlar bilan solishtiring.

Moslik

«Nomaʼlum» dalil topilmaganini bildiradi, mahsulot mos emasligini emas.
PlatformaHolatArxitekturaDalilIshonchlilik
WindowsMosx86_64v0.2.7 relizidagi rasmiy yigʻma: horizon-installer-win-x64.exe0.70
macOSMosarm64v0.2.7 relizidagi rasmiy yigʻma: horizon-installer-osx-arm64.bin0.70
Mosx86_64v0.2.7 relizidagi rasmiy yigʻma: horizon-installer-osx-x64.bin0.70
LinuxMosx86_64v0.2.7 relizidagi rasmiy yigʻma: horizon-installer-linux-x64.bin0.70

Relizlar

tekshirilgan: 30-okt, 2026
v0.2.7Oxirgi2-avg, 2026Rasmiy manba ↗

0.2.7 versiyasi fayllari

Marketpleys bu fayllarni saqlamaydi: yuklab olish rasmiy manbadan. Biz ularni skanerlamaganmiz.
FaylPlatformaHajmSHA-256Rasmiy manbadan yuklab olish
horizon-installer-linux-x64.binlinux · x86_6442,7 MB7281daf39eb9…Rasmiy manba ↗
horizon-installer-osx-arm64.binmacos · arm6435,7 MBc78e15567247…Rasmiy manba ↗
horizon-installer-osx-x64.binmacos · x86_6437,7 MBfc2b4fa6b354…Rasmiy manba ↗
horizon-installer-win-x64.exewindows · x86_64 · exe40,4 MBc313d19b4023…Rasmiy manba ↗
horizon-linux-x64.tar.gzlinux · x86_64 · tarball24,4 MB003da3d33f77…Rasmiy manba ↗
horizon-osx-arm64.tar.gzmacos · arm64 · tarball22,1 MB08df537b4d56…Rasmiy manba ↗
horizon-osx-x64.tar.gzmacos · x86_64 · tarball22,5 MBabdd5d802376…Rasmiy manba ↗
horizon-windows-x64.exewindows · x86_64 · exe41 MB98cf142531fd…Rasmiy manba ↗
SHA256SUMS.txtnazorat summalari749 B62fd8703feb0…Rasmiy manba ↗

Relizlar tarixi

Sifat profili

algoritm quality-v1.0 · baholangan 16-okt, 2026
Sifat bahosi90
Xavf bahosi0
Ishonchlilik 1.00
Sifat bahosi va xavf bahosi — turli koʻrsatkichlar. Yuqori sifat xavfsizlikni kafolatlamaydi.

Baho tarkibi

Qoʻllab-quvvatlash · 2020
last activity 0 days ago
Hujjatlar · 1515
README 52288 chars, description present
Foydalanishga tayyorlik · 1515
install instructions: yes; releases: yes
Metamaʼlumotlar · 108
3/4 of description, homepage, license, topics
Relizlar · 1010
9 release(s), latest 74 days ago
Kategoriya aniqligi · 105
classifier confidence 0.5
Ommaboplik · 107
717 stars (log-dampened, 10k = max)
Litsenziya aniqligi · 55
SPDX MIT
Hamjamiyat · 55
issues enabled, 30 forks

Litsenziya

MIT
Litsenziya maʼlumrepozitoriy xostingi maʼlumotlariga koʻra

Bu katalog siyosati holati, yuridik xulosa emas.

Manbalar

github.com/peters/horizonAsosiy★ 717 yulduzfaollik: 15-okt, 2026Asosiy repozitoriy · Ishonchlilik 1.00

Sharhlar

Sharh yozish uchun kiring

Hali sharhlar yoʻq.

Muqobillar

README

peters/horizon dan · litsenziya MIT · yuklangan 30-okt, 2026

Why Horizon?

Tabbed terminals hide your work. Tiled terminals box you in. Horizon gives you a canvas — an infinite 2D surface where every terminal, agent, browser, VNC desktop, and editor lives as a panel you can place, resize, and group.

Think of it as a whiteboard for live sessions, with a structured workflow on top: color-coded workspaces, preset panels, a command palette, and fit-to-workspace whenever you want a clean overview.


The model

Horizon has five nouns. Everything else is a shortcut, a preset, or a panel kind.

NounWhat it isHow you use it
CanvasThe infinite 2D surfaceDrag empty canvas or swipe with two fingers to pan. Pinch or Ctrl+scroll to zoom. Minimap to jump.
WorkspaceA color-coded cluster with a shared working directoryCtrl+double-click the canvas and pick a preset, or click New in the sidebar. Arrange with Default (free), Rows, Cols, or Grid. Detach it to its own window.
PanelOne live surface inside a workspaceShell, SSH, coding agent, browser, markdown, git, or usage.
PresetA named template for a new panelCommand palette, Ctrl+double-click, or Ctrl+Shift+N (first preset).
SessionA saved boardClose Horizon and come back. Ctrl+Shift+J switches between saved boards.

A typical board looks like this:

Session
└── Canvas
    ├── Workspace "Backend"     cwd: ~/projects/api
    │     ├── Shell
    │     ├── Grok
    │     ├── Browser  →  http://127.0.0.1:3000
    │     └── Git Changes
    └── Workspace "Frontend"    cwd: ~/projects/web
          ├── Shell
          └── Claude

Workspaces stay visible. There are no hidden tabs. If something is off-screen, pan, zoom, fit, or search — do not hunt through a tab strip.

Panel kinds

KindWhat you get
shellLogin shell in the workspace directory
sshRemote shell — usually from the Remote Hosts overlay
grok · claude · codex · open_code · gemini · kilo_code · piFirst-class coding-agent TUIs, with session resume where the CLI supports it
browserLive Chromium, Firefox, or Safari on the canvas, shared with agents
editorMarkdown split view (syntax + preview)
git_changesChanged files, diffs, and hunks for the workspace repo
usageToken spend across agent panels
commandRun an arbitrary command as a panel
deviceNative VNC view of a loopback desktop, or of a remote desktop reached through an SSH tunnel (the host's key must already be trusted, for example by opening it over SSH once). Read-only until you turn Interact on, which sends your mouse and keyboard to that desktop

Five minutes on the canvas

You do not need a config file to start. Launch Horizon, then:

  1. Ctrl+double-click empty canvas. A preset list appears. Pick Shell (or Grok, Claude, Browser, …). Shell and agent presets then ask for a working directory — that becomes the workspace cwd, and the first panel lands there.
  2. Press Ctrl+Shift+N for another panel of the first preset (Shell by default), or Ctrl+Shift+K and type a preset alias (gb, cc, web, gc).
  3. On the workspace header, click Rows, Cols, or Grid when you want structure, or Default to drag freely.
  4. Press Ctrl+Shift+9 to fit the workspace into view. Press Ctrl+Shift+W to jump back to it without changing zoom.
  5. Press Ctrl+Shift+K again. Type a workspace name, a panel title, @ for panels only, or > for presets and actions.
  6. Close Horizon. Reopen it tomorrow — the session, layout, canvas pan/zoom, and terminal history are still there.

Once that loop is familiar, the rest is optional: New in the sidebar for an empty workspace, Ctrl+Shift+H for SSH/Tailscale hosts, the workspace Detach control for a dedicated window, a Browser panel for any site on this machine or on BrowserStack, a Device panel for each VNC desktop you want to watch, or Settings (Ctrl+Shift+,) to change settings in YAML form with the canvas still visible behind it.


More to explore

New here? First steps walks you from install to your first board, agent, browser and cloud.

  • Cloud workspaces — rent a RunPod or Hetzner worker and get its shell, agent, browser and Device panels on your board. Agents keep working when your laptop sleeps. Linux and macOS. See Cloud workspaces.
  • Local Network Bridge — let a cloud reach devices on your home or office network. See Local Network Bridge.
  • Casting — show a panel, a workspace or the whole window on an Apple TV. Linux only. See Casting.
  • Agent-driven desktops — agents click and type in an isolated X11 desktop while you watch it in a Device panel. Linux only. See Watch an app over VNC.

Highlights

Infinite Canvas

Pan and zoom freely. Place panels anywhere. A minimap in the corner keeps you oriented — click or drag it to jump.

Workspaces

Group related panels into color-coded workspaces. Auto-arrange with rows, columns, or grid — or drag freely. Detach a workspace into its own OS window.

Full Terminal Emulation

24-bit color, mouse reporting, scrollback, alt-screen, and Kitty keyboard protocol. Powered by the Alacritty terminal engine. Click a TUI (Grok, vim, less, …) and the click goes to the app; Shift+click still selects text.

Command Palette

Ctrl+Shift+K searches workspaces, panels, presets, and actions. Prefix > for presets and actions, @ for panels only. Selecting a panel pans the canvas to it.

AI Agent Panels

First-class Grok, Claude Code, Codex, OpenCode, Gemini CLI, KiloCode, and Pi integration. Session persistence and resume work where the CLI supports it. Title bars show a working indicator and, when a session is bound, its id. A live usage dashboard tracks token spend.

Live Browser

Open any site in Chromium, Firefox, or Safari on this machine, or on a BrowserStack browser or phone you name in config. On this machine you and an agent share the page: navigate, inspect, click, fill, capture network traffic, and hand control back and forth. A BrowserStack panel shows the live page and takes the same navigate, click, and fill actions.

Git Integration

A built-in git status panel watches the workspace repo. See changed files, inline diffs, and hunk-level detail without leaving the canvas.

Smart Detection

Click an OSC 8 hyperlink to open it. Ctrl+click a URL or file path under the cursor. Horizon sees what the terminal prints and makes it interactive.

Remote Hosts

Ctrl+Shift+H discovers hosts from SSH config and Tailscale. Search, filter, and connect. Type user@filter to override the SSH user.

The SSH | VNC switch (or Tab) picks what opening a host creates: a terminal over SSH, or a Device panel showing the host's desktop (read-only until you turn Interact on in the panel). VNC never crosses the network in the clear: Horizon runs ssh -W 127.0.0.1:<port> to the host and pipes that into its native VNC viewer, so the server only has to listen on the host's loopback interface (remote_hosts.vnc_port, default 5900). A host whose server listens elsewhere gets its own entry in remote_hosts.vnc_ports, keyed by the label the overlay shows or by its SSH host name; typing :port at the end of the filter (for example lab:5901) uses that port for one session, or for the shortcut being saved.

The in picker chooses the workspace that receives the session. It defaults to remote_hosts.default_workspace (Remote Sessions, created as a grid on first use); pick any other workspace for one session (Alt+↑/↓ cycles it from the keyboard), or Set default (Alt+D) to make it the new default in this Horizon's settings store. Set default waits while the Settings editor has unsaved edits, so the two never overwrite each other.

Right-click a host for Open over SSH, Open over VNC, Save SSH shortcut and Save VNC shortcut. A saved shortcut is an ordinary preset (SSH: <host> or VNC: <host>, with the user@ override applied) saved to this Horizon's settings store, so the command palette and the preset picker can add that host to any workspace later.

The same viewer also watches loopback VNC servers on this computer, one port per copy of an app. See Watch an app over VNC.

Live Settings Editor

Ctrl+Shift+, opens the config as a side panel with YAML highlighting and live preview. Toggle light / dark / auto themes; changes apply to the canvas behind the editor.

Session Persistence

Close Horizon, come back tomorrow. Sessions, panel layouts, canvas pan/zoom, and terminal history restore as you left them. Ctrl+Shift+J switches boards. An opt-in setting can line attached workspaces into a horizontal row in sidebar order after restore.

Markdown Editor

Drop a .md file onto the canvas or create one from the palette. Split view with syntax highlighting and live preview, saved with Ctrl+Shift+S.

Speech Input

Opt-in, on-device dictation into terminals and browser pages. Hold a push-to-talk key (default F9) or use the title-bar mic. Nothing leaves the machine.

Audited Browser Automation

Agents discover one MCP browser contract automatically. Every action is correlated in a redacted audit trail. The horizon-browser CLI runs prompt jobs and repeatable JSON plans.


Install

Download (fastest)

Grab the latest release from Releases — no dependencies needed.

PlatformDownload
Linux x64horizon-linux-x64.tar.gzExtract and run
macOS arm64horizon-osx-arm64.tar.gzExtract and run
macOS x64horizon-osx-x64.tar.gzExtract and run

To update a downloaded copy, download the new release and replace the executable after you close Horizon. For package-manager installs, use the package manager's upgrade command. Horizon has no in-app updater.

Release builds don't include speech input. Build from source if you want dictation.

The latest release (v0.2.7, August 2026) predates browser panels, Device panels, cloud workspaces and casting. Until the next release, build from source to get them.

What works where

Horizon runs on Linux and macOS, and some features are Linux-only today:

LinuxmacOS
Board, shell, browser and Device panels✓✓
Coding-agent panels✓✓
Safari browser panels—✓
Cloud workspaces✓✓
Agent-driven desktopsX11—
Apple TV casting✓—
Global push-to-talkX11✓

The full list, with the reason for each gap, is in Platform support.

Homebrew

Stable releases are available through the peters/horizon tap on macOS and Linux x64:

brew install peters/horizon/horizon

If you prefer to add the tap explicitly first:

brew tap peters/horizon
brew install horizon

To update or remove it later:

brew upgrade horizon
brew uninstall horizon
brew untap peters/horizon

Snap

Snap Store publishing is paused for now, so the store may hold an older release. Use another install method for the latest version. The snap is a classic snap for Linux x64:

sudo snap install horizon-ui --classic
snap refresh horizon-ui
snap remove horizon-ui

Classic confinement is intentional. Horizon launches host shells and host tools such as ssh, git, xdg-open, pgrep, lsof, and optional tailscale helpers, so a strict sandbox would compromise core workflows.

Build from source

git clone https://github.com/peters/horizon.git
cd horizon
git lfs install
git lfs pull
cargo run --release

Requires Git LFS for bundled assets and Rust 1.95+. AV1 recording builds on x86_64 also need NASM 2.15+ on PATH; ARM64 uses the platform C compiler's assembler. Linux needs system headers for GPU rendering — see AGENTS.md for installation commands.

The build checks required embedded fonts and stops if one is missing or still a Git LFS pointer. Run git lfs install and git lfs pull from the repository root, then rebuild. If a font is still missing, restore it from a complete checkout with LFS assets; source archives must also contain the hydrated fonts.


Keyboard and mouse

Most app shortcuts use Ctrl+Shift so they do not steal shell chords (Ctrl+C, Ctrl+K, Ctrl+B, …) or OS bindings. Canvas zoom keeps Ctrl/Cmd+0, Ctrl/Cmd+Plus, and Ctrl/Cmd+Minus. Bindings live in the shortcuts: block and in Settings. Duplicate or overlapping bindings are rejected, including near-conflicts such as Ctrl+B and Ctrl+Shift+B.

ShortcutWhat it does
Ctrl+Shift+KCommand palette — jump to a workspace, panel, preset, or action
Ctrl+Shift+NNew panel from the first preset
Ctrl+Shift+WFocus the active workspace at the current zoom
Ctrl+Shift+9Fit the active workspace into view
Ctrl+Shift+HOpen Remote Hosts overlay
Ctrl+Shift+JOpen the sessions picker
Ctrl+Shift+BToggle sidebar
Ctrl+Shift+UToggle HUD
Ctrl+Shift+MToggle minimap
Ctrl+Shift+AAlign visible attached workspaces into a horizontal row in sidebar order
Ctrl+Shift+,Open settings editor
Ctrl+Shift+FFocus the terminal search bar
Ctrl+0Reset canvas zoom to 100%
Ctrl+PlusZoom canvas in
Ctrl+MinusZoom canvas out
F11Fullscreen the active panel
EscapeExit active panel fullscreen
Ctrl+Shift+F11Toggle window fullscreen
Ctrl+Shift+SSave the active Markdown editor
Ctrl+Shift+CCopy the current terminal selection
Ctrl+Shift+VPaste into the focused terminal
Ctrl+Shift+RReconnect the focused disconnected SSH panel
InteractionWhat it does
Middle-mouse dragPan the canvas
Left-click drag on empty canvasPan; the gesture stays with the canvas when crossing panels
Space + Left-click dragPan the canvas
Two-finger swipe on empty canvasPan; swipes starting inside panels scroll their contents
PinchZoom around the cursor (Linux/X11 requires XInput 2.4 gesture support)
Minimap click-and-dragJump to that area of the canvas
Ctrl+ScrollZoom around the cursor
Click a panel headerFocus the panel while keeping the current pan and zoom
Double-click a panel headerRename the panel in place
Click in a mouse-reporting TUIDeliver the click to the app (Grok, vim, less, …)
Shift+Click/dragSelect terminal text while the app has mouse reporting
Middle-click a terminal (Linux)Paste the primary selection
Click an OSC 8 hyperlinkOpen the link in the default handler
Ctrl+ClickOpen URL, file path, or hyperlink under cursor
Ctrl+double-click canvasOpen the preset picker (creates a workspace and its first panel)
Ctrl+double-click inside a workspaceOpen the preset picker to add a panel

On macOS, substitute Cmd for Ctrl. Copy and paste use the standard Cmd+C / Cmd+V bindings. The SSH reconnect shortcut is contextual and is disabled if another global shortcut overlaps with Ctrl+Shift+R.

Programs in a terminal can copy with the OSC 52 escape sequence: Horizon writes it to the system clipboard (c) or, on Linux, the primary selection (p). On Linux, selecting text fills the primary selection. Under Wayland it reaches other apps through the compositor's data-control protocol (for example wlroots and KDE) or through XWayland; where neither exists, use Ctrl+Shift+C and Ctrl+Shift+V.


Configuration

Horizon keeps its settings in one SQLite file, ~/.horizon/settings.sqlite (in HORIZON_HOME when you set it). The settings editor reads and writes that store; you never edit a file by hand to change a setting. To read the settings as YAML, or to keep a copy in version control, run horizon --export-config [<path>]. Horizon does not read that copy again: change a setting in the Settings window or in the store. You can seed workspaces, panel presets, feature flags, and shortcuts, and the YAML below is the form that the editor shows and that an export writes.

Workspace templates use terminals: (each entry needs a name). Unknown keys are ignored, so a panels: block will not create anything.

appearance:
  theme: auto # auto, light, or dark

shortcuts:
  command_palette: Ctrl+Shift+K
  new_terminal: Ctrl+Shift+N
  focus_active_workspace: Ctrl+Shift+W
  fit_active_workspace: Ctrl+Shift+9
  open_remote_hosts: Ctrl+Shift+H
  toggle_sessions: Ctrl+Shift+J
  toggle_sidebar: Ctrl+Shift+B
  toggle_hud: Ctrl+Shift+U
  toggle_minimap: Ctrl+Shift+M
  align_workspaces_horizontally: Ctrl+Shift+A
  toggle_settings: Ctrl+Shift+Comma
  zoom_reset: Ctrl+0
  zoom_in: Ctrl+Plus
  zoom_out: Ctrl+Minus
  fullscreen_panel: F11
  exit_fullscreen_panel: Escape
  fullscreen_window: Ctrl+Shift+F11
  save_editor: Ctrl+Shift+S
  search: Ctrl+Shift+F

remote_hosts:
  default_workspace: Remote Sessions # receives SSH/VNC sessions from the Remote Hosts overlay
  vnc_port: 5900 # VNC server port on the host's loopback, reached through ssh -W
  vnc_ports: # per-host exceptions, by overlay label or SSH host name
    lab: 5901

workspaces:
  - name: Backend
    cwd: ~/projects/api
    terminals:
      - name: Shell
        kind: shell
      - name: Grok
        kind: grok
      - name: App
        kind: browser
        command: http://127.0.0.1:3000
      - name: Git
        kind: git_changes

  - name: Frontend
    cwd: ~/projects/web
    terminals:
      - name: Shell
        kind: shell
      - name: Claude
        kind: claude

presets:
  - name: Shell
    alias: sh
    kind: shell
  - name: Grok
    alias: gb
    kind: grok
  - name: Claude Code
    alias: cc
    kind: claude
    args:
      - --permission-mode
      - auto
  - name: Codex
    alias: cx
    kind: codex
    args:
      - --no-alt-screen
  - name: OpenCode
    alias: oc
    kind: open_code
  - name: Gemini CLI
    alias: gm
    kind: gemini
  - name: KiloCode
    alias: kc
    kind: kilo_code
  - name: Pi
    alias: pi
    kind: pi
  - name: Browser
    alias: web
    kind: browser
  - name: Git Changes
    alias: gc
    kind: git_changes
  - name: Markdown
    alias: md
    kind: editor
  - name: Usage
    alias: u
    kind: usage

features:
  # Optional: align attached workspaces whenever a session loads, including startup
  organize_workspaces_on_session_load: true
  # Optional: collapse inactive workspaces in the sidebar
  sidebar_accordion: true

features.organize_workspaces_on_session_load defaults to false. When enabled, Horizon performs the same horizontal alignment as Ctrl+Shift+A (sidebar order, left to right) whenever a restored session is ready, both at startup and after an in-app session switch; detached workspaces are unchanged. A short Preparing session view… overlay blocks root-window input while restored window geometry settles. The default-disabled path does not show this overlay. Changing the setting in the live editor takes effect when a session is next loaded.

features.sidebar_accordion defaults to false. When enabled, only the active workspace lists panels in the sidebar; other workspaces collapse to a name, accent, and panel count. Enable it from Settings → General → Features, or by setting the flag in the YAML of the settings editor. Like other General feature toggles, the Settings editor applies the change immediately as a live preview; save to persist it, or close/revert without saving to restore the previous value.

Use key names like Plus, Minus, Comma, Escape, and F11 in YAML instead of punctuation-only shortcut components such as Ctrl++.

Fresh configs already ship the agent, browser, git, markdown, and usage presets above. Existing configs gain missing defaults through migration (Grok, Pi, Browser, and the others) without overwriting presets you renamed.

Remote provider profiles

Remote provider settings are empty by default. Local Docker profiles require an explicit local Unix socket; Horizon does not infer a Docker context, environment variable or default endpoint. Profile names must be unique and match the saved environment's profile exactly, including letter case.

remote:
  local_docker:
    - name: development
      docker_host: unix:///path/to/docker.sock

Replace the example socket with your explicitly selected local daemon. The new block rejects unknown fields and remote TCP/SSH endpoints. Keep credentials out of this configuration. Loading or saving profiles does not connect to a provider, create a workspace, attach to a task, or stop/delete remote work. Empty settings are omitted when saving, so existing files need no migration for this feature. Provider-backed overview controls and workspace setup remain separate integration work.

Launch flags

FlagWhat it does
--config <path> / -c <path>Use an explicit settings location: the store is settings.sqlite in that folder, or <stem>.settings.sqlite for another name
--export-config [<path>]Write the settings as YAML and exit; the default path is the configuration path of this run
--ephemeralDo not persist this run
--new-sessionStart a new saved session from the current config
--blankStart with an empty board (combine with --ephemeral for a throwaway canvas)
--export-remote-profile <path>Write the shareable remote browser profile (providers, targets, credential references; no credential values or bindings) and exit
--import-remote-profile <path>Merge a portable remote browser profile into the settings store and exit (one profile command per launch); credentials are entered afterwards in Settings > Remote browsers, or bound in YAML to environment variable names for unattended launches

Machine-local credential_bindings name a store, never a secret. store: session and store: os_keychain are entered in Settings > Remote browsers. store: environment with slot: REMOTE_BROWSER_USERNAME (and a matching access-key variable) copies those values from the launching process at startup so a container or CI job can authenticate without a desktop keychain. Agent and terminal children inherit launch environment variables normally. Each reference selects its own variable, so multiple providers and credentials work together. See the configuration examples. The Horizon process, the container spec, and the parent's environment listing still show launch-time secrets; do not put values in the image, the profile, or argv.

docker run --rm \
  -e REMOTE_BROWSER_USERNAME \
  -e REMOTE_BROWSER_ACCESS_KEY \
  example/browser-test-runner

State folder

Horizon keeps its settings store, sessions, browser profiles, cloud settings and runtime files in ~/.horizon. Set HORIZON_HOME to an absolute path to keep them in another folder instead, for example to run a second Horizon that cannot touch your usual one:

HORIZON_HOME=/path/to/private-horizon horizon
  • If the folder is missing, Horizon creates it, readable only by you (mode 700 on Linux and macOS). Horizon refuses to start when the path is relative, is not a folder or is a symbolic link. On Linux and macOS it also refuses a folder that belongs to another user or whose permission bits let other users in. Horizon does not examine ACLs, so on macOS an ACL can still give other users access.
  • With HORIZON_HOME set, Horizon uses the settings of that folder only (settings.sqlite), never ~/.horizon, $XDG_CONFIG_HOME/horizon or the working directory. --config still wins.
  • HOME stays the same, so terminals in this Horizon keep your normal home directory. They inherit HORIZON_HOME, which is how agents in them find this Horizon's browser panels.
  • Horizon still adds its agent skills to the usual folders of Claude Code, Codex and the other agents in your home directory. To stop that, also set HORIZON_AGENT_INTEGRATIONS=off: Horizon then installs no agent skills, plugins or MCP registrations there, and on Linux no desktop entry. It does not remove what an earlier Horizon installed there, so agents can still find those skills. It also skips the Horizon plugin and the work-resume hooks in the state folder, so Claude Code panels in this Horizon have no Horizon browser or device tools and no work resume. Unset or empty means on. With any other value than on or off, the app does not start and exits with code 2.
  • An empty value is the same as an unset one.

Browse any website

A Browser panel is a real browser drawn on the canvas. Type an address and the page renders there. You and an agent share that same panel. Two places can run the page: a browser on this computer, or a browser or physical device on BrowserStack.

On this computer

  1. Ctrl+double-click empty canvas and pick Browser, or press Ctrl+Shift+K and type web.
  2. Click the address bar, type any site (https://example.com, a docs page, or http://127.0.0.1:3000), and press Enter.
  3. Use the backend menu on that bar to run the page in Chromium, Firefox, or, on macOS, Safari. On Linux, Horizon searches PATH for Chrome, Chromium, Edge, and Brave. macOS searches PATH and the usual browser .app bundles under /Applications, including Brave. Firefox also needs geckodriver on PATH, or browser.geckodriver_command pointing at it. Safari is disabled in that menu on Linux. On macOS, enable safaridriver with Apple's one-time steps; Horizon never runs safaridriver --enable for you.
  4. Back, forward, and reload sit on the same bar. Record writes a private WebM of the live page.

Chromium and Firefox start headless and paint into the panel. A panel you create keeps its own profile under ~/.horizon/browser-profiles. Duplicating a Chromium or Firefox panel shares that profile, including cookies. On Linux, Snap Chromium (/snap/bin/chromium) and Snap Firefox (/snap/bin/firefox, including Ubuntu's /usr/bin/firefox wrapper) use ~/Horizon/browser-profiles instead, so the Snap can read the profile. A workspace can open straight onto a URL:

workspaces:
  - name: Web
    terminals:
      - name: Docs
        kind: browser
        command: https://example.com

Some sites refuse a headless browser. For those, run headed Chromium with its native window minimized behind the panel:

browser:
  backend: chromium
  headless: false
  hide_native_window: true

That needs a working desktop. The native window can appear briefly at startup and stays in the taskbar or Dock. The embedded page keeps running while the Horizon panel is unfocused.

On BrowserStack

The same kind of panel can show a page running on BrowserStack: a desktop browser, or a physical phone. Horizon allocates the session, draws the live page, and releases the device when the panel closes.

Name the account and the targets in Settings → Remote browsers, or in the browser section of the settings YAML that the editor shows. The settings store credential references, never the access key. The example below binds both references to the OS credential store. Open Settings (Ctrl+Shift+,) → Remote browsers and enter the BrowserStack username and access key there. To keep the values only until Horizon exits, bind both references with store: session and omit slot. Settings offers This session only when a reference has no binding yet. The same tab shows that account's running, allowed, and queued sessions.

browser:
  remote:
    providers:
      browserstack:
        adapter: browserstack
        endpoint: https://hub-cloud.browserstack.com/wd/hub
        authentication:
          kind: basic
          username_ref: browserstack-user
          password_ref: browserstack-key
        credential_bindings:
          browserstack-user: { store: os_keychain, slot: remote-browser/browserstack/username }
          browserstack-key: { store: os_keychain, slot: remote-browser/browserstack/access-key }
    targets:
      windows_chrome:
        provider: browserstack
        browser_name: Chrome
        platform_name: Windows
        device: { kind: any, os_version: "11" }
        capability_extensions:
          bstack:options:
            os: Windows
      ios_phone:
        provider: browserstack
        browser_name: safari
        platform_name: iOS
        device: { kind: physical, model: iPhone 16, os_version: "18" }

device.kind: any skips the physical-or-emulated check. A model or OS version on the target is still checked against BrowserStack's session record. windows_chrome is a desktop browser because it names Chrome on Windows and leaves the phone model empty. device.kind: physical requires a real device, and Horizon keeps that panel only when the session record confirms it. Match browser_name, platform_name, os_version, and model to a combination your BrowserStack account offers. Two targets on this provider share the account. Each OS-store binding on one endpoint needs its own slot; Horizon rejects a repeated slot at that endpoint. A second provider on the same endpoint shares a username or access key when both bindings use store: session and the same reference name. Each distinct OS-store slot holds its own secret.

An agent panel in the workspace opens the target. Ask it for the page. It calls browser_create with target set to windows_chrome or ios_phone and url set to the address. Codex, Claude, and default Grok panels already have that tool. A shell panel does not: it has no browser identity, so browser_create from a shell cannot use this board's providers. The same call as a checked plan, run from the agent panel after cargo build -p horizon-browser-cli:

target/debug/horizon-browser run open-site.json
{
  "version": 1,
  "steps": [
    {
      "id": "open",
      "tool": "browser_create",
      "arguments": {
        "target": "ios_phone",
        "url": "https://example.com",
        "visible": true
      }
    }
  ]
}

The panel's top row names the BrowserStack browser and device once the session is ready. The address bar loads another page in that same session. Closing the panel releases the device. When a release stays unresolved, Settings → Remote browsers lists it under Remote allocations, and Reconcile checks that session with BrowserStack.

Local Chromium and Firefox panels also capture network traffic, record a WebM, and let you take the page until you press Done — hand back to agent. A BrowserStack panel shows the live page and is driven with the same navigate, click, and fill actions. The full provider schema, including a generic WebDriver grid and environment variables for CI, is in remote browser sessions.


Browser Panels

Browser panels render a real browser on the canvas through the first-party horizon-browser engine. Browse any website is the short path. This section is the engine, the profile, and the agent contract. Add a panel from the Browser (web) preset, or declare it in a workspace — command is the initial URL:

workspaces:
  - name: Web
    terminals:
      - name: App
        kind: browser
        command: http://127.0.0.1:3000

The panel is a shared human-and-agent session. It can:

  • navigate, reload, traverse history, wait for page state, and query or snapshot semantic DOM nodes;
  • click (including trusted double-click), fill, scroll, evaluate bounded JavaScript, and keep the URL while the page scrolls;
  • start visible or hidden, switch visibility without losing the session, and pause automation so you can steer before handing the same panel back;
  • capture bounded HTTP metadata and response bodies plus high-rate WebSocket frames on Chromium and Firefox;
  • reconnect an MCP client without restarting the browser, and keep a redacted action audit after the panel closes;
  • record the live page to a private WebM (Record in the chrome, or browser_video start/pause/resume/stop).

Safari shares the semantic action and audit surface but currently reports network capture as unsupported.

BackendAutomation and pixelsPrerequisitesImportant limits
ChromiumCDP with change-driven JPEG screencast framesChrome, Chromium, Edge, or BravePush frames; separate persistent profile per panel
FirefoxWebDriver BiDi plus adaptive lossless WebDriver screenshotsFirefox and geckodriverScreenshots, not a live BiDi video stream; capture decays to zero on a static page and is capped at 30 fps while active
SafariClassic WebDriver screenshots, with BiDi events when webSocketUrl is negotiatedmacOS, Safari, and an explicitly enabled safaridriverOne automation session at a time; isolated Safari automation state

Horizon scans the usual executable names and platform install locations. It never runs safaridriver --enable; follow Apple's one-time enablement flow yourself. Safari remains disabled in the picker on Linux.

browser:
  backend: chromium              # chromium, firefox, or safari
  command: /path/to/chrome       # Chromium-family binary; omit to discover
  firefox_command: /path/to/firefox
  geckodriver_command: /path/to/geckodriver
  safaridriver_command: /usr/bin/safaridriver
  extra_args: []                 # managed browser switches are rejected
  quality: 60                    # Chromium JPEG screencast quality, 1–100
  every_nth_frame: 1             # Chromium screencast sampling
  profile_root: ~/.horizon/browser-profiles
  video:
    quality: 90                  # WebM visual quality, 1–100
    compression_level: 4         # 0 fastest/largest … 10 slowest/smallest
    fps: 10                      # encoded frames per second, 1–30
    max_file_bytes: 536870912    # stop ingest at this WebM size

Recordings default to source-frame dimensions with codec-block alignment, bounded by a 3840-pixel longest side and 8,294,400 pixels (4K) to limit encoder memory and work. Larger sources are downscaled proportionally. Set browser.video.max_width (320–1920) to cap the longest encoded side without upscaling. Omitted per-recording options keep the host settings, including any explicit cap. These encoding options do not change the page viewport.

All executable fields are optional. Chromium and Firefox get separate directories under profile_root. Permanently closing the panel or deleting its saved session removes that panel's profile. Safari always uses Safari's isolated automation window and does not reuse your normal history, cookies, or preferences.

Chromium and Firefox remain headless by default. To use a headed Chromium compositor with its native window minimized, explicitly configure:

browser:
  backend: chromium
  headless: false
  hide_native_window: true

This option needs a working desktop and window manager. The native window can appear briefly during startup and remains in the taskbar or Dock. Horizon keeps the embedded document active and focused so animations and timers continue, even when the Horizon panel is unfocused. Initial setup fails if focus emulation, window lookup, or confirmed minimization fails. hide_native_window defaults to false and is ignored for headless Chromium and other backends. It does not promise that websites cannot detect automation.

Passkeys

A headless browser has no authenticator of its own, so a passkey sign-in in a panel normally goes nowhere. Point Horizon at an authenticator command and local Chromium and Firefox panels hand each passkey request to it:

browser:
  webauthn_command: ["/path/to/authenticator", "--profile", "work"]

Horizon, not the page, decides which site is asking. It refuses cross-origin iframes, plain http sites other than http://localhost, and relying party IDs that do not belong to the site. While the command waits for you (up to two minutes), the panel shows passkey request waiting and agents see passkey_pending in browser_panel. Leave the key out and nothing changes. Safari, BrowserStack and standalone horizon-browser CLI sessions keep the browser's own behavior. The command's JSON contract and the full set of rules are in the WebAuthn bridge reference.

Agent steering, audit, and CLI

Horizon-launched Codex, Claude, and default-command Grok agents receive the bundled horizon-browser MCP server automatically. Grok defaults to --no-leader so browser identity stays local to its panel (an explicit --leader remains an opt-out from this isolation); its existing login, home, session history, and permission settings are retained. Horizon adds an environment-based horizon-browser entry to Grok's config only when that name is unused, and preserves an existing custom entry or browser skill. Outside Horizon, that entry has no executable and cannot start a server; Grok may report it as an inactive configuration error. Custom-command Grok presets manage their own integration. Agents start with browser_list and use browser_create to open a visible browser in their own workspace when none exists. They reuse that panel for iframe, popup, dialog, and consent flows. A fresh user page action pauses the agent queue for five seconds; an explicit handoff keeps it paused until you select Done — hand back to agent. browser_handoff waits for that click before returning, so any MCP client — Codex, Claude, Grok, or horizon-browser CLI — resumes in the same turn.

OpenCode, Gemini CLI, KiloCode, and Pi still need dedicated browser integration. Each must forward the calling panel and host identity, load the shared handoff instructions, and allow the full human wait without changing provider authentication or session history.

MCP is the only agent-facing browser contract. Audit journals live under ~/.horizon/audit/browsers/ and redact credentials, query values, and typed text (stored as a character count). See the browser crate README for backend and embedding details.

For scripts, horizon-browser-cli exposes the same MCP tools three ways: a quoted goal (Grok if present, otherwise Codex), a deterministic run plan, or mcp as a standalone stdio server. Deterministic run jobs remain model-free and atomically publish a private job id, validated plan, and deadline-bound prepared lifecycle state together; runs that reach plan execution also persist a final report. After plan validation, durable preparation and MCP execution share a 30-minute default action deadline that can be changed with --timeout; timed-out reports preserve completed steps. Ctrl-C remains active while plan input is open and through preparation, MCP work, and final report delivery; it exits with code 130, gives durable cancellation a bounded flush grace, and cannot be trapped by blocked report output. An in-flight mutation is never claimed safe to replay.

cargo build -p horizon-browser-cli
target/debug/horizon-browser "Go to example.com, extract the heading, save to heading.txt"
target/debug/horizon-browser run browser-job.json --output browser-report.json
target/debug/horizon-browser mcp --backend firefox --visible

Watch an app over VNC

A Device panel is a native VNC viewer on the canvas. Use one panel per running copy of the app you are building, and keep those copies side by side while you test.

You start each copy, on its own display, with a VNC server for that display listening on loopback. Give every copy its own port, then add a preset for each port:

presets:
  - name: App A
    alias: va
    kind: device
    command: 127.0.0.1:5900
  - name: App B
    alias: vb
    kind: device
    command: 127.0.0.1:5901
  1. Start copy A so a VNC server accepts connections on 127.0.0.1:5900, and copy B on 127.0.0.1:5901. The address is a numeric loopback IP and a nonzero port: 127.0.0.1 or [::1]. Use a server that accepts a shared connection and does not ask for a VNC password. Horizon has no password field. On another machine, SSH is the access check.
  2. Ctrl+double-click empty canvas and pick App A. That creates a workspace with the first copy. Ctrl+double-click inside that workspace and pick App B, so both panels belong to it. Ctrl+Shift+K and va or vb adds a panel to the workspace you are in.
  3. Each panel connects and shows that desktop. Drag the panels apart, or use Rows, Cols, or Grid on that workspace. Ctrl+Shift+9 fits them on screen.
  4. The picture starts read-only. Turn Interact on in the panel you want to drive, then click the image. Your mouse and keyboard go to that desktop until you click outside the image or turn Interact off. The other panels stay viewers. Interact is a switch on the panel; agents leave it off.
  5. View controls on the panel choose Fit or 1:1, a crop, and local frame-rate and image-size limits. Those settings change the image in this panel. The desktop stays at the size the VNC server reports.
  6. After Horizon restarts, a restored Device panel stays disconnected until you press Reconnect. The same port may belong to a different process than the one you saved.

A desktop on another machine uses this viewer through SSH. Press Ctrl+Shift+H, switch to VNC, and open the host. Horizon runs ssh -W 127.0.0.1:<port> and requires the host key to already be trusted (open the host over SSH once). Per-host ports live in remote_hosts.vnc_ports. The overlay is described under Remote Hosts.

An agent in the workspace can open these viewers with the device_panel tool: create with the loopback endpoint, then inspect until the panel is connected and showing frames. On Linux, an agent drives a copy with horizon-device --target and a private target file that names that copy's X11 display:

{"id":"app-a","endpoint":{"kind":"local_x11","display":":99"}}

There is no fallback to the ambient DISPLAY. The Device panel does not hand its VNC address to that tool. The panel remains the view.

workspaces:
  - name: App under test
    terminals:
      - name: Copy A
        kind: device
        command: 127.0.0.1:5900
      - name: Copy B
        kind: device
        command: 127.0.0.1:5901

Speech Input (opt-in)

Dictate into a terminal, editor, or browser page. Terminal, Editor, and Browser panels get a mic button in the title bar (Git Changes and Usage do not). A Ventrilo-style push-to-talk hotkey (default F9, hold to record) dictates into the focused text-input panel. Audio is transcribed locally by transcribe.cpp — nothing leaves the machine — and the text is inserted as if typed. Browser dictation targets the page element that currently owns DOM focus. Editor dictation inserts at the caret.

Speech is a compile-time opt-in because it builds a native C++ inference library, so the release downloads and package-manager installs don't include it. You need CMake and a C++ compiler, plus on Linux the ALSA headers (libasound2-dev / alsa-lib-devel):

cargo speech          # alias for: cargo run --release --features speech  (CPU inference; Metal on macOS)
cargo speech-cuda     # NVIDIA GPU inference (needs the CUDA toolkit)
cargo speech-vulkan   # any GPU via Vulkan (needs the Vulkan SDK to build)

On Linux, capture routes through PulseAudio/PipeWire whenever that sound server is running, so the microphone stays shared with other apps. Without a sound server it falls back to raw ALSA, which claims the device exclusively.

features:
  speech:
    enabled: true
    model: /path/to/models/whisper-large-v3-Q8_0.gguf  # any transcribe.cpp GGUF
    language: "no"       # ISO hint; "auto" detects. Supported set = the model's GGUF metadata
    task: transcribe     # translate = speak any language, insert English text
    backend: auto        # auto | cpu | cuda | vulkan | metal
    input_device: ""     # microphone name; "" = system default
    hotkey: "F9"         # push-to-talk; same syntax as the shortcuts table, "" disables
    hotkey_mode: hold    # hold (Ventrilo-style) | toggle
    preload: false       # true = load the model at startup
    desktop_injection: false  # true = insert into another app (macOS or X11 Linux); no clipboard

The push-to-talk hotkey listens in focused Horizon windows, and panels in detached windows can also dictate via their title-bar mic button. A focused terminal, editor, or browser panel receives the transcript locally (PTY, caret, or page insertText). Git Changes and Usage have no text surface. With desktop_injection: true, the same hotkey is grabbed globally on macOS and X11 Linux. A focused Horizon window still receives the transcript locally, including when a global hotkey grab makes the toolkit briefly report the window as unfocused. On macOS, an external editable field is captured through Accessibility when recording starts and any later focus change discards the transcript. On Linux, insertion prefers the focused AT-SPI editable field when the transcript is ready; if the focused app does not expose one (Chromium, Electron, and Microsoft Teams typically do not), the transcript is typed through the accessibility key controller into the focused window. Classified password fields and visible selections are still refused. Both paths insert without reading or writing the clipboard, sending a paste shortcut, or pressing Return. Background push-to-talk is unavailable in a pure Wayland session.

Recommended models (prebuilt GGUFs under handy-computer on Hugging Face): whisper-large-v3-turbo (fast multilingual), whisper-large-v3 (multilingual with a working translate task), parakeet-tdt-0.6b-v3 (fast, 25 European languages). For Norwegian — including dialects — convert NB-Whisper Large to GGUF per the transcribe.cpp docs and set language: "no" (or "nn"). NB-Whisper normalizes dialect speech into standard written Norwegian and ignores the translate task; spoken-Norwegian → English text needs stock whisper-large-v3.

Settings → General → Features → Speech Input exposes all of this with a model-aware UI: languages from the GGUF metadata, a rebindable push-to-talk key, a microphone picker, and the actually-selected backend next to auto. Saved changes apply live.

Speech profiles: one key per language

Each profile has its own model, language, output, and push-to-talk key. Hold F1 for Norwegian, F2 for English, F3 to speak Norwegian and insert English. Models load lazily on first use unless preload: true.

features:
  speech:
    enabled: true
    backend: auto
    hotkey_mode: hold
    profiles:
      - name: Norsk
        model: ~/models/nb-whisper-large-Q8_0.gguf
        language: "no"
        task: transcribe
        hotkey: F1
        preload: true
      - name: English
        model: ~/models/whisper-large-v3-Q8_0.gguf
        language: en
        task: transcribe
        hotkey: F2
      - name: NO→EN
        model: ~/models/whisper-large-v3-Q8_0.gguf
        language: "no"
        task: translate
        target_language: en
        hotkey: F3

Profile hotkeys are validated against each other and against every global shortcut. The mic button uses the last-used profile; with no profiles: list, the flat model / language / hotkey fields act as a single profile.


Built With

RustEdition 2024, safe and fast
eframe / eguiImmediate-mode UI framework
wgpuGPU rendering — Vulkan, Metal, DX12, OpenGL
alacritty_terminalBattle-tested terminal emulation
CatppuccinTerminal palettes (Mocha dark / Latte light) on a warm editorial UI chrome

Contributing

See AGENTS.md for development setup, architecture, coding standards, and CI requirements. Release instructions live in docs/release-flow.md. Manual smoke-test plans live under docs/testing.

cargo fmt --all -- --check
cargo test --workspace
cargo clippy --all-targets --features speech,trace-profiling -- -D warnings

Repozitoriyni ochish ↗