# Offsite documentation Complete contents of https://docs.offsite.sh, one page per section. # Offsite for agents Source: https://docs.offsite.sh/agents/for-agents/ A dense, single-page briefing for AI agents asked about Offsite or running inside it. This page is written for AI agents. If you are a human, everything here is covered in more depth elsewhere in these docs. ## What Offsite is An agentic development environment. A **host** (macOS or Linux) runs coding-agent and terminal sessions inside tmux; native **clients** (macOS, iOS, watchOS) attach to them live. Sessions are host-owned, so they survive client disconnects, network changes, sleep and reboot. Offsite starts the real vendor CLIs — `claude`, `codex`, `grok` — under the user's own subscription. It is not a wrapper and not an API proxy; no token passes through Offsite. ## Requirements | | | |---|---| | Host | macOS 14+ on Apple Silicon, or Linux (glibc ≥ 2.28 — Ubuntu 20.04+/Debian 10+) | | Linux CPU caveat | Claude Code needs **AVX2** on x86_64. Check: `grep -q avx2 /proc/cpuinfo`. Codex and Grok do not. | | Host needs | `tmux`, `git`, `python3`, at least one agent CLI installed and logged in | | Clients | macOS 14+, iOS 18+ | | Not required | an account, Remote Login/`sshd`, an inbound port, an admin password for standard setup | ## Install The Mac app is at [offsite.sh](https://offsite.sh/download/macos); it sets up This Mac as a host, and remote Macs and Linux hosts over SSH. Full walkthrough: [Install & set up](/start/install/). ## Feature map | Capability | Page | Host requirement | |---|---|---| | Agent sessions (Claude/Codex/Grok), permission modes | [Agent sessions](/work/agent-sessions/) | any | | Terminal sessions | [Sessions](/work/sessions/) | any | | Git review, stage, commit, push | [Git card](/cards/git/) | any | | File browse and edit (host-side tabs and drafts; the user sees your writes live) | [Files card](/cards/files/) | any | | The user's own browser — no agent access, but they can mark a page and send it to you | [Browser card](/cards/browser/) | any | | Browser you drive with the `web_*` tools, docked to your card | [Agent Browser](/cards/agent-browser/) | any (needs Chromium on the host) | | iOS Simulator mirror | [Simulator card](/cards/simulator/) | macOS | | Install an iOS build on the user's phone | [Deploy to this phone](/cards/deploy-to-phone/) | macOS | | Screen control for a session | [Computer Use](/computer-use/computer-use/) | macOS | | Read another session, hand context to a new one | [Hand off and ask](/work/handoff-and-ask/) | any | | Marked-up images the user sends you (numbered markers + notes) | [Marking up what you send](/board/annotate/) | any | | Saved commands, schedules, agent prompts | [Workspace Actions](/cards/workspace-actions/) | any | | Push notifications, Watch answers | [Notifications](/mobile/notifications/) | any | ## If you are running inside an Offsite session Your environment carries: | Variable | Meaning | |---|---| | `OFFSITE_SESSION_ID` | this session's id | | `OFFSITE_WORKSPACE_ID` | the workspace root path | | `OFFSITE_TOOL` | `claude`, `codex`, `grok` or `terminal` | | `OFFSITE_SESSION_KIND` | `agent` or `terminal` | The `offsite` CLI is available: ```bash offsite session name '' # name this session's card offsite session recap '' # what just finished / what is next offsite agents list # the user's other agent sessions offsite agents context # another session's transcript offsite web open # a browser on the host, docked to your own card offsite web open --detached # …or as a free board card instead offsite web snapshot | click | fill # drive that page; `offsite web --help` lists it all ``` If you build or change a web UI, look at it. With MCP you have the same verbs as tools (`web_open`, `web_snapshot`, `web_click`, …); otherwise use the command above. Take a `snapshot` for the accessibility tree with refs, `click`/`fill`/ `press` to work the page, and `screenshot` when only pixels answer the question. The browser docks to your own card as a tile the user can enlarge, so they see what you are doing and can take over in it. If the web tools refuse, Browser Use is switched off on that host and only the user can turn it back on. Details: [Agent Browser](/cards/agent-browser/). **Before you diagnose:** [Troubleshooting](/help/troubleshooting/) lists the system messages, quirks and errors already explained, each as Symptom → Why → Fix. When the user pastes a message or asks why something appeared, check it first; the answer is often there, including the case where a macOS notice names Offsite for a program the user ran inside a session. **Rules that matter:** - Name your card in your first response and refresh the recap at the end of every turn — the user's board and their notifications are built from both. Write them in the language the user writes to you in. If `session name` is refused, the user has named this session themselves: keep their name and do not try again. See [Cards that name themselves](/work/self-naming/). - The CLI is **read-only towards other sessions**. There is no `send`. Writing into another session's terminal is forbidden — a delivery once merged with a user's half-typed prompt and was submitted with their authority. Hand context to a *new* session instead. See [Hand off and ask](/work/handoff-and-ask/). - The user may be on a phone, far from the host. Do not trigger GUI dialogs on the host's screen; nobody may be there to answer them. - Do not bring host applications to the front unless asked. On macOS, `open -g` opens a URL without stealing focus. - Long-running processes belong in the background (`nohup … > log 2>&1 &`), and should be verified through their log rather than held in the foreground. ## Nothing of the user's on our side - No account, no server-side user record. - No telemetry: no analytics SDK, no crash reporter. - No model of its own and no token metering. - No writing into your global tmux configuration. - No cross-host orchestration — each host owns its own sessions. ## Where to send a user | Question | Page | |---|---| | "What is this?" | [The big picture](/start/big-picture/) | | "How do I install it?" | [Install & set up](/start/install/) | | "What will macOS ask me?" | [What macOS will ask you](/start/what-macos-asks/) | | "Is it safe / what leaves my machine?" | [Security and privacy](/reference/security/) | | "How does it actually work?" | [Architecture](/reference/architecture/) | | "Something is broken" | [Troubleshooting](/help/troubleshooting/) | | "Why does macOS show this message / why does it name Offsite?" | [Troubleshooting](/help/troubleshooting/), section *Messages from macOS* | --- # Marking up what you send Source: https://docs.offsite.sh/board/annotate/ Circle it, number it, write one line per number. The agent gets the marked image and your notes as text, instead of guessing what "the blue one" meant. "Make this bigger and the blue one darker" is a sentence an agent cannot act on. So you write a paragraph describing where things are, or you send a screenshot and hope the agent finds what you meant. Offsite puts a small editor in front of every image you send. Draw a shape, Offsite numbers it, you write one line per number. The agent gets the marked picture **and** the notes as text ("1: make this bigger, 2: dark blue"), with the numbers on the image matching the numbers in the list. ## Where it appears The editor sits in front of the upload, on every path that sends an image: | You do this | On | |---|---| | Drop images onto an agent card | macOS | | **Upload Files** from the card's action pill | macOS | | Upload from the command bar — photo library, camera, files | iPhone, iPad | | **Mark** an area in a [Browser](/cards/browser/) or [Agent Browser](/cards/agent-browser/) card | macOS, iPhone, iPad | Non-images skip it entirely and upload as before. ## Using it Pick a tool (**circle, rectangle, arrow or freehand**) and drag it over the part you mean. A click without dragging drops the shape at default size. Every shape gets a numbered dot at its top-left corner, in the order you drew them; delete one and the rest renumber, so the agent never reads a list with gaps. Tap a dot to open the comments panel and write the line that belongs to it. A dot with a comment turns black, so you can see at a glance what you have already described. The top row of the panel is **marker 0: the whole image**. Use it for the sentence that applies to everything, like "home screen after login, dark mode", with or without any shapes at all. Sending several images at once puts a strip along the bottom. Each image keeps its own shapes and its own comments; switching between them saves as you go. Then **Upload** sends the batch. On macOS: `V` for select, `1`–`4` for the tools, `⌘Z` / `⇧⌘Z`, `⌫` to delete the selected shape, `←` / `→` to walk through them. ## What the agent receives The shapes are burned into the image at full resolution, and the comments travel as a plain block of text that is pasted into the session next to the file paths: ``` ----- Attached images ----- A detail from a page in my browser, plus the page around it. 2 images; the numbered markers in each picture match the notes below it. ## Image 1: browser-mark-….png /…/uploads/ab12-browser-mark-….png - Overall: home screen after login, dark mode - Marker 1: make this bigger - Marker 2: dark blue - Marker 3: (marked - see the picture) ## Where this comes from - Page: Offsite — http://localhost:3000/ - Selector: main.site > div.page > section.bento - To see it yourself: web_open http://localhost:3000/ in your Agent Browser. ----- end of attached images ----- ``` The block is fenced so an agent reads it as an attachment rather than as a truncated prompt. And when the image came from a browser card, it carries the page it came from plus a line telling the agent how to open that page in its [own browser](/cards/agent-browser/). Nothing is submitted. The text lands in the session's prompt, and you press Enter, as with every other upload. If you add no comment anywhere in the batch, no text block is generated at all: you get the plain paths, the way an upload has always worked. Your drawings are still burned into the image. ## Turning it off **Settings → Annotate images before sending**, on macOS and on iPhone. Off, every entry point uploads directly again. The editor is also on the upload dialog itself, so you can switch it off in the moment it gets in your way. ## Details - Images are PNG, JPEG, HEIC, WebP or TIFF. Anything else never sees the editor. - The marked image is the one that travels: what you see in the editor is the file the agent gets. - Images larger than 2048 px on the long edge are scaled down after the shapes are burned in, so the numbers stay sharp while a 10 MB retina screenshot does not have to travel. - Shapes are stored in normalized coordinates and rendered by the same code that draws the preview and the final file, so what you see is what the agent gets. - Marker numbers are white dots with a black border, black once commented. The colors are fixed so they stay readable on any screenshot. --- # The board Source: https://docs.offsite.sh/board/board/ Why Offsite uses a grid instead of tabs, how cards move between board, tabs and Icebox, and what Tidy board does. Tabs were designed for a world where you write every line yourself and jump between files. With agents, that flips: several things run at once, most of them without you, and the question is no longer "where am I?" but "who needs me?". So Offsite gives you a **grid board** instead. Cards sit side by side, all live, all visible at a glance. ## Cards **New Card** adds one. Seven kinds: | Card | What it is | |---|---| | Agent session | Claude, Codex or Grok | | Terminal | a plain login shell | | [Git](/cards/git/) | the workspace's changes | | [Files](/cards/files/) | browse and edit files on the host, in tabs, live | | [Browser](/cards/browser/) | your own browser on this device, pointed at the host | | [Agent Browser](/cards/agent-browser/) | a browser on the host that your agent drives | | [Simulator](/cards/simulator/) | a mirrored iOS Simulator | Drag a card to move it, drag its edges to resize. The grid keeps itself fully packed — there are no gaps and no overlaps, ever. Cards shrunk past a certain size drop their live view for a compact one: name, type, and — for agent sessions — the agent's own one-line recap of what it is doing. That is why a dense board still reads at a glance; see [Cards that name themselves](/work/self-naming/). ## Where a card can be [Figure: A card lives on the board, in the tab bar, or in the Icebox. Focus mode is a presentation of a card, not a fourth place.] ## Tidy board Dragging and resizing eventually produces a mess. **Tidy board** re-lays the cards: - **Equal** — every card the same size. - **Hero left** — one large card on the left, the rest tiled beside it. Reflow only touches unpinned cards, and needs at least three of them to be useful. [Figure: Tidy board has two layouts: equal, where every card is the same size, and hero left, where one large card sits on the left and the rest tile beside it.] ## Pinned cards **Pin Card** freezes a card in place. Pinning never moves anything — it means "leave this exactly here while I rearrange everything else". A card can only be pinned when it forms a **full edge strip**: a full-height column at the left or right edge, or a full-width row at the top or bottom. That constraint is what keeps the rest of the board a clean rectangle that can still be reflowed. Pins nest: pin the outermost column, and the next column in becomes pinnable. Dragging a pinned card unpins it, rather than refusing the drag. ## Search `⌘K` opens search. It looks across your workspaces, sessions and cards and jumps to the match. Agent cards run their CLI in fullscreen mode, which means the CLI owns its own scrollback — so search finds *sessions*, not text inside an agent's history. ## Details - The board grid is quantized to at most 24 columns and rows on the host, so the same layout renders identically from a phone to a Studio Display. - Reflow modes on the wire are `equal` and `hero_left`; the hero slot always goes to the first *unpinned* card in reading order. - Pin semantics are evaluated against the **free region** (board minus pinned strips), which is what makes nesting work. Pins live on the edges, which is what keeps the rest of the board a clean rectangle. - Cards that support hidden chrome (`terminal`, `browser`, `mirror`) can hide their controls entirely — **Hide card controls** on the card menu. - **Add to Tabs** and **Move to Icebox** live on the card menu of a board card. --- # Focus mode and the card strip Source: https://docs.offsite.sh/board/fullscreen/ Giving one card the whole window without losing sight of the others. Some work needs the whole window. **Focus mode** gives one card the full area and moves everything else into a **strip** along the edge, so the board is still there — small, live, one click away. [Figure: Focus mode turns the grid into one large card with the remaining cards as a live strip along the edge; the same shortcut toggles back.] ## Entering and leaving - Double-click a card, or **Focus Mode** on its menu. - `⌘⌥Return` toggles focus for the selected card — in and out, the same shortcut. - **Close Focus Mode** returns to the grid. ## The strip The other cards become thumbnails along the side. They keep updating: an agent that finishes while you are focused elsewhere shows it in the strip, and its status badge changes. `⌘⌥←` / `⌘⌥→` move along the strip, so you can walk through your sessions without leaving focus mode. ## When to use it - Reading a long agent turn, or a diff, where board-sized cards are too cramped. - Typing a substantial prompt. - Driving a Simulator or a browser target, where you want real pixels. For everything else the grid wins: several agents at once, all visible. ## Details - Focus mode is a presentation of a card, not a placement. The card stays where it was on the board or in the tabs, and the session is untouched. - Focus is per device. Focusing a card on your Mac does not change what your iPhone shows. - Agent CLIs render in fullscreen (alternate-screen) mode, so a focused agent card has no separate scrollback for Offsite to search — the CLI manages its own history, and that is why card search is not offered for those cards. - Placement changes — **Add to Tabs**, **Move to Icebox** — are board moves: leave fullscreen and use the card menu on the board. --- # Keyboard control Source: https://docs.offsite.sh/board/keyboard/ Driving the board without the mouse — and why every Offsite shortcut contains Command. The board is fully drivable from the keyboard on macOS. ## The shortcuts | Keys | Does | |---|---| | `⌘⌥` + arrows | move between cards — 2D on the grid, left/right in the focus strip | | `⌘⌥Return` | focus mode in and out | | `⇧⌘⌥` + arrows | swap the selected card with its neighbour | | `⌃⌘↑` / `⌃⌘↓` | cycle workspaces | | `⌘K` | search | | `⌥⌘C` | copy the last command's output | [Figure: Board navigation is two-dimensional: Command-Option plus an arrow key moves the selection to the neighbouring card in that direction, and Command-Option-Return focuses the selected one.] ## The New Workspace panel The stepper that opens from **+** next to *Workspaces* is fully keyboard-driven with the same `⌘⌥` chords as the board — plain arrows, Return and Esc stay with whatever card or field has them. A hint at its top-right names the chords of the page you are on. | Keys | Page | What happens | |---|---|---| | `⌘⌥↑` `⌘⌥↓` | every page | move the cursor: source tiles, folders, projects, repositories | | `⌘⌥Return` or `⌘⌥→` | tiles, projects, repositories | open the tile, add the project, choose the repository | | `⌘⌥Return` or `⌘⌥→` | folder browser | enter the highlighted folder | | `⌘⌥←` | folder browser | go up one folder | | `⇧⌘⌥Return` | File System, clone target | Create the workspace / start the clone | | `⌘⌥Return` | failed clone | try again | | `⌘⌥Delete` | every page | back one step; from the first page out of the panel | | `Tab` | pages with a text field | into the name / filter / URL field as usual | When the new workspace lands on its empty board, the cursor is already on the start options: `⌘⌥↑` `⌘⌥↓` pick a tool, `⌘⌥Return` starts it — the whole way from **+** to a running agent without touching the mouse. ## Why ⌘⌥ and not plain ⌘ Two rules shaped the layout, and they explain why the shortcuts run slightly long: **Every Offsite shortcut contains Command.** macOS reserves Command for the application, so a combination containing it is never delivered to the terminal. That means Offsite can never eat a keybinding your shell, your editor or your agent CLI depends on. Bare `Esc`, `Ctrl-C`, `Ctrl-R` and friends always belong to the session. **Option is there because Command alone collided with the caret.** `⌘←` and `⌘→` are line-start and line-end in any text field, so board navigation with plain `⌘`+arrows did nothing while you were editing a commit message or a file. Moving to `⌘⌥` fixed it: those are not caret commands, so Offsite can intercept them even over a focused editor, and plain `⌘`+arrows fall through to the editor untouched. Arrows and Return are layout-independent, so the shortcuts work the same on a Swiss or a German keyboard. ## Details - Implemented as a local `NSEvent` monitor on the board, which sees the key before the first responder. That is what lets `⌘⌥` combos win over a focused text view by construction rather than by luck. - `⌘⌥Esc` is deliberately never bound — that is macOS Force Quit. - The old shell prompt-jump binding (`⌥⌘↑`/`⌥⌘↓`) was removed because it collided with navigation. - Plain `↑`/`↓` navigation inside the Git card's file list is unchanged and does not need modifiers. - iOS has no keyboard shortcut layer; use the [command bar](/mobile/command-bar/) instead. --- # Tabs Source: https://docs.offsite.sh/board/tabs/ The tab bar for cards you want reachable but not on screen. Not every card deserves screen space all the time. The **tab bar** holds cards you want one click away without them competing for board area. ## Using it - **Add to Tabs** on a card moves it off the board into the tab bar. - Clicking a tab shows that card large; clicking it again returns you to the board. - **Move to Board** puts it back into the grid. - **New Tab** creates a card directly in the bar. - **Hide tabs** / **Show tabs** collapses the bar when you want the full height. A tabbed card is still running. This is a presentation choice, not a lifecycle one. ## Tabs versus Icebox They look similar and mean different things: | | Tabs | [Icebox](/work/icebox/) | |---|---|---| | For | cards you switch to often | sessions you are not working with now | | Card types | any | agent sessions only | | Lives in | the bar above the board | the sidebar | | Coming back | click the tab | **Move to Board**, restored to its old slot | Rule of thumb: tabs are for *attention*, the Icebox is for *storage*. :::note **Pin** means one thing: [freezing a card at a board edge](/board/board/). The strip at the top is Tabs. ::: ## Details - Tabs are per workspace and stored with the board, so they survive restarts and appear identically on every device. - A card is either on the board or in the tabs, never both; the host rejects a board grid and tab list that name the same card twice. - Tab order is the order you added them; drag to reorder. - A card in the tab bar keeps running like any other; switching to its tab shows exactly what it did while you were elsewhere. --- # Dark mode, light mode and themes Source: https://docs.offsite.sh/board/themes/ Appearance, the built-in themes, and the difference between app colours and terminal colours. ## Appearance Offsite follows the system by default and can be pinned: | Mode | Behaviour | |---|---| | Automatic | follows macOS / iOS appearance | | Light | always light | | Dark | always dark | The setting is per device, so your Mac can stay dark while your phone follows the sun. ## Themes Three themes ship with the app — **Classic**, **Blue** and **Her** — and you can edit them in the **Command Center**. A theme is two separate palettes, and the split matters: | Palette | Controls | Example | |---|---|---| | **App theme** | cards, sidebar, dialogs, buttons, badges, dividers | the chrome around your work | | **Terminal theme** | the terminal surface only — foreground, background, cursor, the 16 ANSI colours, dim opacity | what your CLI draws | They are never mixed. Terminal colours are for terminal content; app colours are for app chrome. That is why editing your terminal background does not tint the sidebar, and why an app accent colour can be near-black without making text disappear. [Figure: A schematic card in light and dark appearance. The outer surface is painted by the app theme; the inner terminal surface, including the ANSI colours, is painted by the separate terminal theme.] ## The terminal palette matters more than it looks Agent CLIs use indexed and dim ANSI colours heavily — Claude's question text is a dim colour, not the default foreground. Offsite installs the full 16-colour palette plus foreground, background and cursor into every live terminal, on both platforms. If you edit those values, edit them for both appearances: a colour that only reads on dark will be invisible in light mode. ## Details - Appearance is stored per device under the key `offsite.client.theme_appearance_mode`; values `light`, `dark`, `automatic`. - Theme profiles ship as JSON (`classic`, `blue`, `her`) and the Command Center writes your edits on top of the selected profile. - App colours have roles that are enforced in the codebase: canvas colours are for backgrounds and fills, text colours for text, icons and carets. The caret and selection tint always come from the primary text colour, never the accent — the accent is a canvas-group colour and may legitimately be very dark. - Diff and code surfaces resolve their own palette, with addition and deletion colours defined separately from the general UI. - The docs site you are reading uses the same palette family, which is why lime appears as a darkened olive in light mode: raw lime fails contrast on paper. --- # Sending files to a session Source: https://docs.offsite.sh/board/upload/ Getting screenshots, logs and documents from your Mac or phone into an agent's hands. Agents work with what they can see. Uploading puts a file on the **host**, next to the session, and hands the agent its path — so "look at this screenshot" or "here is the failing log" works from any device. ## On macOS - **Drag and drop** files onto a session card. Several at once is fine. - Or use **Upload Files** on the card menu. An overlay shows progress with an `index/total` count while a batch runs. ## On iPhone and iPad The [command bar](/mobile/command-bar/) has an upload button with three sources: | Source | For | |---|---| | Files | anything already on the device or in iCloud Drive | | Photo library | screenshots and images you already took | | Camera | photograph something in front of you, right now | The camera path takes about five seconds: a whiteboard, a device screen or a sheet of paper, photographed straight into the agent's context. ## Images stop for a moment first An image on its way to a session opens in the [annotation editor](/board/annotate/): circle what you mean, number it, write a line per number. The agent gets the marked picture and your notes as text. Anything that is not an image uploads directly, and the editor can be switched off in Settings. ## What the agent sees Each session has its own upload directory on the host. Uploaded files land there with a sanitized filename, and the path is handed to the session so the agent can read it directly. Because the file is on the host, the agent can do real work with it — open it, diff it, run it through a tool — instead of only seeing an inlined image. ## Details - Upload directory: `~/.offsite/uploads//` on the host. - Filenames are sanitized to `A–Z a–z 0–9 . _ -`; a name that reduces to nothing becomes `upload`. Directory components are stripped, so an upload can never escape its session directory. - Only regular files are accepted — no directories, no symlinks. - Uploads are user-initiated only. An agent cannot pull a file off your device. - The transport gives no per-byte progress, which is why a batch shows a spinner plus a file count rather than a percentage. - Uploads are not cleaned up automatically; the directory is yours to prune. --- # The Agent Browser card Source: https://docs.offsite.sh/cards/agent-browser/ A real browser on your host that your agent drives directly, with no extension or automation stack to install. You can watch it work and take over whenever you want. An agent that writes a web app cannot see it. It can read its own code, run its own tests, and still ship a page with the submit button behind the keyboard, because at no point did anything look at the rendered page. The **Agent Browser** is a real browser, running on your host, that the agent drives directly: it opens a URL, reads the page, clicks, types, and looks at the result. No browser extension, no plugin, no separate automation stack to install and keep alive. It is part of Offsite, and the agent already has it. It is also a card. So while the agent works, you watch the page move on your board, and you can reach in and take over at any point. [Figure: The Browser card renders a tunnelled page separately on each device, so every device has its own private session. The Agent Browser runs one browser on the host that the agent drives, and streams the same picture to every device.] ## What it is good for **The agent checks its own work.** It opens the dev server it just started, reads the page, clicks through the form and looks at the result. The round trip that used to need you, *look at this and tell me what broke*, runs without you. **Nothing to install.** No Chrome extension, no signed-in browser profile handed to a robot, no Playwright or Puppeteer setup that has to be maintained next to your project. The browser and the tools that drive it ship with Offsite and run on the host the agent is already working on. **You watch, and you can take over.** The stream shows a pointer and a click ripple wherever the agent acts, so the card tells you what is happening to your app right now, not afterwards in a summary. Click into it and it is a browser: scroll, type, navigate. The agent's next command continues in the same page. **Every screen size, without the hardware.** **Responsive** is the default and behaves like a browser window: the page reflows to whatever size the card has. Or pick a device: iPhone SE, iPhone 15, 15 Pro Max, iPad mini, iPad, iPad Pro 11″ or 13″, Desktop 1280×800, Desktop HD, each with the touch and mobile behaviour the real device has. **One browser, all your devices.** Log into your staging environment on the Mac, pick up your phone, and you are still logged in, on the same page, at the same scroll position. Close the app entirely and come back: the page is where you left it. **It runs where there is no screen.** The browser is headless, so a Linux VPS with no desktop is a perfectly good host. ## It lives on the agent's card By default the Agent Browser is not a separate card at all: it docks to the card of the agent that opened it, as a small picture-in-picture tile. Ten agent sessions mean ten browsers, each on its own card, and you still know at a glance which one belongs to whom. The tile has a pill that switches how much room it gets: | Mode | What you get | |---|---| | **PiP** | a small view-only tile, cheap to stream, enough to see that something is happening | | **Dock Right** / **Dock Bottom** | the browser as its own card beside the agent's output, fully interactive | | **Full** | the browser fills the session card | | **Hide** | nothing, until you bring it back from the card's action pill | The mode is yours, per device: your Mac can sit in Dock Right while your phone keeps the same browser as a tile. Offsite suggests the edge that fits the card's shape (wide cards dock right, tall cards dock bottom), and if a card gets too small for the docked layout, the tile takes over until there is room again. Shrink the card further and the browser becomes a small globe badge that pulses when the agent is working. So a board full of agents costs one video stream, not ten: the tile sends a couple of frames a second and only when the picture changes, and the full stream runs for the browser you enlarged. An agent's browser follows its agent. End the session and its docked browser ends with it, profile included. Move the session to the Icebox and the browser stops but keeps its profile, ready when you come back. Detach the browser into a free board card from the same pill, and it outlives the session. `offsite web open --detached` (or `web_open(detached: true)`) creates that free card straight away, for the case where you want a browser that is not tied to one agent. ## What the agent can do with it The verbs reach the agent as tools, from Offsite's own MCP server, so an agent that supports MCP has them directly, without a wrapper or a sandbox exception: | Tool | What it does | |---|---| | `web_open` | open a URL in a card next to the agent's own | | `web_snapshot` | the page as text — the accessibility tree, one line per element, with refs like `[e12]` | | `web_click`, `web_fill`, `web_select`, `web_type`, `web_press` | work the page by ref, CSS selector or coordinates | | `web_screenshot` | a PNG, when only pixels answer the question | | `web_goto`, `web_back`, `web_forward`, `web_reload`, `web_url` | navigation | | `web_eval`, `web_wait` | run JavaScript, wait for load or for a selector | | `web_viewport`, `web_settings` | switch device, quality, frame rate, cursor | | `web_close` | end the card and its browser | `web_snapshot` is the one that matters most: a text tree with stable refs is far cheaper and far more reliable than asking a model to find a button in a screenshot. Screenshots are for the questions only pixels answer. The same verbs exist as a command, for agents without MCP and for you: ```bash offsite web open http://localhost:3000 offsite web snapshot offsite web click e12 offsite web fill e3 "ada@example.com" offsite web screenshot --full ``` Cards belong to a workspace, so an agent can only reach the Agent Browsers in its own. ## Using it yourself You do not have to wait for an agent: add an **Agent Browser** card yourself, give it a URL and start with Responsive, Desktop or Mobile. The URL is resolved **on the host**, so `http://localhost:3000` means the host's own port 3000, with no tunnel and no exposed port involved. A card you made this way belongs to no agent, and any agent in that workspace can drive it. Whatever you see in the card, you can also mark: the **Mark** button captures the page, opens the [annotation editor](/board/annotate/), and hands the marked picture plus your notes to an agent session, on the same path the [Browser card](/cards/browser/) uses. The card carries a slim bar: back, forward, reload, and the address with the page title. On iPhone there is a keyboard button for typing into the page, and an enlarge button when the card is too small to work in. The settings button opens four controls: | Setting | Options | Why you would touch it | |---|---|---| | **Screen** | Responsive, or a device from the catalog | check a phone layout without a phone | | **Quality** | HD, Balanced, Fast | Fast halves the resolution — the real lever over a slow link | | **Frame rate** | 10, 24, 30, 60 fps | lower costs less bandwidth on a remote host | | **Agent cursor** | on / off | show or hide the pointer and click ripple | If nothing is listening on the URL yet, the card says so and offers a retry instead of showing you a stale page from an hour ago. ## Agent Browser or Browser? Two browser cards, two different jobs. | | [Browser](/cards/browser/) | Agent Browser | |---|---|---| | Whose browser it is | yours | the agent's — you are welcome in it | | Runs on | the device in your hand | the host | | Reaches the dev server via | a tunnel over your existing connection | nothing — it is on the same machine | | Cookies and logins | per device, yours | one profile on the host, shared by your devices | | Your personal accounts | yes, this is where they belong | none | | After a reconnect | reloads the page | the page is still there, scrolled where you left it | | Device emulation | no | a catalog of phones and tablets | | Agent access | never | full, through the web tools | Use the Browser for documentation, a deployed site, anything you are personally logged into. Use the Agent Browser for the app you are building, and let the agent have that one instead of anything of yours. ## What it needs - A Chromium-family browser on the host: Google Chrome, Chromium, Brave or Microsoft Edge. The card names the exact install command for that machine if it has to. - **Browser Use** in Host Settings, which is on by default. It is the switch that decides whether agents get the web tools at all, and it can stay on because this browser holds none of your personal logins. Each card owns its browser and its profile: a card is a clean session that starts where you point it and ends when you close it, so an agent always works in a known state. Give a second page its own card. - Both apps need to be reasonably current: the card itself needs protocol 24, the docked browser needs 25. An older client does not show what it cannot render, rather than showing something broken. ## Details - The host runs the browser with `--headless=new` and its own user data directory under the host's state directory, and talks to it over the Chrome DevTools Protocol. Nothing is bundled with Offsite and nothing is downloaded: it uses the browser already installed on that machine. `OFFSITE_WEBSIM_BROWSER` points at a specific binary if discovery picks the wrong one. - The picture is a JPEG screencast on the same channel the [Simulator card](/cards/simulator/) uses; input travels back over a separate socket. Chromium only sends a frame when something changed, so an idle page costs nothing. - **Responsive** means the client reports the card's real size in CSS points plus its pixel ratio, and the host applies that as the device metrics, so the page reflows the way a browser window does rather than zooming. - The agent cursor and the click ripple are injected into the page for the stream only. They are marked `aria-hidden` and are not part of your app. - Refs from `web_snapshot` stay valid until the next navigation or snapshot. - The docked tile and the enlarged browser are two different streams: a scaled screenshot at about two frames a second that only sends when the picture changed, and the full screencast at the quality you picked. - The dock mode is stored per device, not on the host, so it never travels to someone else's screen. - Every action is written to the host's runtime log for that card. --- # The Browser card Source: https://docs.offsite.sh/cards/browser/ Your own browser, on the device in your hand, pointed at the dev server on your host, with tabs, dev tools, and a way to mark what is wrong and hand it to an agent. Your agent starts a dev server. It listens on `localhost` **on the host**, which is a machine you may not be sitting at. Normally that is where remote work stops. The **Browser** card forwards that loopback port over the connection you already have and renders the page, on your Mac and on your phone. It is your browser: your cookies, your logins, your session. No agent can see it or touch it. ## Using it Add a Browser card and it opens straight away, on the last URL you used in a browser card. No dialog, no setup step. The bar across the top is a real browser bar: | Control | | |---|---| | Back, forward, reload | `⌘[`, `⌘]`, `⌘R` — and `⇧⌘R` reloads past the cache, for dev servers that serve stale bundles. `Esc` stops a load | | The address field | type a URL, or type words to search. `⌘L` focuses it | | History | `⌘Y`; `⌘`-click an entry to open it in a new tab. Rows carry the site's icon | | **+** | a new tab — `⌘T`, close with `⌘W`, switch with `⌃Tab` and `⌃⇧Tab`, `⌥⌘→` and `⌥⌘←`, or `⇧⌘]` and `⇧⌘[` on a US layout; jump with `⌘1` to `⌘8`, `⌘9` is the last tab | | Camera | send the whole page to an agent — `⇧⌘S` | | Marquee | mark an area of the page and send that | | Wrench | Developer Tools — `⌥⌘I`, in their own window | `⌘F` opens find-in-page. The ports menu next to the address lists what is actually listening on the host for this workspace (`:3000 node · Claude`), so you can jump to a dev server without remembering its port. Typing a different loopback port reopens the card on a new tunnel; anything else navigates in place. And when nothing is answering yet, the card says exactly that and offers a retry, instead of turning into a broken card that stays broken after the server comes up. Shrink the card and it turns into a launchpad: the current page as a cover with its title and an address box, then your tabs, the servers running in the workspace and recent pages, each with the site's icon. Public pages show the preview image the site publishes for links, the same one messaging apps show; your dev servers show a live capture of the page instead. ## Mark what is wrong, hand it to an agent You are looking at the page the agent just built, and something is off. Instead of describing where it is: 1. Hit the marquee button and drag a rectangle around it, or `⇧⌘S` for the whole page. 2. The [annotation editor](/board/annotate/) opens: circle things, number them, write one line per number. 3. Pick the target session, an **intent** (fix, change, question or approve) and add a note. Offsite uploads the marked image to the host and pastes a block into that session: your notes, the page URL, the CSS selector of the element you marked, its surrounding HTML, and in a dev build of a React app the component and the `file:line` it came from. Plus a line telling the agent how to open the same page in its [own browser](/cards/agent-browser/). Nothing is submitted. The text sits in the prompt; you press Enter. If no agent session is running, the target picker offers to start one. ## This one is yours The Browser renders on **your** device, with your cookies and your logins, and no agent can reach it — not through a tool, not through the CLI, not by asking. Data moves in one direction only: from you to the agent, when you send it. The [Agent Browser](/cards/agent-browser/) is the opposite: one browser on the host that the agent drives, showing the same page on all your devices. One is yours, the other is the agent's, and you are welcome to watch. ## On iPhone and iPad The card is Safari, so it behaves like Safari, including your logins and the share sheet. Marking up a page happens in the Agent Browser card: the card menu has **Open in Agent Browser**, which opens the same URL host-side. ## Details - macOS forwards with `ssh -L`; iOS opens a `direct-tcpip` channel natively over its own SSH implementation. Both ride the connection the app already has, so nothing new is authenticated, no port is opened on the host, and the dev server stays private to your own devices. - Forwarding targets the host's loopback interface, so a server bound to `127.0.0.1` is enough. - The macOS card renders in a bundled Chromium (CEF) rather than WKWebView, because third-party sign-in popups do not work reliably in the latter. Screen captures and page JavaScript run through the Chrome DevTools Protocol. - The browser profile is per workspace and lives on your Mac, not on the host. - A new card opens on the last URL used in a browser card on this device, else the newest browser session on the host, else the Offsite start page. - Port discovery scans the host's socket table every few seconds and maps listeners to sessions through the process tree; servers started outside a session are listed for the workspace instead. - Stale forwards from a previous run are cleaned up when the Mac app launches. --- # Deploy to this phone Source: https://docs.offsite.sh/cards/deploy-to-phone/ Building an iOS app on the host and installing it on the iPhone in your hand — no TestFlight, no cable. Your agent just built an iOS app. It is sitting on a Mac somewhere. You are not. **Deploy to this phone** builds the project on the host and installs the result on the iPhone you are holding — over the network, with no cable, no TestFlight round trip and no App Store review. Available on **macOS hosts only**. ## Using it From the iPhone app, pick the Xcode project in the workspace and start a deploy. The host builds it, packages it, and your phone offers to install it. The build runs on the host's Xcode with the host's signing setup. That is where the requirements come from. [Figure: The host builds and signs the app, publishes the package behind a short-lived signed URL with an install manifest, and the iPhone installs it through Apple's own install mechanism.] ## What you need | | | |---|---| | An Apple developer account signed in to Xcode on the host | the build has to be signed | | Your iPhone registered with that account | a provisioning profile has to include it | | An Xcode project or workspace in the Offsite workspace | Offsite scans a few levels deep for it | | Push notifications registered from this iPhone | the install link is delivered as a push; if the host reports no registration, reconnect or restart the app once | A free personal team works, with Apple's usual limits: apps expire after seven days and some capabilities are unavailable. ## When it fails Signing is where deploys fail, and the failure is reported as a specific kind rather than a wall of build log: - **No signing account** — Xcode on the host has no Apple ID. - **Free team limitation** — the project uses a capability a personal team cannot sign. - **Device not registered** — this iPhone is not in the provisioning profile. - **Generic signing failure** — everything else, with the tail of the log. ## Details - Deploy state lives in `~/.offsite/deploys/` on the host. Builds use a private derived-data directory (`~/.offsite/xcode-derived-data`) rather than the shared Xcode one, both to avoid macOS App Management restrictions and to keep your own Xcode caches untouched. - Install path: the IPA is uploaded to a short-lived signed URL, a manifest is published and verified, and the phone installs through `itms-services://?action=download-manifest`. Apple's own mechanism, no sideloading tricks. - Limits: IPA up to **500 MB**, artefacts expire after **48 hours**, the last **10** deploy jobs are retained. - Project discovery scans up to 3 directory levels and skips `.git`, `node_modules`, `Pods`, `DerivedData`, `Carthage`, `.build` and `.swiftpm`. - Deploys preserve the installed app's container — Offsite does not uninstall first, so your app's data survives a redeploy. - A failed deploy keeps its log and a retry control at the end of the card, so a second attempt is one tap once you have fixed the cause. --- # The Files card Source: https://docs.offsite.sh/cards/files/ Browsing and editing files on the host, with tabs, live updates when the agent writes, and a real diff when your edit and the agent's collide. The **Files** card is a file browser and editor for the workspace, running against the host's filesystem. It exists for the small interventions: fix a typo the agent introduced, read a config it keeps getting wrong, check what a generated file actually contains, without switching to a terminal card and without being at the machine. ## What it does - Browse the workspace tree, with hidden files on or off. - Open files in **tabs**. A path an agent mentions in its terminal (`docs/PLAN.md`, `Foo.swift:42`) is one click away and opens at that line. - Edit in a syntax-highlighted editor. Unsaved work is a **draft**, marked with a dot on the tab. - **Star** files you keep coming back to, and filter the tree down to starred files or to files with drafts. - Search for a file by name. - Create files and folders, delete entries, and **Open in Finder** on macOS hosts when you happen to be at the machine. Tabs, drafts and stars live on the **host**, not in the app. Open three files on the Mac, pick up your phone, and the same three tabs are there — with your unsaved edits. ## It keeps up with the agent The card watches the folders it is showing. When the agent, a terminal, or anything else on that machine creates, renames or deletes a file in one of them, it appears in the tree within a second or two. No refresh button, no wondering whether what you see is current. The file you have open is checked the same way, but cheaply: the card asks "has this changed since version X?", and an unchanged file answers in a few hundred bytes instead of resending itself. ## When you and the agent edit the same file The draft remembers which version of the file it started from. If the file changed on disk before you save, the card stops and shows you a **diff of the host's version against yours**, with two explicit choices: - **Keep my draft (overwrite host)** - **Take host version (discard my draft)** Nothing is decided for you, and nothing is merged behind your back. ## Speed The card was rebuilt around the fact that its host may be a VPS on the other side of a relay: - Opening a file is **one round trip** instead of four. - Directory listings and file contents are fetched **conditionally**: unchanged means "not modified" instead of a resend. - Expanded folders load in parallel, and only the folders that actually changed reload. - File search skips the places nobody means (`.git`, `node_modules`, `DerivedData`, `.build`, `Pods`, `__pycache__`), and stops after about a second rather than walking a monorepo to the end. - Images over ~1 MB are scaled down **on the host** and arrive as a preview, with the original one button away, so a 12 MB screenshot no longer blocks the rest of your board. - Typing in the editor no longer makes every other device reload its tree. ## Where it fits Use it for the quick, surgical edit — the typo, the config value, the file you want to read before you answer the agent. Structural work belongs in the card next to it: describing the refactor to an agent that has the whole repository in its head beats doing it by hand on a phone. ## Details - All paths are resolved inside the workspace root, so the card always shows the project you are in. - Saving writes through a temporary file and an atomic replace, so a failed save cannot leave a half-written file. - Drafts live in the host's state database with the file version they started from, which is what makes both cross-device editing and the conflict check possible. - The host watches at most 48 directories per connection, polling their modification times about once a second, and pushes only the paths that changed. Content changes inside the file you have open are caught by the versioned refresh instead. - Image previews cap at 2048 px on the long edge; GIFs are always sent as-is so they keep animating. - Files are classified from their first few kilobytes: text is editable, images preview, anything else is listed but not opened. - File contents travel over the same connection as everything else; there is no separate file service and no port to open. - Live updates and conditional fetches need host and app on protocol 28 or newer, which is anything current. --- # The Git card Source: https://docs.offsite.sh/cards/git/ Reviewing what your agent changed, staging it, committing and pushing — from any device. An agent that works unattended produces a diff you did not write. Reviewing that diff is the job, and it is the job you most want to do from the couch. The **Git card** shows the workspace's repository: changed files, the diff for the selected file, history and branches. ## What you can do | | | |---|---| | Review | per-file diff with additions and deletions highlighted | | Stage / unstage | per file | | Commit | with a message you type in the card | | Push / pull | push to the tracked remote, pull fast-forward only | | Undo last commit | soft reset, changes stay in your tree | | Discard changes | restore a tracked file, or delete an untracked one | | Branches | list and switch, including tracking a remote branch | | History | recent commits | Everything runs `git` on the host, in the repository, as you. There is no Offsite-specific git state and nothing to clean up if you stop using the card. ## Reading a diff on a phone The card is built to be readable at phone width: file list first, then the diff for the file you pick, with line numbers and hunk headers in the muted colour and additions and deletions in their own palette. ## If the workspace is not a repository The card offers to run `git init`. A [workspace](/work/workspaces/) does not have to be a repository — if it is not, the card has nothing to show until you initialise one. ## Details Actions map to exactly these commands, run in the repository root: | Action | Command | |---|---| | stage | `git add -- ` | | unstage | `git restore --staged -- ` | | discard tracked | `git restore --worktree -- ` | | delete untracked | `git clean -f -d -- ` | | commit | `git commit -m ` | | undo last commit | `git reset --soft HEAD~1` | | switch branch | `git switch `, or `git switch --track origin/` when only a remote exists | | pull | `git pull --ff-only` | | push | `git push` | - Pull is **fast-forward only**: a merge that needs a decision should not happen behind a tap on a phone. - Undo requires `HEAD` to have a parent; on a repository with a single commit it refuses rather than doing something surprising. - Branch names starting with `-` are rejected, and every path is passed after `--`, so a filename can never be read as a flag. - The workspace root is resolved to the repository root, so a workspace pointing at a subfolder still shows the whole repository. - Anything beyond review, stage, commit, push and switching branches is a sentence to the agent in the next card — it has the repository open anyway. --- # The Simulator card Source: https://docs.offsite.sh/cards/simulator/ A live iOS Simulator from the host, mirrored onto your board and driven with your taps. If your agent is building an iOS app, the fastest feedback loop is watching it run. The **Simulator** card mirrors a Simulator running on the host onto your board — and your taps and typing go back into it. Available on **macOS hosts only**; a Linux host has no Simulator to mirror. ## Using it Add a Simulator card and pick a device. The card streams the Simulator's screen live and forwards your input: - Tap and drag with mouse or finger. - Type with your keyboard, or the phone's. - **High Definition** trades bandwidth for a sharper image — worth it on a fast local network, less so over the relay. Because the Simulator runs on the host, the build the agent just produced is the build you are looking at. There is no upload step and no separate device. ## From the phone An iOS Simulator running on your Mac at home, mirrored onto the iPhone in your hand, driven by your thumb. Good for "does the layout break on this screen?", and for marking what to fix instead of describing it. ## Details - The Simulator is driven by a small broker that Offsite installs as a LaunchAgent in the host's **GUI login session**. That is a requirement, not an implementation detail: `simctl` and the Simulator app need a real window server, so a host that never logs in graphically cannot mirror. - The screen arrives as an AVCC video stream; input goes back over a separate channel, so a slow frame never delays a tap. - Only `simulator` is a valid mirror target today. The card type on the wire is `mirror`. - If the card says *Simulator unavailable*, the usual causes are: no Simulator runtime installed, the host is not logged in graphically, or the broker has not been installed yet. - Related: [Deploy to this phone](/cards/deploy-to-phone/) puts the same build on real hardware, and [Testing what you build](/testing/testing/) ties both together. --- # Workspace Actions Source: https://docs.offsite.sh/cards/workspace-actions/ Saved commands and agent prompts per workspace — run them by tap, on a schedule, or when the host restarts. Every project has commands you run over and over: the test suite, the dev server, the deploy script, the "check what broke overnight" prompt. **Workspace Actions** are those commands, saved per workspace and startable with one tap — including from your phone, where typing them would be miserable. ## Two kinds | Kind | Runs | Good for | |---|---|---| | **Terminal** | a shell command in a terminal card | tests, builds, dev servers, scripts | | **Agent** | a prompt in an agent session | recurring review, triage, summaries | An agent action can pin the tool, the model and the [permission mode](/work/agent-sessions/), so "run the nightly cleanup in YOLO with Codex" is one saved thing. ## When they run Four triggers: - **Manual** — you tap it. - **Schedule** — every *N* minutes, or daily at a fixed time. - **Restart** — when the host comes back up. - **Card** — from the card the action belongs to. The scheduler runs on the host, so scheduled actions fire whether or not any device is connected. [Figure: Four triggers can start the same saved action: a tap, a schedule, a host restart, or its own card. The action carries its command or prompt plus its tool, model and permission mode, and every run lands in the run log.] ## The rerun loop A terminal action does not just run and die. When it finishes, the card waits at a rerun prompt: press Enter and it runs again, in the same card, with the same scrollback above it. That turns "run the tests" into a loop you can drive with one key, and it is why a live action card is reused instead of piling up new cards. ## Action memory Every action has a memory file on the host. An agent action is told to read it before starting and may write durable learnings back into it. This is what makes a recurring agent action get *better*: the third nightly run knows what the first two discovered, without you re-explaining it. ## Run log Each run is recorded with what triggered it, when it started and finished, and its exit status. That log is how you find out that the scheduled job has been failing since Tuesday. ## Details - Triggers on the wire: `manual`, `schedule`, `restart`, `card`. - Schedules are either `{kind: interval, minutes: N}` (N ≥ 1) or `{kind: daily, at: "HH:MM"}` in 24-hour form. Anything else is rejected. - Action memory lives at `~/.offsite/action-memory/.md` and is seeded with a short header. Deleting the action deletes its memory file. - The scheduler holds a lock file (`~/.offsite/action-scheduler.lock`) so two host processes cannot double-fire an action. - Run mode `rerun_on_enter` requires a command; the loop is only re-triggered while its prompt is actually up, so a busy card is never interrupted. - A terminal action can choose what happens when it completes — keep the card, or close it. - An empty Actions list opens straight into **New Action…**, so the first action is one dialog away. --- # Computer Use Source: https://docs.offsite.sh/computer-use/computer-use/ Letting an agent see and drive the host's screen — off by default, locked per session, revocable. Some tasks need eyes. An agent that just built a Mac app cannot verify it from a terminal; it needs to look at the window and click things. **Computer Use** gives a session screenshots of the host's screen and the ability to click, type and scroll on it — through a signed helper called the **Offsite Agent Bridge**. Available on **macOS hosts only**. ## Off by default, twice This is the widest access Offsite can hand an agent, so it is gated twice: 1. **The feature is off** until you enable it, which requires granting macOS Screen Recording and Accessibility to the Agent Bridge. 2. **Every session starts locked**, even with the feature enabled. You unlock one specific session when you want it to have the screen. Switching a session on or off applies to the **next** session you start, and the card's pill shows the live state — you can always see which sessions can touch your screen. [Figure: Two independent gates stand between a session and your screen: Computer Use must be enabled for the host, and that specific session must be unlocked. Failing either one refuses the request.] ## The two macOS grants | Grant | Why | |---|---| | Screen Recording | to take screenshots | | Accessibility | to move the pointer, click and type | macOS attributes both to *Offsite Agent Bridge*, the signed helper that holds them, so the dialog names something you can recognise and check. Offsite asks for these while you are **setting computer use up**, not in the middle of a session, so the prompt never arrives on a screen nobody is watching. ## Automation prompts The first time an agent scripts a specific application, macOS asks once per target app: *"Offsite Agent Bridge wants to control …"*. The grant covers that app and nothing else. ## Revoking System Settings › Privacy & Security › Screen Recording / Accessibility, and remove the Agent Bridge. Nothing else in Offsite depends on those grants. ## Details - The Bridge is a separately signed helper with its own identity; the host pins that identity and refuses to talk to a binary whose signature changed unexpectedly. - Communication is token-authenticated over a local channel; the token lives in `~/.offsite/agent-bridge/`. - The tool surface is the standard computer-use set plus a zoom tool — screenshot, click, type, key, scroll, drag, cursor position and friends. - Computer use runs on macOS hosts, through the signed Agent Bridge. - Capture needs a screen the Mac is actually driving: a display attached, or the built-in one open. - The lock is per session: if a call reports that computer use is locked, unlock that session from its card. --- # Pairing devices Source: https://docs.offsite.sh/connect/pairing/ How a device is authorised to reach a host — the QR code, the one-time token, and why one code pairs one device. Offsite has no account, so there is nothing to log in to. A device gets access to a host by being **authorised on that host**, once, by you. ## Pairing an iPhone 1. In the Mac app, open the host's **⋯** menu and choose **Show iPhone QR**. 2. Point the iPhone's Offsite app at the code on the *Scan on iPhone* sheet. That transfers the host's address and its pairing material. From then on the phone connects on its own, over whichever path is reachable. **Passwords are never in the QR code.** What it carries is the host's address and a one-time enrolment token; the device's own key is what authorises it afterwards. ## One code, one device QR tokens are **single-use**. A code that already paired one device will not pair a second one — show a fresh QR for each device. They are also invalidated when the host's agent restarts, so a screenshot of a QR code from last week is not a credential lying around. ## If pairing fails The usual cause is an already-used code. Show a fresh one from the Mac app (the host's **⋯** menu → *Show iPhone QR*) and scan that. :::note[Older versions] Up to 0.1.64, the enrolment token could only be redeemed when the first connection went through the relay. If your iPhone was on the same Wi-Fi as the host, the app preferred the direct path, the token was never redeemed, and the connection failed silently. Current versions redeem the token on **every** path — local network, Tailscale and relay — so the first connect works no matter which one wins. Hosts with the relay switched off can pair too. If you are on an older build, update both the host and the app. ::: An error mentioning **Remote Login** is misleading on a current host: Remote Login is not involved in normal operation, and the message almost always means this device is not authorised yet. ## Per host, not per fleet Each device pairs with each host separately. Pairing your iPhone with the Mac at home says nothing about the VPS — scan that host's QR too. ## Details - Enrolment is redeemed by the host's own agent, on-device, whichever path the connection arrived on. Authorisation no longer depends on the relay being enabled. - Devices are recorded in `~/.offsite/devices.json` on the host, together with their per-device [notification settings](/mobile/notifications/). - Pairing material is pinned per host on the client; a host key that changes — for example after a VPS rebuild — is a deliberate re-confirmation, not a silent reconnect. - Scan the QR from inside the Offsite app, which reads the pairing payload directly. --- # How your devices reach your host Source: https://docs.offsite.sh/connect/paths/ Three possible paths, raced against each other, switched live — and a pill that always names the winner. Your host is sometimes on the same Wi-Fi as you, sometimes behind a router in another country, and sometimes both within the same hour. Offsite does not ask you to know which. ## The three paths | Path | When it works | Typical latency | |---|---|---| | **Local network** | your device and the host share a network | single-digit milliseconds | | **Tailscale** | both machines are on the same tailnet | tens of milliseconds | | **Relay** | always, as long as both sides can reach the internet | a hundred milliseconds and up | [Figure: The client tries the local network, Tailscale and the relay at the same time and uses whichever answers first; when a better path appears later it switches live.] ## Racing, not configuring The client attempts every path it knows at the same time and uses whichever answers first. There is no "connection type" for you to pick and no wrong choice to make. When a better path becomes available later — you walk back into your own network — Offsite switches to it **live**, without dropping your session. ## The pill never lies The connection pill in the app names the path actually in use and its latency. If it says relay, you are on the relay; if it says LAN at 7 ms, that is a measurement, not a label. So when something feels slow, the pill tells you whether the network or the agent is responsible. ## Turning the relay off The relay is optional and switchable **per host**. Switch it off and that host is reachable only on your own network and your tailnet. Nothing else changes. See [The relay](/connect/relay/) for what it can and cannot see. ## Details - Endpoint kinds on the wire are `lan`, `tailscale` and `relay`; a host profile additionally records how it authenticates (`relay`, `directSSH`, `tailscale`). - LAN endpoints are discovered with Bonjour, which is why macOS asks for local network permission on first launch. Decline it and everything still works — connections take a slower path. - Every path terminates in the same place: Offsite's embedded agent on the host, over an SSH-backed RPC channel. There is no separate "relay mode" protocol, which is why a path switch does not reset your session. - Both clients enforce connect deadlines (15 s and 30 s) so a black-holed socket fails instead of hanging forever. - macOS localhost connections use the same architecture — the local host is not a special case. --- # The relay Source: https://docs.offsite.sh/connect/relay/ What the relay is, exactly what it can and cannot see, and how to switch it off. Sometimes neither your local network nor a tailnet connects you to your host — a hotel network, a locked-down office, cellular behind carrier NAT. The **relay** is the path that works anyway. It is optional, switchable per host, and deliberately built so that switching it on does not require trusting us with your work. ## What it is A thin pipe. Both your device and your host make an **outbound** connection to it on port 443, and it forwards bytes between them. That is the whole job. It does not terminate your session, does not run anything, and does not store your work. [Figure: Your device and your host both dial outbound to the relay on port 443, so no inbound port is open on the host. The SSH session runs end to end between device and host; the relay only forwards ciphertext it cannot decrypt.] ## What runs inside it **Unmodified SSH.** The relay carries an SSH connection between your device and your host's agent, exactly the same connection that runs over your local network — the transport underneath is the only thing that changed. So the relay sees SSH ciphertext. It cannot read your terminal, your prompts, your code or your keys, because it never holds the keys that would decrypt them. Your clients additionally **pin the host key**. A compromised relay could not impersonate your host even if it tried: the pin would not match and the client would refuse. ## What it can see Being precise about this is more useful than reassurance: | Can see | Cannot see | |---|---| | That a pairing connected, and when | Anything inside the session | | How many bytes flowed | What those bytes are | | The IP addresses of both ends | Your code, prompts, output or credentials | The realistic worst case for a compromised relay is **denial of service and connection metadata** — never content. ## No open port on your host Because both sides dial out, your host does not need an inbound port. `sshd` stays unreachable from the internet, your router needs no forwarding rule, and there is no public endpoint to find or attack. ## Switching it off Per host, in the host's settings. With the relay off, that host is reachable on your local network and over Tailscale only — everything else works unchanged, including [pairing](/connect/pairing/). ## Details - Implementation: Cloudflare Workers plus one Durable Object per pairing (`idFromName(pairingId)`), acting as a byte pipe with a small buffer for whichever side arrives first. - Access requires pairing authentication — challenge/response against the Ed25519 device keys your devices already use, with the registry in the Durable Object's storage. An unknown device cannot open a pipe. - Latency through the relay is roughly 90 ms in a worst-case measurement with both ends on the same machine (four legs). Real phone-to-Mac paths tend to be better. The [connection pill](/connect/paths/) shows the real number. - The relay is transport-neutral by design — the protocol is trivial enough that moving it onto our own servers later would change nothing for your clients. - Push notifications are a separate channel (Firebase) and, unlike the relay, not end-to-end blind: they carry readable text — the session and workspace names — because a lock screen has to display it. Never session content. See [Notifications](/mobile/notifications/). - Offsite runs **no analytics SDK and no crash reporter**. See [Security and privacy](/reference/security/). --- # FAQ Source: https://docs.offsite.sh/help/faq/ Short answers to the questions people ask before and just after installing. ## Do I need an account? No. There is no sign-up and no server-side user. Devices are authorised against your host by scanning a [QR code](/connect/pairing/). ## Does Offsite charge for tokens? No, and it could not. The agent CLIs call your provider directly from your host under your own subscription — Offsite is not in that path. See [The big picture](/start/big-picture/). ## Is my code sent anywhere? No. Session traffic goes between your own devices, encrypted; if it takes the relay, the relay sees ciphertext only. The one exception is a push notification: it carries readable text — the session and workspace names, so your lock screen can show which agent wants you — through Firebase and Apple. Never code, prompts or output. Full list: [Security and privacy](/reference/security/). ## Do I have to enable Remote Login / SSH? Not for [This Mac](/hosts/this-mac/) and not for normal operation. A [remote Mac](/hosts/remote-mac/) needs it once, for the initial install. ## Does it work when my Mac is closed or asleep? Sessions survive sleep and continue on wake. If you need the host to keep working with the lid closed, use [Keep the host awake](/hosts/keep-awake/); if you need it to come back after a restart, use [Always reachable](/hosts/always-reachable/). ## What happens if I lose connection mid-turn? Nothing, to the agent. It runs on the host and keeps going; you reconnect and find the output waiting. See [Sessions](/work/sessions/). ## Can I use several agents at once? Yes — Claude, Codex and Grok side by side on one board, each in its own card and its own [permission mode](/work/agent-sessions/). ## Does my `AGENTS.md` / MCP setup / skills work? Yes. Offsite starts the real CLI in your workspace folder with your shell environment, so everything the CLI would find on its own, it finds. ## Can I run it on Linux? As a **host**, yes — see [A Linux host](/hosts/linux/). There is no Linux client; you drive a Linux host from the Mac, iPhone and Watch apps. Note that Claude Code needs AVX2 on x86_64. ## Can an agent see my screen? Only if you turn on [Computer Use](/computer-use/computer-use/) *and* unlock a specific session for it. Off by default, macOS hosts only, revocable in System Settings. ## Can one agent message another? It can *read* another session and it can hand context to a **new** session, but it can never type into a running one. The reason is a real incident, described in [Hand off and ask](/work/handoff-and-ask/). ## Can I test what the agent built, from my phone? Yes: a tunnelled dev server, a mirrored iOS Simulator, or the build installed on the phone in your hand — see [Testing what you build](/testing/testing/). ## Which machines can be a host? Apple Silicon Macs, and Linux on `x86_64` or `arm64` — your own box, a machine in the other room, or a VPS. See [A Linux host](/hosts/linux/). ## Is there telemetry? No analytics SDK and no crash reporter. Feedback reaches us only when you send it from the app. ## Something is broken [Troubleshooting](/help/troubleshooting/) first, then [Get help](/help/get-help/). --- # Get help Source: https://docs.offsite.sh/help/get-help/ Where to send a problem, and what to include so it can actually be fixed. Offsite is in beta and the fastest fixes come from reports that contain enough to reproduce the problem. ## Send feedback from the app The in-app feedback form is the direct line. It reaches us with what you wrote — and nothing else, because there is no telemetry attached to it. Use it for bugs, confusing behaviour, and things that should exist and do not. ## What to include A report that contains these gets fixed much faster than one that does not: | | | |---|---| | App and host version | both, from the host row in the sidebar | | Host type | This Mac, a remote Mac, or Linux | | Which device | Mac, iPhone, Watch | | Connection path | what the [pill](/connect/paths/) said — LAN, Tailscale or relay | | What you did | the click path, not the conclusion | | What happened | including the exact message, if there was one | The version pair matters more than it looks: several symptoms that read like network bugs are really an app and host that no longer speak the same protocol. See [Updating Offsite](/start/updating/). ## Before you send Two checks that resolve a good share of reports on their own: 1. **Are the app and the host on the same release?** If not, update both and fully restart the app — a running app keeps its old protocol even after the bundle on disk was replaced. 2. **Is it in [Troubleshooting](/help/troubleshooting/)?** Known issues are listed there with what actually fixes them. ## Release notes Every version is described at [offsite.sh/releases](https://offsite.sh/releases). If something changed under you, that is where it is written down. --- # Troubleshooting Source: https://docs.offsite.sh/help/troubleshooting/ Known messages, quirks and errors around Offsite, each with the reason and what fixes it. Everything on this page is a symptom we have seen, with the reason behind it and the step that resolves it. It grows with what we find and what you report. Your agent knows this page. Paste a message into a session and ask why it appeared; the agent finds the entry and walks you through the fix. If a symptom is missing here, [send it in](/help/get-help/) with the exact text. ## Messages from macOS ### "Support Ending for Intel-based Apps" names Offsite Host Service ![macOS notification titled Support Ending for Intel-based Apps. It reads: This version of "Offsite Host Service" includes a component that will not work with a future release of macOS.](../../../assets/help/macos-intel-apps-notice.png) **Symptom:** macOS Tahoe shows this notification, and the app it names is *Offsite Host Service*. **Why:** Apple is retiring Rosetta, the layer that runs Intel-only programs on Apple silicon. Since macOS Tahoe, the system posts this notice whenever an Intel-only program starts, and it credits the notice to the process responsible for that program. Everything you run inside an Offsite session (a terminal command, an agent, a tool the agent calls) runs under the Offsite Host Service, so when one of those programs is Intel-only, the notice names Offsite. The host service itself is an Apple silicon build. The usual culprits are tools installed years ago as standalone Intel binaries: a CLI downloaded from a vendor's installer, an old Homebrew under `/usr/local`, a Node package that shipped a prebuilt Intel binary, a project toolchain fetched for the wrong architecture. **Fix:** find the Intel-only program and replace it with an Apple silicon build. Package managers ship the right architecture, so `brew install …` or `npm install -g …` over the old copy resolves it. To check a single tool: ```bash file "$(which firebase)" # "Mach-O 64-bit executable x86_64" → Intel-only, runs under Rosetta # "Mach-O universal binary … x86_64 arm64" → fine ``` To list every Intel-only tool on the paths a session uses: ```bash for f in /usr/local/bin/* ~/.local/bin/*; do [ -x "$f" ] && lipo -archs "$f" 2>/dev/null | grep -qv arm64 && echo "$f" done ``` The notice is informational. Until Apple removes Rosetta, the Intel-only tool keeps working exactly as before. ## Setup and pairing ### Pairing the iPhone fails while on the same Wi-Fi **Symptom:** you scan the QR code, the app shows the host, but **Connect** does nothing: no error, no connection. **Why:** on the first connection, your iPhone still has to be authorized on the host (that is what the QR code's one-time token is for). Today that authorization only happens when the connection goes through the Offsite relay, but when your iPhone and the host share a network, the app prefers the direct local path, where the token is never redeemed. The host then rejects the unknown device, and the app currently stays silent about it. **Fixed in current versions:** the host now redeems the QR token on every connection path (local network, Tailscale, and relay), so the first connect works no matter which path wins. If you are on an older version: turn Wi-Fi off on the iPhone for the very first connect (so it uses the relay once and gets authorized), then turn Wi-Fi back on. All later connections work normally. Update both the host and the iPhone app to get the fix. ### "Check that Remote Login is healthy on the Mac" This older error text can appear when a connection is rejected during login. On hosts set up with current Offsite versions, Remote Login is **not** involved. The message usually means your device is not authorized yet (see above: scan a fresh QR from the Mac app, ⋯ menu → *Show iPhone QR*). QR tokens are single-use: a QR that already paired one device will not pair a second one. --- # Uninstalling and resetting Source: https://docs.offsite.sh/help/uninstall/ Removing Offsite from a host and from a device, in the order that leaves nothing behind. Offsite keeps itself in one directory plus a few named services, so removing it is short. Do it in this order — stopping the services after deleting their files leaves orphaned jobs behind. ## Remove a host ### 1. End your sessions From the app, end the sessions you care about cleanly. Anything still running in tmux will otherwise be killed with the agent. ### 2. Turn the opt-in services back off In the app, switch off [always reachable](/hosts/always-reachable/) and hand sleep control back with **Allow host sleep**. That is the supported path and it removes the services properly. If the app is already gone, clean up by hand. Find what is actually installed rather than guessing at names — Offsite installs several helpers and their labels differ between builds: ```bash # macOS — what is registered launchctl list | grep -i offsite ls ~/Library/LaunchAgents | grep -i offsite ls /Library/LaunchDaemons | grep -i offsite ``` Then unload and delete what you found: ```bash # user agents launchctl bootout "gui/$(id -u)/