CLI reference
@nomac/cli — a full Mac session from your terminal or agent. Requires Node 20+ and OpenSSH; sync/pull also need rsync. Use WSL on Windows for this workflow. Also see the API reference and the agent install page.
Cloud Mac sessions
Add credit at Your Mac, or from the CLI with nomac credits buy --packs N, which opens a payment link. Mac time is $0.80 an hour, billed by the minute; $10 buys 12.5 hours and credit never expires. nomac credits shows what is left. A Builder subscription is separate. Install the CLI for hosted sessions:
npm install -g @nomac/cli nomac login nomac start --json nomac sync . nomac ssh nomac pull ./build nomac stop --json
Run these in your project directory. Wait for ready before connecting. Queue/setup time is free; time between commands on a ready Mac counts. Disconnecting does not stop the session. Download files before stopping: stopping deletes the VM and its files.
| Command | Purpose |
|---|---|
nomac start --json | Allocate or recover the attached session. Defaults: two-hour duration and ten-minute idle timeout. Inspect returned funding and expiry. |
nomac sync . | Upload changes from this project: every file, .env and keys included, and symlinks as links. Skips node_modules, Pods, DerivedData, build folders, .git and folders .gitignore lists; a ! line in .nomacignore sends any of them. Remote extra files are preserved. |
nomac ssh | Open the remote shell. Use ssh -- COMMAND for a remote command; --forward 8080:3000 forwards a port. |
nomac exec -- COMMAND | Run a command: prints its output and exits with its exit code. Waits at most --wait seconds (default 30) and returns as soon as the command finishes; for builds and tests use --wait 600. A command still running when the wait ends keeps running, and exec exits 75 with how to wait for it. --timeout defaults to 300 seconds, or to --wait when longer, and caps at 600; a first simulator boot needs about 90. In scripts add --request-key KEY and reuse it with the same arguments if the response is lost. |
nomac job JOB_ID [--wait 600] | Check a command that outlasted --wait, or with --wait wait for it, then print its output and exit with its code. Never runs it again. |
nomac output JOB_ID --cursor 0 --stream stdout | Read base64 output; decode data_base64 and resume at next_cursor. Also read stderr. The response identifies truncation and a retained tail. |
nomac pull ./build --from build | Download a remote workspace directory. Use --overwrite only when you intend to replace conflicting local files. |
nomac status | State, cost so far and when the Mac stops; --json for usage, funded deadlines and confirmed cleanup. |
nomac start --prompts ask | The Mac is fully open to your agent by default: no administrator password prompts, the login password in $NOMAC_MAC_PASSWORD. --prompts ask keeps macOS's usual prompts for the session. |
nomac password [--copy] | The Mac's login password for this session (user agent), for the login keychain or a macOS prompt. On the Mac it is in $NOMAC_MAC_PASSWORD. |
nomac input type TEXT | Real keyboard and mouse: type TEXT, key return (or cmd+q), click X Y [--right] [--double], move X Y, scroll X Y up|down [N], password. It reaches the lock screen and password dialogs, and saves a screenshot after (-o FILE, --no-screenshot). |
nomac unlock | Unlock the Mac's lock screen with the session password. |
nomac permit APP camera microphone | Grant an app privacy permissions by bundle ID (camera, microphone, contacts, calendars, reminders, photos, desktop, documents, downloads, or all), so it never asks; also for an app built later. At start: nomac start --permit APP=camera,microphone. |
nomac defaults | What every new Mac starts with. --prompts ask and --permit APP=PERMISSIONS change them; --reset goes back to fully open. A start's own options win for that Mac. |
nomac desktop [--app] | The Mac's desktop in your browser; --app, on a Mac, opens Apple's Screen Sharing instead through the session's SSH tunnel, with a shared clipboard. |
nomac stop --json | Request shutdown and deletion. Verify cleanup_confirmed; stopping only acknowledges the request. |
Native SSH is the flexible path for long commands and transfers requiring symlinks. A local rename can leave the old remote source file: remove obsolete files explicitly. Managed output keeps an 8-MiB combined prefix and, if truncated, a final 64-KiB tail per stream. Tail offsets refer to original bytes. Older output may expire while its completion receipt remains; never rerun a command just to retrieve logs. All guest output disappears when the VM is deleted.
The current hosted CLI does not have nomac run or a session nomac logs alias. For MCP, see the agent setup and full agent guide.
Secrets, signing and shipping
Your App Store Connect key, connected once at Apple, stays on NoMac's server: it never reaches a Mac. Secrets are entered by you, never shown back, and every Mac you start receives them, with their values masked in command output.
| Command | Purpose |
|---|---|
nomac secrets set NAME | Save a variable; the value comes from stdin, hidden when typed, never from an argument. --file PATH saves a file (up to 1 MiB, --destination ~/.netrc to choose where it goes); --from-env-file .env saves every line; --on-request installs it only when named. |
nomac secrets | List secrets by name, where they go and their size. nomac secrets rm NAME deletes one. |
nomac secrets install [NAME...] | Put new or changed secrets on this folder's running Mac now. New Macs receive them on their own. |
nomac secrets request NAME --description TEXT | A link where someone enters the secret in a browser, so it never passes through a chat. |
nomac sign [path] | Set up App Store signing for the project on the Mac: makes the profiles its app and extensions need and installs them with your certificate, then prints a build settings file, export options, the reserved build number and the xcodebuild, Flutter and fastlane commands. The project itself is not changed. |
nomac testflight App.ipa | Send a signed .ipa on the Mac to TestFlight and follow it until it is there: the Mac sends only the file, then Apple processes it with no Mac needed. --notes sets What to Test; --encryption no|yes answers Apple's export-compliance question once per app; --no-wait returns at once (follow with --id). |
nomac publish App.ipa | Send the .ipa (or name a build with --project and --build), set What's New with --whats-new, and stage the App Store version with Apple's blockers. Nothing goes to App Review until you run it again with --confirm. |
nomac notarize App.app | Notarize a Developer ID-signed .app, .dmg, .pkg or .zip on the Mac and staple the ticket. Sign first with codesign --options runtime --timestamp. Keep the Mac running until it is stapled. |
Paths are on the Mac, relative to the synced folder. Steps only a person can take, such as creating the app in App Store Connect or accepting Apple's agreements, are printed with their link.
Builder commands
For existing Builder subscribers only: the Builder plans are closed to new subscriptions. These commands drive the separate build, TestFlight and App Store workflow and do not start full Mac sessions. For a Mac, use the session commands above.
| Command | What it does |
|---|---|
nomac login | Device login: prints a URL + short code, waits for approval in the browser, stores the key in ~/.nomac/credentials.json. No secret ever touches the terminal. |
nomac login <api-key> | Store an nmk_ key directly (CI / power users). Create keys in the dashboard or via POST /api/v1/api-keys. |
nomac push | Pack the current directory (respecting .gitignore, secrets excluded) and upload a snapshot. Auto-creates the project on first push, auto-detects the Xcode project, scheme and bundle id, and runs source-level review lint. |
nomac build | Start a signed release build on a cloud Mac from the latest snapshot. Prints the build id and returns immediately. |
nomac build --smoke | Fast unsigned compile check — use this to verify the project builds before spending a release build. |
nomac build --testflight | Release build that waits (polling every 10s) until the build is live on TestFlight, printing each pipeline stage. --wait is the same flag; combine --smoke --wait for a blocking compile check. Exits non-zero on failure — CI-friendly. |
nomac status [build-id] | Show a build as JSON (defaults to the last build started from this directory). |
nomac logs [build-id] | Print per-step build logs, plus the failure excerpt if the build failed. |
nomac mcp | Run the stdio MCP server — this is what agents connect to. See /install. |
nomac whoami | Show your org, plan tier and usage as JSON. |
Configuration
| Where | Meaning |
|---|---|
NOMAC_API_KEY | API key (nmk_…) — overrides the stored credentials. The way to authenticate in CI. |
NOMAC_API_URL | API base URL. Defaults to https://nomac.app. |
~/.nomac/credentials.json | Key stored by nomac login (file mode 0600). |
.nomac.json | Per-project state (project id, last build) written next to your code. Commit it — CI and teammates then push to the same project instead of creating a new one. |
Builder by hand (existing subscribers)
Everything an agent does over MCP, you can do by hand. One-time setup: sign in at nomac.app, connect your Apple Developer account with an App Store Connect API key (the dashboard wizard walks you through it), then:
cd YourApp/ # the folder with the .xcodeproj npx @nomac/cli login # approve in the browser, once per machine npx @nomac/cli push # upload a snapshot + run review lint npx @nomac/cli build --testflight
About 3 minutes later the build is on your phone in TestFlight. From there the loop is push → build → test on device → repeat. When a build fails, nomac logs shows the failing step and excerpt.
Metadata, screenshots, review checks and the App Store submission itself have no CLI subcommands yet — drive them with curl against the REST API (/review-lint, /metadata, /screenshots, /publish), or let an agent do it. The MCP tools get_review, update_review, prepare_review_reply, get_testflight and manage_testflight cover review and external beta actions. Reviewer correspondence still requires App Store Connect; reply drafts are never reported as sent.
get_store_capabilities, read_store and update_store cover paid pricing/availability, subscriptions, in-app purchases and offers, public customer-review replies, phased releases, product pages, analytics and app webhooks. Discovery returns the exact Apple schemas; reads return reusable app-owned scopes. Writes preview by default and require confirm:true.
upload_store_asset accepts a local file_path for reviewer documents, previews, purchase images and other Apple file resources. It reserves the file, streams Apple's requested byte ranges and commits the checksum. Preserve request_key and the original bytes to resume; poll until ready:true.
Builder in CI/CD (existing subscribers)
The CLI runs on plain Linux runners — no macOS runner, no certificates, no fastlane. Authenticate with NOMAC_API_KEY:
- Create a key: dashboard → API keys (or
nomac loginlocally and reuse it). - Store it as a repo secret named
NOMAC_API_KEY. - Commit
.nomac.json(created on your firstnomac push) so CI builds the same project.
Ship every push to main to TestFlight:
# .github/workflows/testflight.yml
name: TestFlight
on:
push:
branches: [main]
jobs:
testflight:
runs-on: ubuntu-latest
env:
NOMAC_API_KEY: ${{ secrets.NOMAC_API_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npx @nomac/cli push
- run: npx @nomac/cli build --testflightCheap compile check on pull requests (smoke builds don't sign or upload):
# .github/workflows/smoke.yml
name: Smoke build
on: pull_request
jobs:
smoke:
runs-on: ubuntu-latest
env:
NOMAC_API_KEY: ${{ secrets.NOMAC_API_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npx @nomac/cli push
- run: npx @nomac/cli build --smoke --waitWorth knowing before you wire this to every commit:
- Every build attempt counts toward your monthly cap (Starter 50, Pro 300) — including failed ones. Gate the TestFlight job on main, use smoke builds for PRs.
- Build numbers (
CFBundleVersion) increment automatically per project — no version bumping step needed. Bump the marketing version in your Xcode project when you cut a release. build --testflightexits non-zero on failure, so the job fails visibly; runnpx @nomac/cli logs(orGET /builds/:id/failure) to see why.- POSTs accept an
Idempotency-Keyheader — the CLI already sends one, so a retried CI step won't double-start a build.
Questions: support@nomac.app · Agents can file issues directly with the report_issue tool.