CLI
The inverse-embed package installs one command under two names, inverse and inverse-embed. Run it with npx, or install it globally.
npx inverse-embed@latest --help
# or
npm i -g inverse-embed && inverse --helpCommands
| Command | What it does |
|---|---|
login [--origin URL] | Signs in through your browser and saves the token. |
deploy [dir] [--name N] [--env .env] [--force] [--quiet] [--json] | Checks env, uploads the app in dir (default: here), streams the build and prints its link. |
logs [id] [--json] | Prints the app's last build: status, error and log. |
apps [--json] | Lists the apps you made. |
share [id] [flags] | Sets who can open an app. With no flags, prints its link and current access. |
domains [id] [--json] | Lists the app's custom domains and re-checks their DNS. See domains. |
domains add <domain> [id] | Serves the app at your domain and prints the DNS records to set. |
domains remove <domain> [id] | Removes a custom domain. |
open [id] | Prints the app's link and opens it in your browser. |
skill install [--dir path] [--print] | Installs the agent skill. See agent skill. |
sync <room link> [flags] | Streams a room's session into your project, and optionally to a webhook. |
mcp <room link> | Runs an MCP server on stdio over the room's context. See MCP. |
share, domains and open default to the app saved in this folder, so you only pass an id for another app.
login
Opens /cli/login in your browser and listens on a one-time 127.0.0.1 port for the answer. After you click Authorize Inverse CLI, the token is saved to ~/.inverse/config.json, readable only by you. Tokens last 90 days. The login waits up to 5 minutes.
deploy
Uploads source only and lets Inverse build it. The upload skips:
node_modules, build output (.next,dist,build,.svelte-kit,.outputand similar) and.git.env*,.npmrc,.yarnrc,vercel.jsonand key files (.pem,.key,.p12,.pfx)- your
.inversefolder
The limit is 500 files and 3 MB. The folder needs a package.json; its name becomes the app name on the first deploy unless you pass --name. New apps start private, and each account can host up to 50. The command waits up to 20 minutes for the build.
Before uploading, deploy looks for the environment variables your source requires (process.env.X and import.meta.env.X without a fallback, and the required keys of a t3-env or zod schema in an env file) and stops if any aren't saved on Inverse. It reads names only, never values. Push them with inverse env push .env, or pass --force when they're optional. It also warns when the app's installed inverse-embed is older than the CLI, with the exact command to update it. Use an exact version: pnpm 11 holds back versions published less than a day ago, so @latest can resolve to an older one.
While it builds, deploy prints each phase (queued, installing, building, deploying, ready or failed) and the build log, dimmed. --quiet hides the log. The whole log is saved to .inverse/deploys/<time>.log, and inverse logs prints the last build's log from Inverse. The first deploy saves the app's id as soon as its build starts, so a failed first build still shows on Inverse Dev with its error, and its variables can be set before you try again.
$ npx inverse-embed deploy
queued…
installing…
│ Running "pnpm install"
building…
│ Running "next build"
ready…
Live: https://www.inverse.at/a/imported-3f9c0a1b2d4e5f60
Manage: https://www.inverse.at/dev
Log: .inverse/deploys/2026-11-01T12-00-00-000Z.logshare
| Flag | Effect |
|---|---|
--private | Only you. |
--link | Anyone with the link. |
--public | Anyone, and listed in the store. |
--email a@b.co | Adds a person to the share list. Repeat it for more people; up to 50 in total. |
--price 5.00 | A price in dollars, up to 1000. 0 makes it free. Buyers pay once through Stripe Checkout, once this Inverse server has a Stripe key. |
--pens / --no-pens | Turns pen tools (draw and highlight over the app) on or off in its rooms. Off by default. |
Pick one of --private, --link and --public. You can combine it with --email and --price. See sharing for what each level means.
npx inverse-embed share --private --email ana@team.co --email li@team.codomains
Serves the app at a domain you own, up to 5 per app. The domain shows the app itself; its Inverse link (/a/<id>) stays the way into the live room. After add, set the printed records at your DNS provider: an A record to 76.76.21.21 for an apex domain, or a CNAME to cname.vercel-dns.com for a subdomain, plus a TXT record when ownership needs verifying. Names are relative to your apex (@ is the apex). Run inverse domains to check again; DNS can take a few minutes to an hour.
$ npx inverse-embed domains add app.example.com
app.example.com waiting for DNS
Set at your DNS provider:
CNAME app cname.vercel-dns.comsync
Streams a room into .inverse/sessions/<room>/ while it runs: recap.md, session.json, events.jsonl and intent.json. Stop it with Ctrl+C.
| Flag | Effect |
|---|---|
--out dir | Where sessions go. Default .inverse/sessions. |
--webhook URL | POSTs each new event once as { room, type, at, data }. |
--secret KEY | Signs webhook requests with x-inverse-signature: sha256=<HMAC of the body>. |
npx inverse-embed sync https://www.inverse.at/apps/<room> --webhook https://your.app/hooks/inverse --secret "$INVERSE_WEBHOOK_SECRET"Add .inverse/ to .gitignore (or at least .inverse/sessions/ and .inverse/deploys/): it holds what your visitors wrote and said, and your build logs.
Environment
| Variable | Use |
|---|---|
INVERSE_TOKEN | A CLI token, used instead of the saved login. For CI and agents. |
INVERSE_ORIGIN | The Inverse server. Default https://www.inverse.at. |
INVERSE_WEBHOOK_SECRET | The sync webhook secret when you don't pass --secret. |
Environment variables win over ~/.inverse/config.json. Copy a token from that file after inverse login and keep it in your CI secrets.
.inverse/app.json
The first deploy writes the app's id and link next to your source. Later deploys from this folder update the same app, so its link, name, sharing, price and branding stay. Only the account that owns the app can redeploy it. Delete the file to deploy as a new app.
{
"id": "imported-3f9c0a1b2d4e5f60",
"link": "https://www.inverse.at/a/imported-3f9c0a1b2d4e5f60"
}--json for agents and scripts
deploy, logs, apps, share and domains take --json. The result is printed as JSON on stdout. A failure prints { "error": "…" } and exits with status 1.
deploy --json streams instead: one JSON event per line as it happens, and the last line is always the outcome, a result or an error. Read the stream as it comes, or take the last line with tail -1.
| type | Fields |
|---|---|
warning | warning: the app's inverse-embed is older than the CLI, with the command to update it. |
preflight | missing: required env names not saved on Inverse. Not sent with --force. |
phase | phase (queued, installing, building, deploying, ready, failed), at. |
log | at, stream (stdout, stderr, command…), text. ANSI colors and obvious tokens are stripped. |
result | The app (below), plus logFile. |
error | error, missing (env names the build blamed), log (the build's last lines), logFile, hint. |
{"type":"phase","phase":"queued","at":1793491200000}
{"type":"phase","phase":"building","at":1793491203000}
{"type":"log","at":1793491203120,"stream":"stdout","text":"Running "next build""}
{"type":"phase","phase":"ready","at":1793491260000}
{"id":"imported-3f9c0a1b2d4e5f60","name":"my-app","description":"","url":"https://…","ownerName":"Ana","mine":true,"visibility":"private","shared":[],"price":0,"createdAt":1793491200000,"updatedAt":1793491260000,"link":"https://www.inverse.at/a/imported-3f9c0a1b2d4e5f60","type":"result","logFile":".inverse/deploys/2026-11-01T12-00-00-000Z.log"}price is in cents. apps --json prints an array of the same app objects.