How to run the Aucert Mac app
How to build, configure and run the macOS app and the aucert agent from a fresh clone, without
asking anyone. If you only want to use a released build, skip to Install a DMG.
Every command below is run from the repository root unless stated otherwise.
Terminology
Read this first. Most confusion about this app is vocabulary, not steps.
| Term | What it actually is |
|---|---|
| Console | The Next.js web UI (frontend/apps/console). This is the entire interface — the Mac app has no bespoke UI of its own |
| Aucert.app | A thin macOS shell: a WKWebView showing the console, plus a supervisor that starts the agent. It owns no device logic |
| Agent | aucert agent — a Kotlin/JVM daemon. Finds your devices, talks to the relay, holds credentials. All device behaviour lives here |
| CLI | The same binary as the agent. aucert init, aucert devices, aucert run, aucert status, aucert logout, aucert agent |
| Relay | A cloud service that lets a scan worker in Azure reach a device on your Mac. Neither side accepts inbound connections; both dial the relay |
| Pipe | One tunnelled TCP connection through the relay, carrying adb traffic for exactly one device, for the lifetime of one scan |
| Enrollment | Linking this Mac to your Aucert account. Produces two credentials: an agent credential (offers your devices) and a user credential (starts scans) |
| Device ref | dvc_… — Aucert's stable id for one of your devices. Distinct from an adb serial, which changes |
| jlink runtime | A trimmed JRE built into the app/CLI bundle. You do not need Java installed to run them — only to build them |
The single most useful thing to know: the app is a window around the console plus a launcher for the agent. If something device-related is broken, it is the agent, not the app.
Prerequisites
| Need | For | Check |
|---|---|---|
| macOS 13+ | running the app | — |
| Xcode Command Line Tools | building Swift | swift --version |
| JDK 21+ | building the agent | java -version |
| Bazel | building the agent jar | bazel --version |
Android platform-tools (adb) | talking to devices | adb version |
Missing adb? brew install --cask android-platform-tools.
You do not need Xcode.app itself, an Apple Developer account, or Java at run time.
Quick start
Three commands, then sign in.
clients/macos/packaging/build-agent.sh # 1. build the agent + its jlink runtime
clients/macos/packaging/build-app.sh # 2. build Aucert.app (embeds the above)
open .build/dist/Aucert.app # 3. run it
Then, in the app window: sign in, and approve the device-linking prompt.
By default the app points at http://localhost:3000, so you need the console running locally
(tilt up — see tools/local/README.md). To point it at the deployed dev console instead:
AUCERT_CONSOLE_URL=https://dev-console.aucert.ai \
AUCERT_API_URL=https://dev-api.aucert.ai \
AUCERT_RELAY_URL=wss://dev-relay.aucert.ai \
open -a .build/dist/Aucert.app
The four build scripts
All live in clients/macos/packaging/. Each is independently runnable; later ones call earlier ones
when needed.
build-agent.sh — the agent bundle
Builds the CLI's deploy jar (Bazel) and links a trimmed JRE around it, producing a self-contained
aucert launcher that runs on a Mac with no Java installed.
clients/macos/packaging/build-agent.sh
This output is reused byte-for-byte by the next two scripts. Build it once.
build-app.sh — Aucert.app
clients/macos/packaging/build-app.sh [output-dir] # default: .build/dist
Compiles the SwiftPM target and assembles an .app bundle, copying the agent bundle into
Contents/Resources/. That placement is what lets the app find its own agent instead of needing one
on your PATH.
Produces an unsigned bundle. Fine for local use; macOS may warn on first launch (see Troubleshooting).
make-dmg.sh — the distributable
clients/macos/packaging/make-dmg.sh
Builds Aucert.app if absent, then wraps it in a DMG. Signs, notarises and staples only when
signing credentials are present in the environment — without them you still get a working
unsigned DMG, so this is safe to run locally.
package-cli.sh — the CLI, without the app
clients/macos/packaging/package-cli.sh
Tars the same agent bundle into a standalone archive: extract, run ./aucert --version. No .app,
no macOS shell, no system Java. This is what CI and headless machines use.
macOS only. Cross-building a Linux runtime from a Mac is not possible with a stock JDK — jlink
links against the current JVM's platform modules, and producing a foreign-OS image needs that
platform's jmods.
Configuration
Three URLs
| Variable | Points at | Default |
|---|---|---|
AUCERT_CONSOLE_URL | the web UI the app displays | http://localhost:3000 |
AUCERT_API_URL | the product API the agent enrols against | https://api.aucert.ai |
AUCERT_RELAY_URL | the device-tunnel relay | wss://relay.aucert.ai |
Note the asymmetry, because it surprises people: the console URL is the app's setting and defaults to localhost. The API and relay URLs are the agent's settings and default to production. The app only forwards them to the agent it spawns — it does not use them itself.
So an app with no environment set shows a local console while its agent talks to production. That is deliberate (a released build should reach production without configuration), but it means you must set all three together when pointing at dev.
Per-environment values
# Local development (console from `tilt up`)
AUCERT_CONSOLE_URL=http://localhost:3000
AUCERT_API_URL=http://localhost:8080
AUCERT_RELAY_URL=ws://localhost:8081
# Deployed dev
AUCERT_CONSOLE_URL=https://dev-console.aucert.ai
AUCERT_API_URL=https://dev-api.aucert.ai
AUCERT_RELAY_URL=wss://dev-relay.aucert.ai
# Production — the defaults; set nothing
Other settings
| Variable | Effect |
|---|---|
AUCERT_HOME | State directory: credentials, logs, the agent's IPC descriptor. Default ~/.aucert |
AUCERT_LOG | OFF / ERROR / WARN / INFO / DEBUG / TRACE. Default WARN |
AUCERT_AGENT_BINARY | Use an agent at this path instead of the bundled one. For developing the agent against a released app |
AUCERT_MAC_INSPECTABLE | =1 enables Safari Web Inspector on the embedded console. Essential for debugging UI |
Setting them for a GUI launch
Environment variables do not reach an app launched from Finder or Spotlight. Use open:
AUCERT_CONSOLE_URL=https://dev-console.aucert.ai open -a .build/dist/Aucert.app
For a persistent setting, the app also reads Info.plist keys — AucertConsoleURL, AucertApiURL,
AucertRelayURL — where environment wins over plist. A release build with no keys set adds nothing,
leaving the agent's own defaults intact.
Running the agent without the app
Useful for CI, headless machines, or when you want to see the agent's own output.
aucert init # enrol this machine — prints a code and a URL to approve it
aucert agent # run the daemon in the foreground
aucert devices # list the devices this machine is offering
aucert status # enrollment + agent state
aucert run # start a scan from the terminal
aucert logout # drop credentials
aucert --help and aucert <command> --help are authoritative; this list will drift, that will not.
On enrollment producing two credentials: aucert init mints both the agent credential and the
user credential. Linking a Mac through the app's one-click flow mints only the agent one — which
is why aucert run can report "this machine is not enrolled" about a machine the app shows as
connected. Running aucert init afterwards resolves it.
Install a DMG
- Open the DMG, drag Aucert to Applications.
- First launch: right-click → Open (unsigned builds are blocked by double-click).
- Sign in, approve the device link.
Confirm it worked: your device appears under Settings → Devices in the console, and
aucert devices lists it.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
"Aucert" cannot be opened | unsigned build | Right-click → Open. Or xattr -dr com.apple.quarantine /Applications/Aucert.app |
| App opens to a blank window | console URL unreachable | Is tilt up running? Is AUCERT_CONSOLE_URL right? |
| Device not listed in the console | agent can't see it | adb devices first. An emulator must be booted before the agent finds it |
Device shows ONLINE but scans fail relay_device_not_found | registry says online, relay disagrees | The emulator stopped while the agent kept heartbeating. Restart the emulator |
aucert run says not enrolled, but the app shows connected | only the agent credential exists | Run aucert init |
build-agent.sh fails on Java | JDK missing at build time | brew install openjdk@21. Not needed at run time |
| Nothing in the UI responds | console JS crashed | Set AUCERT_MAC_INSPECTABLE=1, relaunch, right-click → Inspect Element |
Logs
tail -f ~/.aucert/logs/agent.log # agent — start here for device problems
log stream --predicate 'process == "Aucert"' # the Swift shell
AUCERT_LOG=DEBUG before launching gives far more.
Where things live
| Path | |
|---|---|
clients/macos/ | the Swift shell (Sources/AucertMac/) |
clients/macos/packaging/ | the four build scripts |
public/cli/ | the Kotlin agent + CLI — all device logic |
~/.aucert/ | credentials, logs, agent IPC descriptor |
.build/dist/ | build output |
Further reading
clients/macos/README.md (in the repo) | why the shell is this thin, and the SPEC-076 rationale |
public/cli/README.md | the agent and CLI in depth |
tools/local/README.md | running the backend + console locally (tilt up) |
| SPEC-076 | client architecture — one agent, three surfaces |
| ADR-028 | why a WKWebView shell rather than native UI |