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

# Cloud commands

> login, connect, logout and sync: signing in, choosing a repository's factory, and uploading launched sessions.

## outerlayer login

Sign in to your account, and save the login and the gateway address to `~/.outerlayer/config.json`.

```bash theme={"system"}
outerlayer login [--dashboard <address>] [--no-browser] [--check <id>] [--no-input] [--json]
echo "$OUTERLAYER_KEY" | outerlayer login --url <gateway url> --app-id <factory id>
```

| Flag | Default | What it does |
| - | - | - |
| `--dashboard <address>` | `https://app.outerlayer.ai` | The dashboard to sign in on. It is saved as `dashboard`, so `logout` revokes the token there. The gateway address comes back with the login and is saved as `url`. |
| `--no-browser` | off | Print the approve link without opening a browser. |
| `--check <id>` | none | Check a login that is still waiting for approval, once. |
| `--url <url>` | none | Gateway base URL for a piped key. Required with a piped key. |
| `--app-id <id>` | none | Factory id for a piped key, from **Settings → General**. |
| `--no-input` | off | Never wait on a person. Exit 1 with a message when no key is on stdin. |
| `--json` | off | On success, print the config file's path, the gateway address and your email. It never holds the token. |

With no key on stdin, you sign in to your account by browser approval:

1. `login` prints an approve link and an 8-character code, such as `K7QM-2XPD`, and opens the link.
2. Sign in to the dashboard if needed. Check that the code on the approve page matches your terminal.
3. Select **Approve**. There is no factory, organization or permission to choose. `login` prints `Logged in as <your email>`.

The link and code expire after 10 minutes. If they do, run `login` again.

A login picks no factory. Run `outerlayer init` in a repository next: it runs [`connect`](#outerlayer-connect), which chooses one. Until then, a command that needs a factory exits 1 and tells you so.

* The login acts with your role on the factory a command names. Within about five minutes, it shrinks if your role is reduced and stops on a factory you leave.
* A login cannot create or revoke factory keys, change members, roles, billing or single sign-on, or delete a factory.
* A second `login` on the same machine revokes the token it replaces.
* To see or revoke every machine signed in to your account, open **Security** on your profile (`/profile/security`). The **CLI logins** list has a **Revoke** button for each.
* **A factory key always wins over a login.** A key from `OUTERLAYER_API_KEY` or `apiKey` in the config file is sent instead, so CI and runners keep acting as their key. See [Credential resolution](/reference/cli#credential-resolution).

**From an agent or a script.** When stdout is not a terminal, `login` does not wait. It prints one JSON document and exits 75:

```json theme={"system"}
{"status":"waiting","url":"https://app.outerlayer.ai/cli/login?code=K7QM-2XPD","code":"K7QM-2XPD","check":"outerlayer login --check 3f0e6a52-…"}
```

Give the `url` to the person. Run the `check` command until it stops exiting 75. The waiting login's one-time key is kept in `~/.outerlayer/login-pending/` until it finishes or expires.

**A factory key you already have.** Pipe it on stdin, as in the second synopsis line. Use this in CI and on runners. The key is saved as `apiKey`, never accepted as a flag, and never printed.

* `login` reads stdin to the end, so secret managers that prompt work.
* After 1 second with no input, it says on stderr that it is waiting for a key.
* A pipe silent for 30 seconds counts as nothing piped, and `login` starts a browser login.
* A piped key is checked for shape only. The first `sync` proves it.

| Exit code | Meaning |
| - | - |
| 0 | Signed in, or the piped key is saved. |
| 75 | Still waiting for approval, or the dashboard could not be reached, answered 5xx or asked you to slow down. The reason is on stderr. Run the check again. |
| 2 | `--url` or `--app-id` was given with nothing piped. Nothing was requested. Drop them, or pipe the key. |
| 1 | Denied, expired, delivered elsewhere, or the answer could not be read. Run `login` again. |

See also: [Quickstart](/quickstart), [API keys](/api-keys#give-the-key-to-the-cli), [What leaves your machine](/what-leaves-your-machine#other-commands-that-reach-the-network).

## outerlayer connect

Choose the factory this repository's work goes to.

```bash theme={"system"}
outerlayer connect [--factory <org>/<factory>] [--json]
```

| Flag | Default | What it does |
| - | - | - |
| `--factory <org>/<factory>` | none | The factory to use, by the `<org>/<factory>` pair in its dashboard address, or by id. Wins over a saved connection. |
| `--json` | off | Write one JSON document to stdout and the text to stderr. It holds `status` and `reason`. When a login chose the factory, it also holds `repository`, `factory` and `linked` (`null` when the link could not be checked), plus `linkUrl` when `linked` is false. |

Run it in a repository after `login`. `outerlayer init` runs it for you. With a login, it takes the first rule that gives one answer:

1. `--factory`.
2. The repository's saved connection, while you can still use that factory.
3. The one factory the repository is linked to.
4. The one factory you can use.
5. On a terminal, it asks. With no terminal, it exits 1 and lists each factory with the `--factory` value to pass.

It always says what it chose and why, and how to change it. `connect` never links a repository. When the repository is not linked to the chosen factory, it saves the connection anyway and exits 2 with the link to fix it:

```text theme={"system"}
Connected github.com/acme/app to factory "Studio" in Acme Inc (the only factory you can use).
Studio has no link to github.com/acme/app yet, so work from this repository cannot reach it.
Link it on the factory's Work page: https://app.outerlayer.ai/orgs/acme/factories/studio/work
  1. Connect GitHub repository, and grant the App access to acme/app.
  2. Link repository, and pick acme/app and its default branch.
Then run `outerlayer connect` again.
```

If the gateway cannot answer whether the repository is linked, `connect` says it could not check and exits 0.

When `.outerlayer/` holds files that are not committed, a `connect` that succeeds ends with a line telling you to commit and push them. The factory reads your policy from the default branch, so no check runs on pull requests until it is there.

**With a factory key,** the key decides the factory. `connect` changes nothing, makes no network call and exits 0. `--factory` naming another factory is an error.

**Where the connection is stored.** In the repository's shared git directory, at `outerlayer/connection.json`, readable by you only. Every worktree of the clone shares it. It is never committed, and it goes away with the clone. Two repositories on one machine can go to two factories. How a login's factory is chosen, including what overrides the connection, is in [Credential resolution](/reference/cli#credential-resolution).

| Exit code | Meaning |
| - | - |
| 0 | Connected and linked, the link could not be checked, or a factory key decides the factory. |
| 2 | Connected, but the repository is not linked to the factory. Link it at the printed URL, then run `connect` again. |
| 1 | No login (run `outerlayer login`), no git repository or remote, no factory you can use (the message links to create one), or more than one choice with no terminal (pass the listed `--factory`). |

### Automatic connection

`work build`, `emit artifact` and the other commands below connect automatically when one factory fits:

* `outerlayer work build`, `work list`, `work status`, `work remove` and `work pr`
* `outerlayer emit <name>`, `emit artifact`, `emit criteria`, `emit finding` and `emit findings`

They connect when all three hold:

* You are logged in.
* The repository has no saved connection.
* Exactly one factory fits by rules 2 to 4 above.

They never ask. They print one line to stderr naming the factory and why, save the connection, and carry on. They skip the link check, so `outerlayer doctor` still reports a missing link. When more than one factory fits, they refuse and list each factory with the `--factory` value to pass to `outerlayer connect`.

```text theme={"system"}
Using factory "Studio" in Acme Inc for github.com/acme/app (the repository is linked to it); saved for this checkout.
```

The line changes only which factory this checkout uses. It never links a repository.

A command given `--repo`, such as `work build --repo acme/app`, chooses the factory by the links of the repository it names, not by the checkout's own remote. So running it from another checkout, such as a factory repository, still reaches the factory that builds `acme/app`. The connection is saved for the checkout you ran it from.

These never connect on their own, so run `outerlayer connect` before them:

* The other `work` subcommands: `claim`, `renew`, `release`, `comment`, `threads`, `open-pr` and `link-session`.
* `sync`, live upload and the MCP server.

See also: [Quickstart](/quickstart), [Connect a repository](/connect-repository), [Capture commands](/reference/cli-capture).

## outerlayer logout

Revoke this machine's login and remove it.

```bash theme={"system"}
outerlayer logout
```

`logout` deletes `accountToken` and `dashboard` from the config file. A factory key (`apiKey`), its `appId` and every repository's saved connection stay.

| Exit code | Meaning |
| - | - |
| 0 | Revoked and removed, or not logged in. |
| 1 | The local copy is removed, but the dashboard could not be reached or did not confirm. Revoke the token on the **CLI logins** list at `/profile/security`. A revoked token stops working within about five minutes. |

See also: [What leaves your machine](/what-leaves-your-machine#other-commands-that-reach-the-network).

## outerlayer sync

Upload the sessions launched with `OUTERLAYER_WORK`, incrementally.

```bash theme={"system"}
outerlayer sync [--dry-run] [--all] [--tier <tier>] [--limit <n>] [--quiet] [--json]
                [--url <url>] [--app-id <id>]
                [--root <dir>] [--codex-root <dir>] [--cursor-root <dir>]
```

| Flag | Default | What it does |
| - | - | - |
| `--dry-run` | off | Print what would leave this machine, per session. Send nothing and make no network calls. |
| `--all` | off | Ignore the checkpoint and re-send everything. Safe to repeat. |
| `--tier <tier>` | `OUTERLAYER_TIER`, then `tier` in the config file, else `full` | `full`, `redacted` or `metrics`. |
| `--limit <n>` | all | Only the N most recent sessions. |
| `--quiet` | off | No output. Exit code only. |
| `--json` | off | Machine-readable output. |
| `--url <url>` | see [Credential resolution](/reference/cli#credential-resolution) | Gateway base URL. |
| `--app-id <id>` | see [Credential resolution](/reference/cli#credential-resolution) | Factory id. |
| `--root <dir>` | `~/.claude/projects` | Claude Code transcript root. |
| `--codex-root <dir>` | `~/.codex/sessions` | Codex sessions root. |
| `--cursor-root <dir>` | `~/.cursor/chats` | Cursor chats root. Needs Node 22.5 or later. |

```bash theme={"system"}
outerlayer sync --dry-run
outerlayer sync --tier redacted
```

The summary line counts sessions uploaded and skipped, with the reason:

| Summary says | Meaning and fix |
| - | - |
| `N skipped — not launched with OUTERLAYER_WORK` | Started without the variable. It never uploads. See [Launch a session](/launch-a-session#if-you-forgot-the-variable). |
| `N skipped by repo filter` | The `repos` setting in the config file excludes the session's repository. |
| `the launch spool could not be read, so none could be checked` | Nothing is sent this run. Fix the launch spool's permissions and sync again. |
| `the launch spool has N damaged line(s), so these could not be checked` | The same, for the sessions those lines covered. |
| `nothing new since the last sync` | Use `--all` to re-send everything. |
| `not authorized — the login is expired or was revoked` | Exit 1. Run `outerlayer login`. |
| `not authorized — the API key is unknown, expired, or bound to a different factory` | Exit 1. Mint a fresh key on **Settings → API keys**. |

| Exit code | Meaning |
| - | - |
| 0 | Done, including a run that skipped sessions. |
| 2 | The server rejected at least one session, for example because its repository is not connected. Each reject is listed with its reason. |
| 1 | Missing or refused credentials, or the gateway could not be reached. |

The hooks run `sync --quiet` in the background after each agent turn and at session end, at most once every five minutes, once `login` has saved credentials. Set `"autoSync": false` in the config file to stop that.

See also: [Launch a session](/launch-a-session), [What leaves your machine](/what-leaves-your-machine).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.