> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revyl.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration files

> The .revyl/ directory and config.yaml: build profiles, session prep, project settings, and environment overrides.

The CLI uses a `.revyl/` directory for project configuration:

```
your-app/
├── .revyl/
│   ├── config.yaml       # Project configuration
│   ├── tests/            # Local test definitions
│   │   └── login-flow.yaml
│   └── .gitignore        # Allowlist: only config.yaml and tests/ are committed
└── ...
```

## config.yaml

`revyl init` writes the local project config. A minimal project has a stable
project ID and may add session behavior, named build profiles, and PR review
policy.

Device commands do not require this file. `revyl device start` also accepts
legacy session settings without migration and does not validate unrelated
build or PR-review settings. See [Auth And Session Prep](/devices/session-prep)
for discovery and session behavior. `revyl build`, `revyl config validate`,
and `revyl config push` still require the canonical format shown below.

```yaml
project:
  id: "00000000-0000-4000-8000-000000000000"

session:
  idle_timeout_seconds: 1800
  before_script:
    script_path: "scripts/prepare-test-session.sh"
    timeout_seconds: 120
  auth_bypass:
    launch_vars: [REVYL_AUTH_BYPASS_ENABLED, REVYL_AUTH_BYPASS_TOKEN]
    deep_link: "myapp://revyl-auth?token=${REVYL_AUTH_BYPASS_TOKEN}"

build:
  framework: expo
  env:
    NODE_ENV: development
  secrets: [EXPO_TOKEN]
  caches:
    - key: node-modules
      paths: [node_modules]
  profiles:
    development:
      ios:
        app_id: "00000000-0000-4000-8000-000000000001"
        setup_commands: ["npm ci"]
        build_commands:
          - "npx eas build --platform ios --profile development --local --output build/app.tar.gz"
        output_path: "build/app.tar.gz"
      android:
        app_id: "00000000-0000-4000-8000-000000000002"
        setup_commands: ["npm ci"]
        build_commands:
          - "npx eas build --platform android --profile development --local --output build/app.apk"
        output_path: "build/app.apk"
```

See [Mint The Token Per Session](/devices/auth-bypass#mint-the-token-per-session)
for the full before-script contract. `session.before_script` runs before iOS
and Android device sessions, but not Web sessions. For Web, use
`session.auth_bypass.sign_in` for worker-minted sign-in; any data a Web proof
needs must already exist in the preview environment, for example seeded by
the preview's own deploy or a test-login route. See
[Auth And Session Prep](/devices/session-prep) for how those blocks
attach on `revyl device start`.

This example deliberately uses EAS.
Expo recipes that run `expo prebuild` followed by Xcode or Gradle do not need
Expo authentication solely because their framework is Expo.

### Section Reference

| Option                                             | Type       | Description                                                                                                                                                                                                                                                                                                                |
| -------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `project.id`                                       | `string`   | Required stable project UUID. New local projects generate one; `revyl init --project` can supply one. Possessing an ID is not authorization and does not prove that a server project exists.                                                                                                                               |
| `session.idle_timeout_seconds`                     | `int`      | Optional idle timeout for sessions started from this project.                                                                                                                                                                                                                                                              |
| `session.before_script.script_path`                | `string`   | Optional executable path relative to the project root. It may use `../` only while remaining inside the Git worktree.                                                                                                                                                                                                      |
| `session.before_script.timeout_seconds`            | `int`      | Optional bound for the before-script process.                                                                                                                                                                                                                                                                              |
| `session.auth_bypass.launch_vars`                  | `string[]` | Names of launch variables attached at session start. Secret values are not stored in config.                                                                                                                                                                                                                               |
| `session.auth_bypass.deep_link`                    | `string`   | Optional deep link opened after launch. Its `${VAR}` placeholders must resolve from one supported launch-variable source.                                                                                                                                                                                                  |
| `session.auth_bypass.sign_in.path`                 | `string`   | Required app-relative sign-in path for a [worker-minted web sign-in link](/devices/session-prep#web-sign-in-links).                                                                                                                                                                                            |
| `session.auth_bypass.sign_in.secret`               | `string`   | Required name of an organization secret key-value launch variable, never the secret value.                                                                                                                                                                                                                                 |
| `build.framework`                                  | `string`   | Required unless every profile has only a `web` recipe: `ios`, `android`, `react_native`, `expo`, `flutter`, or `web`.                                                                                                                                                                                                      |
| `build.env`                                        | `object`   | Non-secret environment defaults inherited by every recipe. Recipe values override matching keys.                                                                                                                                                                                                                           |
| `build.secrets`                                    | `string[]` | Secret reference names inherited by every recipe. Values never belong in config.                                                                                                                                                                                                                                           |
| `build.caches`                                     | `object[]` | Cache defaults, each with a stable `key` and project-relative or `~/` paths.                                                                                                                                                                                                                                               |
| `build.profiles.<name>`                            | `object`   | Customer-named profile containing an optional `ios` recipe, an optional `android` recipe, an optional `web` recipe, or any combination. No profile is stored as the active/default profile.                                                                                                                                |
| `build.profiles.<name>.<platform>.app_id`          | `string`   | Optional authored Revyl app association. It may remain absent until an execution or server-policy boundary requires and resolves an active organization-owned app.                                                                                                                                                         |
| `build.profiles.<name>.<platform>.setup_commands`  | `string[]` | Optional ordered setup commands.                                                                                                                                                                                                                                                                                           |
| `build.profiles.<name>.<platform>.build_commands`  | `string[]` | Required ordered build commands for a declared recipe.                                                                                                                                                                                                                                                                     |
| `build.profiles.<name>.<platform>.output_path`     | `string`   | Optional project-relative artifact file, glob, or app-bundle path produced by the recipe.                                                                                                                                                                                                                                  |
| `build.profiles.<name>.<platform>.image`           | `string`   | Optional remote build image.                                                                                                                                                                                                                                                                                               |
| `build.profiles.<name>.<platform>.timeout_seconds` | `int`      | Optional bounded recipe timeout.                                                                                                                                                                                                                                                                                           |
| `build.profiles.<name>.<platform>.env`             | `object`   | Non-secret recipe environment merged over `build.env`.                                                                                                                                                                                                                                                                     |
| `build.profiles.<name>.<platform>.secrets`         | `string[]` | Recipe secret references merged with `build.secrets`.                                                                                                                                                                                                                                                                      |
| `build.profiles.<name>.<platform>.caches`          | `object[]` | Recipe caches with project-relative or `~/` paths, merged with matching build-level cache keys.                                                                                                                                                                                                                            |
| `build.profiles.<name>.web.app_id`                 | `string`   | Web app for managed bundles or registered URLs. App-ID-only profiles remain valid for URL registration and device start. Managed builds also require `build_commands` and an output directory.                                                                                                                             |
| `build.profiles.<name>.web.run`                    | `object`   | Optional runtime configuration: `start`, `port` (3000), `healthcheck` (`/`), `start_timeout_seconds` (60; range 10–600), `env_keys`, and `secret_names`. The two lists contain existing plaintext/encrypted organization launch-variable names, never values. See [Web recipes](/remote-builds/configuration#web-recipes). |
| `pr_review`                                        | `object`   | Optional PR review policy containing triggers, build selection, proof policy, workflow IDs, and strict build-check behavior.                                                                                                                                                                                               |
| `pr_review.review_triggers.paths`                  | `string[]` | Optional repository-relative globs. Empty defaults a nested project to `<project-root>/**`; the repository-root project remains repo-wide. Explicit globs replace that default.                                                                                                                                            |
| `pr_review.review_triggers.labels`                 | `string[]` | Optional trimmed, non-empty, unique label names. Positive names are OR filters; prefix a name with `!` to exclude matching pull requests.                                                                                                                                                                                  |

Path globs are trimmed, use forward slashes, contain 1-1,024
characters, and have no leading or trailing slash, repeated slash, NUL, or
`.`/`..` path segment. Invalid values are rejected when the configuration is
validated or saved.

Label filters must already be trimmed and cannot be empty, `!` by
itself, or duplicated.

## Build Configuration

Profiles name complete build intents such as `development`, `pull-request`, or
`release`. Each profile owns its iOS and/or Android recipe. There is no authored
active profile: commands accept an explicit profile or resolve one only when
the choice is deterministic.

```yaml
build:
  framework: react_native
  profiles:
    development:
      ios:
        setup_commands:
          - "bundle exec pod install --project-directory=ios"
        build_commands:
          - "xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyAppDev -sdk iphonesimulator -derivedDataPath build"
        output_path: "build/Build/Products/Debug-iphonesimulator/*.app"
      android:
        build_commands:
          - "cd android && ./gradlew assembleDebug"
        output_path: "android/app/build/outputs/apk/debug/*.apk"
```

Executable recipes require `build_commands`; `setup_commands` is optional.
Initialization materializes detected mobile command, output, and image defaults.
Web is in alpha. To add it to your organization, email [support@revyl.ai](mailto:support@revyl.ai).
Web configuration is manual: add `build.profiles.<name>.web.app_id` for URL
registration and device start, and add your own `build_commands` and
`output_path` for bundle builds. `revyl init` does not guess a Web framework,
package manager, preview host, or CI workflow. See [Web recipes](/remote-builds/configuration#web-recipes).
An Xcode scheme selected during initialization is embedded in the generated
commands instead of being stored as a separate field.

`revyl build` runs in the cloud by default and requires `app_id` before
submission; it can discover the artifact when `output_path` is absent. Managed
PR recipes likewise require an active app. Local execution requires
`output_path`, authenticates, and uploads the result. Interactive local builds
can select or create an app after building; non-interactive builds require
`app_id` in advance. See [Build Configuration](/remote-builds/configuration).

Build-level `env`, `secrets`, and `caches` are defaults. Recipe environment
values override matching build-level keys; secret names are de-duplicated; and
caches with the same key merge their paths. A name cannot be both an effective
environment key and a secret reference.

### Artifact-First CI

If CI already produced the artifact, skip the config-driven build step and
upload it directly:

```bash
revyl build upload --file build/App.app.zip --platform ios --app "$REVYL_IOS_APP_ID" --json
revyl build upload --url "$ARTIFACT_URL" --header "Authorization: Bearer $ARTIFACT_TOKEN" --app "$REVYL_IOS_APP_ID" --json
```

## Development Runtime

The project config has no `hotreload` block and stores no active profile or
platform. A `revyl dev` start applies explicit `--profile` and `--platform`
values first. Without `--profile`, it prefers one unique development-like
profile, where `dev` or `development` must be a complete token, and then infers
only a sole eligible profile or platform. Remaining ambiguity prompts in an
interactive terminal and fails with the valid choices in a non-interactive
session.

The framework identifies the runtime family. One resolved profile/platform
recipe supplies the app and build definition for the dev context, including
its initial local or remote build and subsequent rebuilds. Expo dev-client
scheme and prefix information belongs to the selected build artifact metadata
rather than the current checkout.

## Project Settings

```bash
revyl config path                    # Show the selected config file location
revyl config show                    # Display the local project configuration
revyl config validate                # Validate locally; attempt connected validation with authentication and a supported GitHub or GitLab origin
revyl config migrate --check         # Summarize a legacy conversion without writing
revyl config migrate                 # Confirm, back up, and atomically replace a legacy file
revyl config migrate --write         # Back up and replace without confirmation
revyl config push                    # Publish a manual-authority project configuration
revyl config push --force            # Publish once; the repository's default branch keeps authority
revyl config pull                    # Pull server configuration; back up a divergent existing config
revyl config authorize-cursor-proof  # Authorize Cursor proof runs for this project
```

Project commands select the nearest `.revyl/config.yaml` between the current
directory and the active Git worktree root. Use `-C <directory>` to select from
another directory. `config show` expects the project config format and directs legacy
files to migration.

Migration only mutates local project files. A successfully prepared legacy
migration uses one concise clean-or-lossy human summary; a write creates an
exact-byte backup. JSON `--check` contains the complete migration proposal and
migration ledger.
The default mode asks for confirmation; `--check` creates no backup and writes
nothing; and `--write` skips confirmation. A write creates an exact-byte
config backup, creates
only missing conflict-free `.revyl/tests/<alias>.yaml` files, and then
atomically replaces the config. An existing alias file is preserved
byte-for-byte only when it points to the same remote test; any other conflict
stops migration because the destination is unsafe to write.

When authenticated, migration may reuse or interactively select a project from
the verified repository, resolve enabled PR-build app names to exact platform
app IDs, and resolve legacy PR workflow names through exact, case-sensitive
read-only lookup. Missing, duplicate, or inaccessible workflow names are
omitted and reported, including the implicit `smoke` workflow used by the
legacy `smoke_every_pr` preset. The retired top-level workflow alias cache is
reported and removed. An enabled PR build whose app or framework cannot be resolved
is omitted and prominently reported as lossy rather than silently removed. Inspect
JSON `--check` before writing; afterward, compare the reported backup or ask a coding
agent to reconcile omissions. Migration never
creates or attaches a server project, pulls or publishes configuration, or otherwise
mutates server state.

Edit `.revyl/config.yaml` directly for ordinary changes and run
`revyl config validate`; the config commands have no separate
command for mutating individual YAML keys. Validation does not publish: when
connected validation succeeds, it reports whether the configuration is
published and, when it is not, names the publish step (`revyl config push`, or
a commit on the repository's default branch for Git-managed projects).
When the local file lacks enabled PR review or Proof of Changes, that step first
asks you to configure `pr_review` with its build settings and
`proof_of_changes.enabled: true`; publishing alone does not add those settings.

### Configuration publication and retrieval

GitLab.com and public HTTPS self-managed GitLab repositories use the same
configuration commands. Connect the automation account and install the project
webhook in Revyl first. The Git origin supplies the instance and subgroup path;
self-managed HTTPS ports are preserved. Standard SSH origins assume HTTPS port
443, so use an HTTPS origin when the API uses a different port.

`revyl config push` publishes typed configuration meaning, not raw YAML, and
`revyl config pull` retrieves the server configuration. Neither operation
is part of test sync. Push requires a readable canonical project config,
authentication, a stable `project.id`, a supported GitHub or GitLab origin, and verified
access to the exact repository. A failed prerequisite
names 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 to
recover. Pull accepts the same project config, or—when no config exists—uses
the verified repository and nearest configured project root to create
the complete config. Pull never converts or overwrites a legacy or
invalid file. When an existing config differs from the server, pull
creates an exact-byte backup, atomically replaces the file, and reports the
backup path without an interactive diff or confirmation. If the local project
was deleted and exactly one active replacement owns that same root, pull backs
up and replaces the stale file without trusting its retired ID. Revyl stores these
top-level groups from the project configuration:

- `project.id`
- `session`, including its timeout, before-script declaration, and auth-bypass references
- `build.framework` and every named build profile, including each iOS or Android recipe, app association when present, commands, output, caches, non-secret environment values, secret references, image, and timeout
- optional `pr_review`, including triggers, managed-profile or external-CI build selection, proof policy, workflow references, and strict build-check behavior

Comments, YAML formatting, local tests, the repository remote URL, invocation
directory, and any transient profile/platform selection are not stored as part
of the project configuration aggregate. Secret references are stored by name;
secret values are not sent in this contract.

The server independently verifies the authenticated GitHub repository and
repository-relative project root, recompiles the supplied typed contract, and
checks referenced resources. Manual publication atomically replaces the whole
aggregate using the state observed immediately before the write. Under
authority from the repository's default branch, ordinary manual publication is
rejected: use `revyl config validate`, commit the designated file, and let
reconciliation publish it. `revyl config push --force` deliberately replaces
the aggregate once while preserving that authority and its reconciliation
position. The next newer accepted configuration on that branch can replace
that override. `--force` is a no-op when authority is already manual.

`revyl config authorize-cursor-proof` is separate from configuration publication. It
records server-owned human authority only after live Cursor repository access
is confirmed; it does not change authored configuration, hashes, or authority,
and works for both manual projects and projects managed from the repository's
default branch.

### Project roots and deletion

Project roots and config paths are immutable identity. To correct a root:

1. Delete the manual project from **Settings → Integrations → GitHub**.
2. Choose **Add project root** and enter the correct root.
3. Run `revyl -C <replacement-root> config pull` to create or safely replace its local config.

A Git-managed project cannot be deleted directly. Remove its designated config
file from the repository's default branch and wait for successful reconciliation to return it
to manual authority, then delete it explicitly. Removing the file alone preserves
the project, its configuration, and its automation.

Deletion archives only that project. It preserves the repository connection,
GitHub installation, repository report destination, frozen and historical results,
and already-frozen review and build work, which may finish. Pending Cursor proof
that has not launched is the exception: deletion removes its live authorization,
so it fails closed. Deleting the last project opens the dashboard's form for a
new project root, and nothing is created until you save it. After navigation,
the repository leaves the configured list but remains selectable through
**Add Repo**.
There is no CLI project-delete command. A stale local file remains usable for
local-only commands. Connected project-configuration surfaces—`config validate`,
`config push`, `config pull`, GitHub setup/status, Cursor authorization, and TUI
publication—identify the retired project and explain how to create or pull the
replacement instead of retrying its ID. Automatic pull recovery is limited to one
exact same-root replacement; otherwise Revyl does not assume the old root.

## Environment Variable Overrides

These environment variables configure CLI defaults, credentials, and integration behavior:

| Variable            | Description                                                                                                                      |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `REVYL_API_KEY`     | API key for authentication; normally takes precedence over stored credentials, except after an explicit local `revyl auth login` |
| `REVYL_BINARY`      | Executable path or command name used by SDK and MCP subprocess delegation; resolved again for each delegated command             |
| `REVYL_PROJECT_DIR` | Override the project directory for MCP server; MCP runs and uploads use that project's Git branch when nothing else chooses a branch ([Git branch detection](/builds/branches#git-branch-detection)) |

## .gitignore Defaults

The `.revyl/.gitignore` generated by `revyl init` uses an allowlist approach:
everything inside `.revyl/` is ignored by default except for the shared project
files listed below.

**Committed (shared with your team):**

- `.revyl/config.yaml` — project configuration
- `.revyl/tests/**` — local test definitions
- `.revyl/.gitignore` — the ignore rules themselves

Everything else under `.revyl/` (device sessions, MCP artifacts, PID files, etc.)
is local runtime state and stays out of version control automatically.

## Test Aliases

Test aliases are managed as files in `.revyl/tests/`. Each file maps to a
remote test via `_meta.remote_id`. Explicit `revyl config migrate` externalizes
conflict-free legacy `tests:` entries as stub files; ordinary config and test
commands never migrate them implicitly.
