Skip to main content

Authentication

Project Setup

Initialization always writes a stable local project.id. When detection succeeds, it also writes the detected build.framework and named profiles for the detected iOS and/or Android platforms. Supplying --project writes that ID into the local file; it does not validate, register, attach, or publish a server project. An Xcode scheme supplied during setup is materialized into the generated build commands, not stored as separate runtime state. Use -y to accept only deterministic local choices without prompts and stop after config creation. Interactive initialization may continue through the existing optional authentication, app, skill, and first-build onboarding after the local file is written. If authentication is needed, the CLI prints an approval URL and short code that may be approved in any signed-in browser. After it writes the project config, authenticated setup can also continue into the same GitHub PR-automation flow as revyl github setup. The locally written project.id remains unverified and unpublished unless that separate server-backed flow succeeds.

Project Config

Inspect, validate, and migrate the nearest local .revyl/config.yaml. Use the global -C <directory> option to select another project root in the same worktree.
config path and config show use the same nearest-config selection as other project commands. config show expects the canonical contract; migrate a legacy file first. To change settings, edit .revyl/config.yaml directly and validate it; the config commands have no separate command for mutating individual YAML keys. config migrate only mutates local project files. A successfully prepared legacy migration uses the same concise clean-or-lossy human summary; a write creates an exact-byte backup. JSON --check contains the complete migration proposal and migration ledger. Legacy top-level test aliases become conflict-checked .revyl/tests/<alias>.yaml files. A matching file for the same remote test is preserved byte-for-byte; any other destination conflict fails migration. The retired top-level workflow alias cache is reported and removed. The default mode asks for confirmation, creates an exact-byte config backup, creates only missing alias files, and atomically replaces the config. --check creates no backup and writes no file; --write performs the same local changes without confirmation. When authenticated, migration may make read-only lookups to reuse or interactively select a project from the verified repository, resolve enabled legacy PR-build app names to exact platform app IDs, and resolve legacy PR workflow names—including the implicit smoke workflow from smoke_every_pr—through authenticated exact-name lookup. Missing, duplicate, or inaccessible workflow matches are omitted and reported instead of stopping the remaining conversion. An enabled PR build is never silently removed: unresolved app or framework meaning is reported as lossy and omitted from the best-effort proposal. Inspect JSON --check before writing; afterward, compare the reported backup or ask a coding agent to reconcile omissions. Use --project when you need to supply the local project identity explicitly. Migration never creates or attaches a server project, pulls or publishes configuration, or otherwise mutates server state. config validate always validates the discovered project config locally. When authenticated and a supported GitHub origin is available, it also verifies GitHub access and checks the project identity, repository root, configuration path, and referenced Revyl resources without writing anything. Validation requires a stable project.id; it does not convert or scaffold a legacy config. config push requires a readable local project config, authentication, and verified access to its GitHub repository. Recovery errors name the exact config migrate, config pull or init, auth login or auth status, github status or github connect, Git-origin or repository-access repair, config validate, or retry command needed for the failed prerequisite. Push then reads the current server state and publishes with an exact absent-or-hash precondition. It makes a single publish attempt so an ambiguous network result cannot be retried as a new write. Projects managed from the default branch reject ordinary manual publication; validate the file and commit the designated .revyl/config.yaml instead. config push --force is an explicit one-publication override. It keeps default-branch management and its reconciliation position intact, so a later default-branch configuration change can replace the forced aggregate. Under manual authority, --force does not change behavior. When no local config exists, config pull verifies the GitHub repository, selects the nearest configured project root containing the current directory, verifies its configuration hash, and atomically creates .revyl/config.yaml at that root. --project may select an explicitly identified, more-specific registered project beneath an ancestor local config, but it never rebinds an applicable active local project with a different ID. It does not replace a legacy or invalid file. With an existing valid file, semantic equality remains a no-op. Divergence creates an exact-byte backup and atomically replaces the file. When the local project was deleted and exactly one active replacement owns the same root, pull performs that backup/replacement without trusting the retired ID. Pull reports the backup path and does not require an interactive confirmation, including in JSON and non-interactive invocations. config authorize-cursor-proof records server-owned human authority for unattended Cursor proof runs after Revyl confirms the caller can launch cloud agents and the caller’s live Cursor repository catalog includes the project repository. It does not edit or publish .revyl/config.yaml, and works for both manual and default-branch configuration authority.

Inherited Launch Configuration

REVYL_INHERITED_LAUNCH_ENV_VAR_IDS is a comma-separated list of stored launch-configuration UUIDs applied automatically by revyl dev, revyl test run, revyl workflow run, and raw revyl device start commands. Cloud Agent Conversations sets it from the launch selection. Explicit --launch-var and --launch-arg-set values remain additive; --no-inherited-launch-vars ignores it entirely. Restart an existing device or dev session before changing launch configuration.

Running Tests

Advanced Run Flags

Explore

Build or refresh an Atlas map from the terminal:
Common run flags:
See Explore from the CLI for output, outcome, and CI semantics.

Dev Loop

revyl dev starts a local development loop backed by a cloud device session. Both Expo and bare React Native projects are supported, plus native Swift/iOS and Android projects via rebuild-based loops. Each revyl dev start creates a dev context — a named, worktree-local dev loop bound to one resolved profile/platform recipe. Use --context for same-repo concurrency; separate worktrees use the default context automatically. The context keeps that recipe for subsequent rebuilds.
Profile and platform selection applies explicit flags first. Without --profile, revyl dev prefers one uniquely named development-like profile, then falls back to a sole eligible choice. Development-like names contain a dev or development token, so dev, development, and ios-dev match but device does not. Any remaining interactive ambiguity prompts; a non-interactive run fails with the choices and the required flag. There is no active/default profile or default platform. revyl dev:
  • starts your local dev server (Expo via npx expo start --dev-client, or Metro via npx react-native start)
  • creates a Revyl relay to expose it to cloud devices
  • resolves the latest build for your current git branch from the selected recipe’s app, then installs it
    • if no branch-matching build exists, it falls back to the latest available build and prints a warning
  • uses the same resolved recipe for a forced local build, remote build, and every rebuild in the context
  • opens a cloud device session wired to the deep link
Normal revyl dev runs suppress advisory HMR diagnostics. Use revyl dev --debug when you specifically need relay/HMR troubleshooting output. In cloud-agent environments, keep the Revyl relay running after Dev loop ready and use device screenshots or revyl device report --session-id <id> --json as source of truth before switching to an external Expo tunnel. For Expo manifest readiness timeouts, use --force-hot-reload as the first diagnostic path. Revyl still starts Expo and verifies relay transport; the flag skips only the manifest and bundle proof so the device can be the source of truth. If the app loads, keep working. If the dev client shows a project load error, restart Expo/Metro or capture a session report with revyl device report --session-id <id> --json. For externally managed Expo tunnels, start Expo yourself and pass either the full dev-client link Expo prints or the raw https://... tunnel URL:
When you pass the full Expo dev-client link, revyl dev uses its scheme and prefix directly, so selected-build metadata is not required. For a raw tunnel URL or normal startup, Expo scheme and prefix information comes from the selected build’s metadata. After the dev loop is running, use revyl device commands to interact:

Device-first flow

Start a plain device session first, then attach and run the dev loop on it:
When a context has an attached session, revyl dev --context <name> reuses it instead of provisioning a new device. Attached sessions are left running when the dev loop exits — use revyl device stop to end them.

Context management

Hot reload providers

Expo deep-links into a dev client via app_scheme; bare React Native loads the JS bundle directly over the Revyl→Metro relay. For provider config schema and the full per-framework setup walkthrough, see Dev Loop Overview.

New Branch Build Flow

Use this when you create a new branch and want revyl dev to run that branch’s build:
If you need to pin exactly one build:

New Branch Direct File Flow (No Build Step)

Use this when you already have a local artifact and want to upload it without running the build command.
  1. Build the artifact with your normal local or CI build command.
  2. Upload it with revyl build upload --file.
  3. Run revyl dev.
Optional explicit version label:
Plain device sessions (no hot reload — these are the base layer revyl dev builds on):

Builds and Dev Mode

All revyl build and revyl build upload commands push to an app container. For config-driven builds, that is the app_id on the resolved recipe. When Git metadata is available, each upload includes its branch and commit. When you run revyl dev, the CLI scans the selected recipe’s app for a build matching your current git branch. If found, it uses that build. If not, it falls back to the latest available build and prints a warning. Each developer gets their own cloud device session, relay, and local dev server — builds are the only shared resource. When you need a new build (by project type):
  • Expo / React Native: Dev mode serves your JS/TS live from your local Metro via a Revyl relay. The binary is just a “dev client shell.” You only need a new build when native dependencies change (new native modules, Podfile changes, Gradle dependency changes, app.json native config).
  • Swift: Every code change requires a new build. The binary is the app.
  • Kotlin/Android (coming soon): Every code change requires a new build.
Team workflow commands:
Tip: For Expo/React Native, multiple developers can use the same dev build and still see their own code changes, since JS is served locally. For native projects, each developer should upload their own branch build.

App Management

An app is a named container for your uploaded builds (e.g. “My App Android”). Tests run against an app.

Build Management

Direct builds select one customer-named profile and its ios or android recipe. There is no active/default profile. If an omitted value has one eligible choice, the CLI resolves it; otherwise interactive use prompts and non-interactive use fails with the available choices. Local and remote builds execute the same inherited recipe from the selected project root.

Remote Build Caches

These commands manage configured path caches. Automatic compilation caches are controlled through the remote build configuration. See Caching.

Uploading a Build

Use revyl build any time you want to refresh the binary on Revyl without re-running tests. Common flow:
  1. Make sure your app is configured and credentials are available.
  2. Run:
When --version is omitted, the CLI defaults to a branch-aware version label: <branch-slug>-<timestamp> (for example feature-new-login-20260227-153000). In detached-head/non-git contexts it falls back to timestamp-only.
  1. Use the uploaded binary by running tests against the latest upload:
Or let Revyl handle build + upload automatically:
Useful companion commands:
  • revyl build list to verify uploads and inspect platform/app history
  • revyl test run <test> --build-id <id> to pin a specific build
For Expo projects, authenticate any tools named by the selected recipe before running the build. Revyl executes the authored command arrays without rewriting eas.json or switching EAS profiles.

Uploading from a URL

For teams that store builds in internal artifact storage (Artifactory, S3, GCS, GitHub Actions), the CLI can ingest an artifact directly from a URL without downloading it locally first:
The backend downloads, validates, and stores the artifact server-side. The --url flag is mutually exclusive with --file.

Preview a just-uploaded build

--preview prints a URL that auto-starts a simulator with that exact build when a signed-in org member opens it. Upload itself does not start a device. Opening the link lands in the normal session viewer. The launch page reads platform from the uploaded build’s app. --json stays the same envelope and adds optional preview_url on the build object (/sessions/launch?buildId=<id>). Refreshing /sessions/launch while a start is in flight opens the same session; after that start settles, opening the durable URL again starts a new device. /sessions/launch?appId=<app_id> resolves the app’s latest build at open time instead of pinning one upload.

Native iOS: Build in Xcode, then revyl dev

For native iOS developers, revyl dev automatically detects the most recent simulator .app from Xcode DerivedData. Build your app in Xcode as you normally would, then run:
The discovery scans ~/Library/Developer/Xcode/DerivedData/<Project>-*/Build/Products/Debug-iphonesimulator/*.app for the most recently modified non-test .app that matches the project in the current directory. Test runner bundles (*Tests.app, *UITests.app) are automatically excluded.

Test Management

For the end-to-end CLI authoring workflow, see Test Configuration.
revyl sync is a deprecated compatibility alias for revyl test sync. Both forms reconcile tests only. Project configuration remains explicit through revyl init, revyl config pull, and revyl config push.

Test Variables

Per-test variables referenced in step descriptions via {{name}} syntax and substituted at runtime. Names may contain letters, numbers, hyphens, or underscores.
Test variables are distinct from Global Variables (org-wide {{global.name}}) and Global Launch Variables (org-wide reusable launch env vars).

Tags

Organize tests with tags for filtering and grouping.

Module Management

Reusable modules can be imported into tests with module_import blocks. For examples, see Creating Tests.

Scripts

Manage code-execution scripts used by code_execution blocks in tests. Supported runtimes: python, javascript, typescript, bash.

File Management

Manage files (certificates, configs, images, media) in your organization’s file library. Tests reference uploaded files via revyl-file:// URIs.
Supported types (with size limits): certificates .pem .cer .crt .key .p12 .pfx .der (50 MB), config .json .xml .yaml .yml .toml .csv .txt .conf .cfg .ini .properties (50 MB), images .png .jpg .jpeg .gif .pdf (250 MB), media .mp4 .mp3 (500 MB).

Global Resources

Org-wide resources available across all tests.

Global Variables

Shared variables referenced via {{global.name}}. Local test variables with the same name take precedence. Use --secret for credentials or tokens; secret values are encrypted and shown as ******** in list/get output.

Global Launch Configuration

Reusable launch configurations are stored at the org level and attached to tests from the web UI. Environment configurations hold key-value startup settings. iOS argument sets hold exact ordered argv tokens and are created and edited in Library > Launch Configurations. Either type can be secret. Getting started with raw device sessions:
Create an environment configuration with a KEY=VALUE pair, then pass its key or UUID to --launch-var. Select a stored iOS argument set by name or UUID with repeatable --launch-arg-set. Append non-secret inline tokens with repeatable --launch-arg=<token>.
Aliases: launch-var, launch-vars, launch-variable.

Workflow Management

Blocking human-terminal test and workflow runs open their completed report by default. Use --no-open to suppress it. No-wait, JSON, GitHub Actions, CI, SSH, and other headless executions never open a browser, even when --open is present. All four launch flags are repeatable. For duplicate keys, inline --launch-env values override --launch-var, and --launch-var overrides values attached to an individual test. Prefer stored --launch-var values for secrets because inline values are non-secret and may be visible in shell history, process arguments, and execution reports. Stored argument sets are flattened in attachment/flag order, then inline tokens are appended in flag order. Mixed workflows skip argument sets for Android children; Android-only runs reject them.

Workflow Settings

Stored overrides and run configuration that apply to every execution of a workflow.

Workflow Quarantine

Quarantined tests still run but their failures are ignored when computing overall workflow pass/fail — useful for unblocking CI while flaky tests are investigated.

GitHub PR Automation

Connect the Revyl GitHub App and configure PR automation for the project root selected by the nearest .revyl/config.yaml.
  • github connect only installs or verifies repository access. It never edits or publishes project configuration.
  • github setup guides the selected project through a managed-profile or finished-artifact CI-upload policy, writes pr_review, and validates the project config. Under manual authority, it confirms publication and publishes the same complete aggregate as revyl config push. Under Git authority, it prepares the designated file for commit without publishing.
  • GitHub installation is repository-wide, while setup, publication, authority, and reconciliation are project-root-specific. Run setup separately from each intended root in a monorepo.
  • Ordinary config push uses an absent-or-hash precondition and is rejected when the project is managed from its designated default-branch config. github setup prepares the designated file for commit in that mode. Run revyl config validate and commit the file, or use revyl config push --force for a deliberate one-time publication that keeps default-branch management enabled.
  • A successful default-branch reconciliation makes that project Git-owned. Fetch or validation failures preserve its current state. Removing the designated file returns only that project to manual authority while retaining its last accepted aggregate and automation; it does not delete the project.
Project roots are immutable. Manual projects can be deleted from the GitHub integration in the web app and recreated at the correct root. Git-owned projects must first return to manual authority through the designated-file flow above. Deletion preserves the repository connection, GitHub installation, repository report destination, historical results, and already-frozen review and build work, which may finish. Pending Cursor proof that has not launched fails closed because deletion removes its live authorization. Deleting the last project shows No projects configured and Add project. There is no CLI project-delete command; after creating a same-root replacement, use revyl -C <replacement-root> config pull to back up and replace a stale local file. Pull adopts a replacement automatically only when exactly one active project owns the old root; otherwise Revyl does not assume where the replacement belongs. The standalone github init and github push commands no longer exist; config push is the direct publication command and never scaffolds. See the GitHub integration for the pr_review schema and the exact server-stored configuration groups.

Device Management

See Live Observability for the full reference on perf, requests, logs, report --artifact, network, and --device-name presets. For raw device sessions, URL-based app flows work in two modes:
  • revyl device start --app-url ... preinstalls the app before the session is ready.
  • To inject launch config at boot, use --launch-var <key-or-id> for stored environment values, --launch-arg-set <name-or-id> for stored iOS argument sets, and --launch-arg=<token> for non-secret inline iOS tokens.
  • revyl device install --app-url ... installs into an already running raw session.
  • revyl device download-file --url ... only downloads the file to device storage; it does not install the app.

Live Stream URL

Every active session streams the device screen over WebRTC. The --json output from device info and device list includes a whep_url field — a standard WHEP playback URL you can embed in your own platform or feed into any WHEP-compatible player.

Device Session Flags

Every revyl device command that acts on a session accepts both spellings. -s takes either form: -s 2 is a local index from revyl device list, while -s 9f3c1a2b-... is the server-issued session_id returned by revyl device start --json. REVYL_SESSION_ID scopes an entire shell to one session, so batch workers do not have to repeat the flag:
Precedence is --session-id, then -s, then REVYL_SESSION_ID, then the active local session. Setting --session-id and -s to different sessions is an error rather than a silent pick. REVYL_SESSION_ID applies only to commands that accept -s. The revyl dev commands pick their session from the dev context instead, so exporting it for a batch leaves them alone. revyl device stop --all ignores REVYL_SESSION_ID, -s, and --session-id and still stops every session revyl device list would show. Use a per-ID device stop when a batch worker should release only its own device.
Local indexes live in a per-project file that every CLI process in the directory shares. Writes to it are serialized, so concurrent device start runs no longer drop each other’s sessions, but an index still only means anything relative to that file. Session IDs are resolved straight from the backend in a single call and never touch the file, so prefer them for anything running more than one session at a time.

Parallel batches

Start each session, keep its session_id, and drive every later command by ID:

Shell Completion

Diagnostics & Utilities

Global Flags

These flags are available on all commands: