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.

CommandPurpose
nomac start --jsonAllocate 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 sshOpen the remote shell. Use ssh -- COMMAND for a remote command; --forward 8080:3000 forwards a port.
nomac exec -- COMMANDRun 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 stdoutRead 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 buildDownload a remote workspace directory. Use --overwrite only when you intend to replace conflicting local files.
nomac statusState, cost so far and when the Mac stops; --json for usage, funded deadlines and confirmed cleanup.
nomac start --prompts askThe 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 TEXTReal 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 unlockUnlock the Mac's lock screen with the session password.
nomac permit APP camera microphoneGrant 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 defaultsWhat 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 --jsonRequest 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.

CommandPurpose
nomac secrets set NAMESave 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 secretsList 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 TEXTA 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.ipaSend 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.ipaSend 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.appNotarize 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.

CommandWhat it does
nomac loginDevice 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 pushPack 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 buildStart a signed release build on a cloud Mac from the latest snapshot. Prints the build id and returns immediately.
nomac build --smokeFast unsigned compile check — use this to verify the project builds before spending a release build.
nomac build --testflightRelease 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 mcpRun the stdio MCP server — this is what agents connect to. See /install.
nomac whoamiShow your org, plan tier and usage as JSON.

Configuration

WhereMeaning
NOMAC_API_KEYAPI key (nmk_…) — overrides the stored credentials. The way to authenticate in CI.
NOMAC_API_URLAPI base URL. Defaults to https://nomac.app.
~/.nomac/credentials.jsonKey stored by nomac login (file mode 0600).
.nomac.jsonPer-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:

  1. Create a key: dashboard → API keys (or nomac login locally and reuse it).
  2. Store it as a repo secret named NOMAC_API_KEY.
  3. Commit .nomac.json (created on your first nomac 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 --testflight

Cheap 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 --wait

Worth knowing before you wire this to every commit:

Questions: support@nomac.app · Agents can file issues directly with the report_issue tool.