The connector, the ovenlight command, runs on the computer with your apps. It publishes
each app as its own node (a device in your tailnet) with HTTPS, and manages guests.
Support's setup steps are the short version.
This page has the full one, from installing to uninstalling, then
Troubleshooting, every command, and for reference,
installing by hand and what it installs.
What the computer needs
The computer must stay on and awake while anyone uses your apps. A laptop can keep serving with its lid closed once you change one setting (how). It must be one of these:
- A Mac with macOS 13 or later, logged in to its desktop and kept awake: in System
Settings, under Energy (Battery, Options on a laptop), turn on Prevent automatic
sleeping when the display is off. The connector runs only while you're logged in, so a
Mac without a display needs automatic login (Users & Groups, Automatically log in as).
FileVault turns automatic login off, so after any restart, such as a power cut or a
macOS update, the connector waits until someone logs in at the Mac.
ovenlight doctorchecks that the Mac won't sleep. - A Linux computer with systemd, such as Ubuntu. The connector is a systemd user
service, and with linger on, it runs from startup with nobody logged in.
install.shturns linger on, or prints thesudocommand for it when it can't. - A Windows 11 PC that never sleeps: in Settings, under System > Power (Power &
battery on a laptop), set sleep to Never, which
ovenlight doctorchecks. The connector runs as you from startup, with nobody signed in. On a work or school account (Microsoft Entra ID) or a domain account, it runs only while you're signed in.
You also need a Tailscale account with MagicDNS and HTTPS certificates turned on in the
admin console, under DNS; ovenlight doctor
checks both. The computer doesn't need the Tailscale app. Tailscale's free Personal plan
is for non-commercial use, so a business needs one of
Tailscale's paid plans.
A laptop with its lid closed
Closing a laptop's lid puts it to sleep and takes your apps offline, until you change one setting. Then it stays fully awake with the lid closed, so keep it plugged in, and never put it in a bag while it's on, where it can overheat.
-
On a Mac, turn off sleep in Terminal:
sudo pmset -a disablesleep 1The Mac then never sleeps, lid open or closed, even on battery. The same command with
0in place of1turns sleep back on.ovenlight doctorsays so when closing the lid would still put the Mac to sleep. On an Apple Silicon laptop, the Amphetamine app's Power Protect add-on can turn this setting on for a session and off again when it ends. -
On Windows 11, set what closing the lid does to Do nothing when plugged in. It's in Control Panel, under Power Options > Choose what closing the lid does, and on Windows 11 24H2 or later also in Settings, under System > Power & battery.
ovenlight doctordoesn't check this one. -
On Linux, the commands under A computer you have keep a laptop awake with its lid closed.
Install
On a Mac or Linux, in a terminal:
curl -fsSL https://ovenlight.app/install.sh | sh
On Windows, sign in as the account that will own the apps, which must be an administrator: elevating with another account's password installs the connector for that account. Open PowerShell with Run as administrator and run:
irm https://ovenlight.app/install.ps1 | iex
Never used a terminal? Here's how to open one.
The script checks the download before it installs anything, and stops with nothing installed if a check fails (Read the script first has what it checks and changes). Install by hand covers checking a download yourself, installing without the script and building from source.
Open a terminal
The command goes in a terminal, a window where you type commands:
- Mac: press Command-Space, type
Terminaland press Return. - Linux: on Ubuntu, press Ctrl-Alt-T. Elsewhere, open Terminal from your apps. If the
command then says
curlisn't found, install it withsudo apt install curl(or your system's package manager) and run the command again. - Windows: open Start, type
PowerShell, choose Run as administrator, and choose Yes when Windows asks.
Copy the command, paste it into the terminal (Command-V on a Mac, Ctrl-Shift-V on Linux, Ctrl-V on Windows) and press Return (Enter on a PC). The script says what it does as it goes. When it's done, open a new terminal window for the next steps it lists.
Read the script first
To read the script before it runs, save a copy, read it, and run that copy. On a Mac or
Linux, curl -fsSLO https://ovenlight.app/install.sh saves it and sh install.sh runs
it. On Windows, in an administrator PowerShell,
irm https://ovenlight.app/install.ps1 -OutFile $HOME\install.ps1 saves it and
powershell -ExecutionPolicy Bypass -File $HOME\install.ps1 runs it. Reading it at its
address in a browser isn't the same: the site could send your terminal another one.
The script takes the verified steps for you, all but comparing
the index's serial with the one your connector holds. Then it unpacks the archive into
~/ovenlight-connector ($HOME\ovenlight-connector on Windows) and runs its install
script. On a Mac or Linux, when ~/.local/bin isn't on your PATH, it adds the line that
puts it there to your zsh or bash profile and names the file, or prints the line for
another shell. Running it again installs the latest release over yours, keeping your apps,
node state and update settings.
Checking the signature needs an ssh-keygen that can read the release key: macOS 14 or
later, or OpenSSH 8.2 or later on Linux and 8.9 or later on Windows. Without one, the
script says so and checks the download against the index alone, trusting HTTPS as
installing by hand without the check does. On Windows it then also checks that Snowy
Ghost LLC signed ovenlight.exe, install.ps1 and uninstall.ps1.
What it trusts has the rest.
Publish an app
Run your app on the computer with the connector. It must serve plain HTTP on 127.0.0.1,
and serve static files only from a public folder, never from the project's folder with its
data and code. Give it a port nothing else uses (on a Mac, avoid 5000 and 7000, where
AirPlay Receiver listens). No app yet? ovenlight new "Family List" --dir ~/src makes a
starter app that suits Ovenlight and says how to publish it.
Choose a name you don't mind people seeing: once the app has its HTTPS certificate, public Certificate Transparency logs list its full address, which includes your tailnet's name. Then:
ovenlight publish --port 4317 --name "Recipe Box"
Recipe Box becomes its own node, at https://recipe-box.<your-tailnet>.ts.net/. The
hostname comes from the name, and --slug chooses another. For each new app, publish
prints a login link: open it and sign in with your Tailscale account. The node then gets
its HTTPS certificate, which can take up to a minute the first time (publish waits for
it when the node is already signed in), and the connector renews it before it runs out.
An app's sign-in expires on your tailnet's key expiry schedule (180 days by default), and
doctor warns 30 days ahead. A stored API credential (Share an app)
turns key expiry off, and with an API access token, later apps sign in as you without the
browser (the first still needs its login link).
These commands help along the way, and Commands lists every one:
ovenlight status: apps, node state, URL, owner, whether each app answers, and any login link.ovenlight doctor: checks, each with its fix. It exits 1 on any failure.ovenlight check recipe-box: how the app will look and work on the phone, with fixes.ovenlight unpublish recipe-box: stops serving it (see Unpublish).
Open it on your phone
On your phone, open Ovenlight, choose Use My Own Computers, tap Connect, and sign in with the same Tailscale account. If you've already joined someone else's app, Use My Own Computers is in Settings, under the More button. If your tailnet has device approval on, approve the phone in the admin console. An app you signed in with a login link waits there for approval too, listed by its slug. Ovenlight then finds your published apps by itself. Add App, under the More button, also takes an app's address, but only one on your own tailnet; apps on other people's computers come by invite.
Keep an app running
An app started in a terminal stops with that terminal, and doesn't come back after a
restart. To have the connector run it instead, stop your copy (Ctrl-C in its terminal,
never pkill by name) and, in the app's folder, give publish the command that starts
it:
ovenlight publish --port 4317 --name "Recipe Box" --run 'npm start'
ovenlight logs recipe-box -f follows its output (-n <lines> shows more of it), and
ovenlight restart recipe-box stops the command and starts it again.
The connector runs the command in the app's folder, with PORT set to the app's port and
HOST to 127.0.0.1. That folder is the one --dir names. Without --dir, an app given
a command when it has none (a new app, or one after --run=) takes the current
directory, and keeps it until --dir changes it. Keep projects out of /tmp, which a
restart empties, and out of Desktop, Documents, Downloads, iCloud Drive, cloud storage
folders and external drives: macOS asks on the Mac's screen before the connector may use
those, and the app waits until you click Allow.
A project's own tools aren't on the command's PATH: run them through npx (npx vite)
or an npm script, and a Python virtual environment's tools by path (.venv/bin/uvicorn,
or .venv\Scripts\uvicorn on Windows) or through uv run.
On macOS and Linux, the command runs with /bin/sh in your login shell's environment,
without what only a terminal's shell sets up. So a tool set up only in ~/.zshrc or
~/.bashrc (nvm, pyenv) isn't found, and ovenlight logs <slug> says command not found.
Put its directory first on PATH in the command, such as
--run 'PATH=/path/to/node/bin:$PATH npm start', with the directory from
command -v node. A variable exported only in those files is missing too, and nothing
warns about it: keep settings the app needs in a file in its folder, outside the public
one. On Windows, the command runs with cmd, without the connector's administrator
rights. Keep it running in the building guide has the
rest: quoting, virtual environments and Windows.
Whenever the command exits, the connector starts it again, waiting longer each time, up to
a minute. Once a run has lasted a minute and served on the app's port, the wait starts
over. Output goes to <slug>.log in ~/Library/Logs/ovenlight on macOS, and in the
logs folder of the state directory on Linux and Windows. status
shows whether the command runs. Publishing the app again keeps the command and folder, and
--run= or unpublish stops running it.
Unpublish
unpublish stops serving an app. Its node stays in your tailnet, offline, so publishing
it again needs no new login. A shareable app's node is deleted instead
(Share an app). On your phone, the app's tile stays until you remove it
(touch and hold it, then choose Remove App). Ovenlight doesn't add a removed app back,
even when you publish it again: add it by its address with Add App, under the More button.
Connect a coding agent
The connector is also an MCP server, ovenlight mcp, which speaks over stdio. Through it,
your coding agent (Claude Code, Codex and others that support local MCP servers) reads the
building guide, publishes your app, checks it, restarts it, and reads its logs and the
feedback people send. Install the connector first, then connect the agent on
the same computer. If you build on your laptop and run your apps on a Linux server of
your own, set up the server and give your agent the note
in Let your agent send your apps instead.
In Claude Code, install Ovenlight's plugin. It adds the MCP server and a skill that reads the guide whenever you ask to put an app on your phone:
/plugin marketplace add snowy-ghost/ovenlight
then:
/plugin install ovenlight@snowy-ghost
If you install the connector after the plugin, reconnect the ovenlight server in /mcp.
In Codex, on a Mac:
codex mcp add ovenlight -- \
~/Library/Application\ Support/ovenlight/bin/ovenlight mcp
On Linux, the path is ~/.local/state/ovenlight/bin/ovenlight (under $XDG_STATE_HOME
when that's set). On Windows, in PowerShell:
codex mcp add ovenlight -- "$env:LOCALAPPDATA\ovenlight\bin\ovenlight.exe" mcp
To add only the MCP server to Claude Code, without the plugin's skill, on a Mac (on Linux, with the path above):
claude mcp add --scope user ovenlight -- \
~/Library/Application\ Support/ovenlight/bin/ovenlight mcp
On Windows, in PowerShell:
claude mcp add --scope user ovenlight -- "$env:LOCALAPPDATA\ovenlight\bin\ovenlight.exe" mcp
For another agent, add a local MCP server whose command is the connector's binary and
whose one argument is mcp. Write the binary's path out in full, with no ~ or
%LOCALAPPDATA%, such as /Users/you/Library/Application Support/ovenlight/bin/ovenlight
on a Mac (What it installs has the path on each system).
Then ask your agent to put your app on your phone. It publishes and checks the app, and asks you for what only you can do, such as opening the login link and connecting your phone.
The MCP tools
| Tool | Arguments | What it does |
|---|---|---|
guide |
none | The building guide, as ovenlight guide prints it |
status |
none | What ovenlight status --json shows: your apps, their addresses, whether each answers, sharing and updates |
doctor |
none | The checks ovenlight doctor runs, each problem with its fix |
check_app |
slug |
The checks ovenlight check runs: how one app will look and work on the phone |
publish |
name, with port for a new app, and an existing app's slug to update it or change its name |
Publishes an app or updates one, keeping the command the connector runs for it |
unpublish |
slug |
Stops serving an app that isn't shareable, and stops its command |
restart_app |
slug |
Restarts an app the connector runs, then lists the checks that fail or warn once it's back |
logs |
slug, and lines (100 unless you say, up to 1000) |
The last lines of an app's output, for an app the connector runs |
feedback_list |
app (a slug) and limit (20 unless you say, up to 200), both optional |
The notes and screenshots people sent from Ovenlight, newest first, each screenshot as the path of a PNG on the computer |
The MCP server has no tool to share an app or invite anyone, or to set the command that
runs an app. An agent with a shell sets an app's command with
ovenlight publish --slug <slug> --dir '<folder>' --run '<command>', which you approve.
Once the app is shareable, you change its command yourself, in a terminal.
The MCP server lists what else takes a terminal, and why.
An agent with a shell can run the commands too. ovenlight doctor --json and
ovenlight check <slug> --json print an object with ok, false when a check fails, and a
list of checks. Each check has an id, the app it's about when there is one, a
status of ok, warn or fail, and a message. Most problems also have a fix and an
actor, which says who applies it: agent or person. A fix the person makes on a web
page, such as the Tailscale admin console, comes with its url. Both commands exit 1 when
a check fails.
Share an app
Your friend needs only Ovenlight. Their phone joins your tailnet as a guest device that can reach only the apps you invited them to, and none of your other devices.
Sharing uses tagged devices, which Tailscale's plans count: each shareable app is one, and
so is each guest's phone, however many of your apps it holds. Three shared apps and ten
guests use 13, for example. The free Personal plan includes a set number and charges for
more (pricing). Removing a guest from all your apps
deletes their device, so it stops counting. ovenlight guests says how many tagged
devices your tailnet has and how many are Ovenlight's, and when each guest last opened
each app.
First create an API access token in the Tailscale admin console, under
Settings, Keys, and store it once;
auth set asks for it, with input hidden:
ovenlight auth set
Tokens expire after at most 90 days; run auth set again with a new one. An OAuth client
works too (auth set --oauth-client-id <id>);
security.md lists the scopes it needs.
Then make each app shareable:
ovenlight publish --slug recipe-box --shareable
It asks you to confirm the owner, then shows the exact change it will make to your tailnet
policy, and changes nothing until you type yes. Only one connector per tailnet can share
apps, so it refuses while a node in the tailnet is tagged for an app and isn't one of
this connector's own, and says what to do. security.md
explains what it changes and what it refuses to touch.
Then, for each person:
ovenlight share recipe-box --to "Sam"
It prints a link, a QR code and a message to send. The other sharing commands:
ovenlight share garden-log --to "Sam" --existing: the same Sam, for another app or another device.ovenlight guests: guests, the day each last opened each app, and open invites.ovenlight revoke Sam: removes Sam from every app; add--app <slug>to remove just one.ovenlight share --cancel <id>: withdraws an invite that hasn't been used, or a review invite with places left (devices that joined with it keep the app).ovenlight share recipe-box --to "App Review" --review: an invite for an app store's review, which lasts 90 days and works on up to 10 devices (development.md).
Apart from a review invite, an invite link works once and expires after 7 days. Your friend's phone joins without waiting for your approval, even if your tailnet has device approval on (security.md says why). You can also share apps and remove guests in Ovenlight on your phone: touch and hold one of your apps, or open People & Sharing under the More button.
Revoking takes effect at once on the connector. As soon as the guest's phone notices it's no longer in your tailnet, it removes the app and its data. If the guest still has another of your apps, the phone stays in your tailnet and removes the revoked one about 3 minutes later.
With Send Feedback, in an app's menu, guests can send you a note and, unless they turn it
off, a screenshot of the page. ovenlight feedback lists what arrives, and so does People
& Sharing in Ovenlight; security.md has the limits.
Unpublishing a shareable app removes its guests, cancels its open invites, deletes its
node from the tailnet (so publishing it again logs in a new node, which serves the app's
existing HTTPS certificate) and offers to take its tags and guest rule out of your
policy. When any of that fails or has to wait, unpublish says what's left and what to
run, and security.md has the details.
Update
The connector updates itself. Releases up to 1.0.9 don't: run the install command once, and from then on it does.
It checks once a day and installs a new release two days after it first sees it, at a quiet moment in the night: once nothing has used your apps or the connector for 10 minutes. Each computer's night starts at its own time between 02:00 and 04:00 and ends at 05:00, local time.
- A release that fixes a security problem in yours goes in at the first quiet 10 minutes, day or night.
- On a computer that's never quiet, or asleep every night, updates go in busy or not: a security fix from the start of the next night, and any other update from the start of the first night a day after it was due. Each waits until the connector has been running and awake for 10 minutes.
Installing restarts the connector, so the apps it runs stop and open connections drop for a few seconds (about 10 on Linux, up to a minute on Windows). If the new release doesn't serve your apps again within 10 minutes, the connector goes back to the one before. Unpublishing an app before the new release serves it again also makes the connector go back. On Windows, so does running Stop or Restart from Start and stop in those 10 minutes, or shutting down or signing out of a work, school or domain account. A restart of your computer in those 10 minutes starts the 10 minutes over. A failed update, whether it went back or its download broke off, is tried again at the start of the next night, busy or not.
ovenlight status shows the last check, a release waiting its turn and when it installs,
the last update, one that failed, and whether yours has a known security problem.
security.md has what the connector checks before it installs
anything. Only a release updates itself: from source, pull and run the install script
again.
ovenlight update offstops installing updates. The connector still checks daily.ovenlight doctorwarns while updates are off, says so when no check has worked for seven days, and still fails when your release has a known security problem. Turn updates back on at least once every four weeks, and leave them on untilovenlight statusshows no release waiting: a connector that misses a change of release keys can't verify updates after that.ovenlight update onturns them back on. A release already overdue then installs within the hour, busy or not.
When status or doctor says your release needs its install script, run the
install command again. Install a release by hand
covers checking a release yourself and staying on an older one.
Move to a new computer
The connector keeps everything in two folders: its config (the app list, the commands it
runs for apps, and the API credential) and its state (node state, guests and invites). On
a Mac they are ~/.config/ovenlight and ~/Library/Application Support/ovenlight; on
Linux, ~/.config/ovenlight and ~/.local/state/ovenlight, or under $XDG_CONFIG_HOME
and $XDG_STATE_HOME when those are set; on Windows, one folder holds both:
%LOCALAPPDATA%\ovenlight. Back them up. To move, stop the old computer's connector if it
still runs (the uninstall script stops it and keeps its data), copy the folders to the new
computer and install the connector there: every app and guest carries over. An app the
connector runs needs its project folder at the same path on the new computer, and the
tools its command uses; otherwise publish it again from its new folder with --dir. After
restoring an older backup, run ovenlight guests and revoke anyone who shouldn't be
there: a guest removed after that backup can regain access, and anyone who joined after it
needs a new invite.
Without a backup, first delete the old computer's app nodes in the admin console
(Machines). Otherwise the new nodes come up named <slug>-1, and every saved tile and
invite points at the old name. Then publish each app again and send new invites; existing
guests lose access.
On a new or restored phone, your own apps come back once you tap Connect again. Apps shared with you don't move: ask for a new invite.
Uninstall
Uninstalling changes nothing in your tailnet. So first, with the connector still running,
run ovenlight unpublish <slug> for each shareable app, which
takes it out of your tailnet. Other app nodes stay in your tailnet,
offline, until you remove them in the admin console, and the API credential works until
you revoke it there.
~/ovenlight-connector/uninstall.sh
Add --purge to delete the connector's data too.
On Windows, run this in an administrator PowerShell, adding --purge to the second line
to delete the data too:
cd $HOME\ovenlight-connector
powershell -ExecutionPolicy Bypass -File .\uninstall.ps1
If you installed from another folder, run the uninstall script beside the install script
you ran (connector/uninstall.sh, or connector\uninstall.ps1 on Windows, in a clone).
uninstall.ps1 also takes the bin folder out of your PATH. On Linux, linger stays on,
since other services of yours may need it: loginctl disable-linger turns it off.
--purge deletes node state, the config, the stored API credential, guest records, the
feedback inbox, the policy backups, the updater's record and settings, and the logs.
Without it, all of that stays, so a reinstall needs no new logins and keeps updates off if
you turned them off. Neither deletes ~/ovenlight-connector, the unpacked release the
uninstall script runs from. Afterwards, rm -r ~/ovenlight-connector deletes it, or on
Windows, cd ~ and then Remove-Item -Recurse -Force $HOME\ovenlight-connector. The
PATH line the one-line installer added stays in the profile it named, since other tools
may use ~/.local/bin too.
Troubleshooting
Start with ovenlight doctor. It checks the computer's side and gives a fix for each
problem it finds, such as an app on the wrong address or a node waiting for its login.
ovenlight check <slug> looks at one app the way your phone will. When ovenlight isn't
found, or neither finds the problem, look here.
- Your terminal says command not found for
ovenlight(on Windows, that it isn't recognized). Open a new terminal window: the install puts it on yourPATHonly for windows opened after it. If it's still not found on a Mac or Linux, addexport PATH="$HOME/.local/bin:$PATH"to your shell's profile and open a new window. The profile is~/.zshrcfor zsh, and for bash,~/.bashrcon Linux or~/.bash_profileon a Mac (or the~/.bash_loginor~/.profilebash reads in its place). Until then, run~/.local/bin/ovenlight. On Windows, run& "$env:LOCALAPPDATA\ovenlight\bin\ovenlight.exe". - The connector isn't running.
ovenlight statusgives the command that starts it, the same as in Start and stop. If it starts and then stops again, the end of its log says why, or after a crash the stderr log beside it (the user journal on Linux). What it installs says where each is. - Your coding agent doesn't have Ovenlight's tools. If your laptop sends apps to a
Linux server of your own, that's expected: the agent runs the connector over
ssh(Let your agent send your apps), so don't install the connector on the laptop. Otherwise, install it and connect the agent, then start a new session (or in Claude Code, reconnectovenlightin/mcp). A setting you added by hand must name the binary at its installed path.claude mcp listchecks Claude Code's servers, and/mcpin Codex lists its tools. - Your app doesn't appear on your phone. The phone must be signed in to the same Tailscale account as the computer. To check, tap the More button in Ovenlight, then Settings, then Advanced, which shows the phone's account and tailnet. If it's another account, choose Sign Out in Settings and connect again with the right one. With device approval on in your tailnet, the phone and any app that signed in with a login link wait for you to approve them (Open it on your phone). An app you removed from the phone stays removed until you add it back (Unpublish).
- Ovenlight says it can't reach one of your apps. The computer has to be on, awake
and online (What the computer needs covers a Mac's login and
a laptop's lid). Once it is,
ovenlight statussays whether the connector is running. - The phone shows an error page from the app, such as 400, 403 or Vite's "Blocked
request", though it works at
127.0.0.1. Usually the app turns away its tailnet name, which the phone sends asHostwith every request and asOriginwith a form. Servers that check hosts need to allow.ts.net, as the building guide shows for Vite's dev server and Django.ovenlight check <slug>testsHostbut sends no form. An app you already self-host may need more, whichcheckcan miss: Home Assistant answers 400 until it trusts the connector as a proxy. Follow the app's recipe in Apps you already run, or Any app for one without a recipe. - An app's output in
ovenlight logs <slug>says command not found. A project's own tools, such asvite, aren't on the command'sPATH. On a Mac or Linux, neither are tools set up only in~/.zshrcor~/.bashrc, such as nvm and pyenv. On Windows, a tool installed after you published the app is found once you runovenlight restart <slug>. Keep an app running has the fixes. - A change doesn't show on the phone. Choose Reload in the app's menu on the phone,
after
ovenlight restart <slug>if the app's command doesn't reload when files change. For a new icon or color, runovenlight publish --slug <slug>again, then touch and hold the app in Ovenlight and choose Refresh Icon (the building guide has the details). - Sharing fails with 401 Unauthorized, or
doctorsays the API credential doesn't work. Access tokens expire.ovenlight auth status --checktests the one you stored, andovenlight auth setreplaces it (Share an app). - A friend can't open an app you shared. See Support.
For apps on your own server, Your own server has more, for
ssh and reaching the server.
Commands
ovenlight help lists these. To see a command's flags, add -h after it, as in
ovenlight share -h. For auth and update, add it after both words:
ovenlight auth set -h. Every command also takes --config <file> and --state <dir>,
to use an app list and state directory other than the installed connector's
(What it installs).
Apps
ovenlight new "<App Name>" [--dir <parent>] [--port <n>]: writes a starter app in a new folder under<parent>, on a free port from 20000 to 29999 unless--portpicks one, and prints how to run and publish it.ovenlight publish --port <n> --name "<App>" [--slug <slug>]: publishes an app, or updates the one with that slug (Publish an app).ovenlight publish ... --run '<command>' [--dir <path>]: has the connector run the app and keep it running, and--run=stops that (Keep an app running).ovenlight logs <slug> [-n <lines>] [-f]: the last 100 lines of output of an app the connector runs, or the last-n, fenced in<untrusted-...>tags for coding agents. With-f, it keeps printing new lines, fenced too, until Ctrl-C.ovenlight restart <slug>: stops an app the connector runs and starts it again.ovenlight unpublish <slug>: stops serving an app (Unpublish).ovenlight status [--json]: whether the connector runs, each app's address and whether it answers, any login link, sharing and updates.ovenlight doctor [--json]: checks the whole setup, with a fix for each problem.ovenlight check <slug> [--json]: checks how one app will look and work on the phone, with a fix for each problem.ovenlight guide: prints the building guide for the installed release.ovenlight mcp: the MCP server for coding agents (Connect a coding agent).
Sharing
ovenlight auth set [--oauth-client-id <id>]: stores a Tailscale API access token, or an OAuth client's secret, asking for it with input hidden (Share an app).ovenlight auth status [--check]: which credential is stored, and with--check, whether Tailscale's API still accepts it.ovenlight auth remove: deletes the stored credential. Revoke it in the admin console too.ovenlight publish --slug <slug> --shareable [--owner <login>] [--owner-label <name>]: makes an app shareable once you've confirmed the owner and typedyesto the policy change it shows (Share an app).ovenlight share <slug> --to "<Name>": an invite link, a QR code and a message to send. For someone you already share with, add--existing, or give--person <ID>in place of--to.--reviewmakes one for an app store's review.ovenlight share --cancel <id>: withdraws an open invite.ovenlight guests [--all] [--sync] [--json]: your guests, when each last opened each app, and open invites.--alladds removed guests, and--syncchecks the tailnet's device list first.ovenlight revoke <name | device ID | invite ID> [--app <slug>]: removes a guest from every app, or from one with--app(If something goes wrong says when to use a device or invite ID).--person <ID>, fromovenlight guests, in place of a name removes every device and open invite of that person.ovenlight feedback [--app <slug>] [-n <count>] [--json]: the last 20 notes and screenshots people sent, or the last-n(0 for all), with what guests wrote and every page address fenced in<untrusted-...>tags for coding agents.ovenlight setup-sharing --owner <login> [--owner-label <name>]: changes the recorded owner and how guests see you.--rollbackin their place restores the policy saved before the last change, and--remove <slug>takes an app you no longer share out of the policy (Later changes).
The connector itself
ovenlight version: the connector's version.ovenlight update offandovenlight update on: stop installing updates, and start again (Update).ovenlight update now --index <file>: installs the release a canary index names right away. It's for testing a release (development.md).ovenlight run: runs the connector. Its background service does this, so use the commands in Start and stop instead.
Install by hand
Without the one-line installer, download your computer's archive, unpack it and run its install script.
On a Mac, in Terminal:
mkdir -p ~/ovenlight-connector && cd ~/ovenlight-connector
curl -fsSL https://downloads.ovenlight.app/connector/latest/ovenlight-connector-macos.tar.gz |
tar xz --strip-components 1
./install.sh
On Linux, use linux-amd64 or linux-arm64 in place of macos. If install.sh prints
an export PATH line, add that line to the profile install.sh names and open a new
terminal window. On Linux, if it prints a sudo loginctl enable-linger command, run that
too, so the connector keeps serving with nobody logged in and after a restart.
On Windows, signed in as the account that will own the apps, open PowerShell with Run as administrator and run:
mkdir $HOME\ovenlight-connector -Force | Out-Null; cd $HOME\ovenlight-connector
curl.exe -fsSL https://downloads.ovenlight.app/connector/latest/ovenlight-connector-windows-amd64.zip -o connector.zip
tar -xf connector.zip --strip-components 1
powershell -ExecutionPolicy Bypass -File .\install.ps1
On an Arm PC, use windows-arm64 in place of windows-amd64. The ovenlight command
works in PowerShell windows opened after the install. ovenlight.exe, install.ps1 and
uninstall.ps1 are signed by Snowy Ghost LLC. To check one, open its Properties and look
under Digital Signatures.
To download in a browser instead, use these links:
macOS
(its binary is notarized), Linux
amd64
and
arm64,
and Windows
x64
and
Arm.
These links always point to the latest release. Unpack the archive if your browser
hasn't, then run ./install.sh in its ovenlight-connector-<version> folder. On Windows,
right-click that folder in File Explorer and choose Copy as path. Then, in PowerShell
opened with Run as administrator, type cd and a space, paste the path, press Enter, and
run powershell -ExecutionPolicy Bypass -File .\install.ps1.
Check a download first
To check a download before installing it, fetch it with the index the connector updates
from (latest.json), the index's signature (latest.json.sig) and the release keys from
this repository (allowed_signers). Then check that a release key signed
the index, and that the index names the archive's SHA-256. On a Mac or Linux:
mkdir -p ~/ovenlight-connector && cd ~/ovenlight-connector
archive=ovenlight-connector-macos.tar.gz
base=https://downloads.ovenlight.app/connector
curl -fsSL --remote-name-all "$base/latest.json" "$base/latest.json.sig" "$base/latest/$archive"
curl -fsSLO https://raw.githubusercontent.com/snowy-ghost/ovenlight/master/docs/allowed_signers
ssh-keygen -Y verify -f allowed_signers -I team@snowyghost.com -n ovenlight-latest \
-s latest.json.sig < latest.json
grep -F "\"$archive\": \"$(shasum -a 256 "$archive" | cut -d ' ' -f 1)\"" latest.json
On Linux, put linux-amd64 or linux-arm64 in place of macos, and if shasum is
missing, sha256sum in place of shasum -a 256. The first check prints
Good "ovenlight-latest" signature. On macOS 13, or Linux with OpenSSH before 8.2, it
says unknown or unsupported key type instead: check the download on a Mac with macOS 14
or later, or Linux with OpenSSH 8.2 or later. The second check prints the index, which
names the release after "latest" and its serial after "serial". If both checks pass,
and the serial is no lower than the one ovenlight status shows for the index your
connector holds, unpack the archive and run its install script, in the same window:
tar xzf "$archive" --strip-components 1
./install.sh
On Windows, in PowerShell opened with Run as administrator:
mkdir $HOME\ovenlight-connector -Force | Out-Null; cd $HOME\ovenlight-connector
$archive = 'ovenlight-connector-windows-amd64.zip'
$base = 'https://downloads.ovenlight.app/connector'
curl.exe -fsSL --remote-name-all "$base/latest.json" "$base/latest.json.sig" "$base/latest/$archive"
curl.exe -fsSLO https://raw.githubusercontent.com/snowy-ghost/ovenlight/master/docs/allowed_signers
cmd /c 'ssh-keygen -Y verify -f allowed_signers -I team@snowyghost.com -n ovenlight-latest -s latest.json.sig < latest.json'
(Get-Content latest.json -Raw | ConvertFrom-Json).archives.$archive -eq (Get-FileHash $archive).Hash
(Get-Content latest.json -Raw | ConvertFrom-Json).serial
On an Arm PC, use windows-arm64 in place of windows-amd64. The first check needs
OpenSSH 8.9 or later (ssh -V shows yours): an older one can't read the release key that
signs with Touch ID, and says unknown or unsupported key type. If yours is older, check
the download on a Mac with macOS 14 or later, or Linux with OpenSSH 8.2 or later. If the
first check prints Good "ovenlight-latest" signature, the second True, and the last
the index's serial, no lower than the one ovenlight status shows, unpack the archive and
run its install script, in the same window:
tar -xf $archive --strip-components 1
powershell -ExecutionPolicy Bypass -File .\install.ps1
If a check fails, don't install the download. For five minutes after a release comes out,
the server may still hand out some of the last release's files, so a check that fails
then is worth trying again. A serial that stays lower than the one ovenlight status
shows means the server is sending an older index than your connector holds, which the
other checks pass too: report it to team@snowyghost.com.
Install a release by hand
A release that changes what the install script sets up can't come as an update: status
and doctor say when yours needs its install script. To install it, or the latest
release at any time, run the install command again. To also check that the
index is no older than the one your connector holds, take the
verified steps yourself instead. Either replaces the files in
~/ovenlight-connector, and the install script reinstalls the connector.
Each release from 1.0.1 on also stays in its own folder, such as
https://downloads.ovenlight.app/connector/1.0.9/, with its checksums signed by the keys
in allowed_signers as it was at that release's tag. The release you updated from is in
ovenlight status's Last update line. To install an older release and stay on it, first
run ovenlight update off, or the connector may move to the newest again. Check it on a
Mac or Linux with its version in place of 1.0.9, and the Linux changes above:
mkdir -p ~/ovenlight-connector && cd ~/ovenlight-connector
release=1.0.9
archive=ovenlight-connector-macos.tar.gz
base="https://downloads.ovenlight.app/connector/$release"
curl -fsSL --remote-name-all "$base/$archive" "$base/SHA256SUMS" "$base/SHA256SUMS.sig"
curl -fsSL -o allowed_signers "https://raw.githubusercontent.com/snowy-ghost/ovenlight/connector-v$release/docs/allowed_signers"
ssh-keygen -Y verify -f allowed_signers -I team@snowyghost.com -n file \
-s SHA256SUMS.sig < SHA256SUMS
shasum -a 256 -c --ignore-missing SHA256SUMS
tar tzf "$archive" | cut -d / -f 1 | sort -u
On Windows, check it in an administrator PowerShell, with its version in place of 1.0.9
(a release after 1.0.9 needs OpenSSH 8.9 or later, as above):
mkdir $HOME\ovenlight-connector -Force | Out-Null; cd $HOME\ovenlight-connector
$release = '1.0.9'
$archive = 'ovenlight-connector-windows-amd64.zip'
$base = "https://downloads.ovenlight.app/connector/$release"
curl.exe -fsSL --remote-name-all "$base/$archive" "$base/SHA256SUMS" "$base/SHA256SUMS.sig"
curl.exe -fsSL -o allowed_signers "https://raw.githubusercontent.com/snowy-ghost/ovenlight/connector-v$release/docs/allowed_signers"
cmd /c 'ssh-keygen -Y verify -f allowed_signers -I team@snowyghost.com -n file -s SHA256SUMS.sig < SHA256SUMS'
[bool](Select-String -SimpleMatch "$((Get-FileHash $archive).Hash.ToLower()) $archive" SHA256SUMS)
tar -tf $archive | ForEach-Object { ($_ -split '/')[0] } | Sort-Object -Unique
If the first check prints Good "file" signature, the second the archive's name with OK
(True on Windows), and the third only the release's own folder, such as
ovenlight-connector-1.0.9 (an archive's name carries no version), unpack and install it
as the verified steps do, in the same window.
From source
This needs Git and Go 1.21 or later (brew install go, your package manager's golang,
or go.dev/dl); the go command fetches the version
connector/go.mod asks for when yours is older. On macOS and Linux, clone this repository
and run its install script:
git clone https://github.com/snowy-ghost/ovenlight
cd ovenlight
connector/install.sh
If go env GOTOOLCHAIN says local, as on Fedora and Red Hat Enterprise Linux, go
won't fetch a newer one: run GOTOOLCHAIN=auto connector/install.sh instead.
On Windows, signed in as the account that will own the apps, install Git and Go with
winget install Git.Git and winget install GoLang.Go, open a new PowerShell window so
it finds them, and run git clone https://github.com/snowy-ghost/ovenlight $HOME\ovenlight.
Then open an administrator PowerShell (right-click it and choose Run as administrator)
and run:
cd $HOME\ovenlight
powershell -ExecutionPolicy Bypass -File connector\install.ps1
A binary you build isn't code-signed, so Smart App Control, when it's on, may refuse to
run it; install.ps1 says so when the connector doesn't start.
What it installs
Running the install script again installs the connector it came with, even an older one, and restarts it, keeping your apps, node state and update settings. It installs:
| macOS | Linux | Windows | |
|---|---|---|---|
| Runs as | launchd agent com.snowyghost.ovenlight.connector |
systemd user service ovenlight |
scheduled task ovenlight |
| Binary | ~/Library/Application Support/ovenlight/bin/ovenlight |
~/.local/state/ovenlight/bin/ovenlight |
%LOCALAPPDATA%\ovenlight\bin\ovenlight.exe |
| App list | ~/.config/ovenlight/config.json |
~/.config/ovenlight/config.json |
%LOCALAPPDATA%\ovenlight\config.json |
| State directory | ~/Library/Application Support/ovenlight/ |
~/.local/state/ovenlight/ |
%LOCALAPPDATA%\ovenlight\ |
| Log | ~/Library/Logs/ovenlight.log |
~/.local/state/ovenlight/ovenlight.log |
%LOCALAPPDATA%\ovenlight\ovenlight.log |
On Linux, $XDG_CONFIG_HOME and $XDG_STATE_HOME take the place of ~/.config and
~/.local/state when set. At 20 MB the log moves to ovenlight.log.1 beside it,
replacing the one before. What bypasses the log, such as a crash, goes to
ovenlight.stderr.log beside the log on macOS and Windows, and to the user journal on
Linux (journalctl --user -eu ovenlight). The updater keeps a few files of its own in the
state directory and the bin folder (security.md).
On macOS and Linux, install.sh links ovenlight in ~/.local/bin, never replacing a
file it didn't make. When ~/.local/bin isn't on your PATH, it prints the
export PATH line to add to your shell profile, which the one-line installer adds for
you. On Windows, install.ps1 adds the bin folder to your PATH. Either way, ovenlight
works in terminal windows opened after that.
Start and stop
ovenlight status says whether the connector runs. On macOS:
- Start:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.snowyghost.ovenlight.connector.plist || launchctl kickstart gui/$(id -u)/com.snowyghost.ovenlight.connector(when the connector is loaded already,bootstrapsays it failed, andkickstartstarts it) - Stop, until you next log in:
launchctl bootout gui/$(id -u)/com.snowyghost.ovenlight.connector - Restart:
launchctl kickstart -k gui/$(id -u)/com.snowyghost.ovenlight.connector
On Linux:
- Start:
systemctl --user enable --now ovenlight - Stop:
systemctl --user disable --now ovenlight - Restart:
systemctl --user restart ovenlight
On Windows, in an administrator PowerShell. Stopping disables the task too, since a task that's only stopped starts again within a minute.
- Start:
Enable-ScheduledTask ovenlight; Start-ScheduledTask ovenlight - Stop:
Disable-ScheduledTask ovenlight; Stop-ScheduledTask ovenlight - Restart:
Stop-ScheduledTask ovenlight; Start-ScheduledTask ovenlight