---
title: "Sign in to Govern"
---

# Sign in to Govern

Sign the CLI in to your organisation's Neural Inverse Govern deployment, in a browser or with a device code, and understand how tokens, the organisation policy and offline use work.

Neural Inverse Govern (powered by Keystone) is your organisation's account and policy service. The CLI signs in to it the same way the Editor and the Agents app do: the same endpoints, the same device list, the same signed policy format and the same operating-system credential store. Settings and commands keep the name `keystone`.

Signing in is optional unless your organisation requires it. When you are signed in, the CLI can:

- register this device with your organisation;
- fetch your organisation's signed policy: settings it locks, the models you may use, an organisation model gateway, and organisation skills, agent profiles, slash commands and permission rules (see [Known gaps](#known-gaps));
- use your organisation's context: retrieval, Team memory, agent instruction files and the team work registry ([Organisation context](https://neuralinverse.com/docs/cli/organisation-context/));
- keep working offline for a grace period, and lock agent features after that.

## Sign in

```bash
neuralinverse login                  # opens your browser and prints the URL too (SSH, headless)
neuralinverse login --no-browser     # only print the URL
neuralinverse login --device         # a code to enter on any other device
neuralinverse login --browser        # insist on the browser flow where none is detected
neuralinverse login --base-url https://keystone.example.com   # another Govern address, for this sign-in
```

`--device` and `--browser` cannot be used together. In a session, use `/login`.

### In a browser

The CLI uses the OAuth 2.0 authorization code grant with PKCE (S256). It listens once on `http://127.0.0.1:<random port>/callback` (never `localhost`), opens your browser, checks the `state` value, shows a "Signed in, you can close this tab" page, and gives up after 10 minutes. A callback with another `state` is refused without ending your attempt.

Over SSH, open the printed URL in a browser on the machine that runs the CLI, or forward the port, because the browser returns to that machine's loopback address.

### With a device code

In an SSH session, and on Linux with no display server, `neuralinverse login` uses the device authorization grant (RFC 8628) by itself. It prints the sign-in page of your Govern server and a code; you enter the code on any device with a browser, choose the account and approve, and the CLI waits for the approval. The page must be on the Govern server's own domain; anything else is refused before a code is shown. Enter a code only if you started the sign-in yourself.

## Check who you are

| Command | Shows |
|---|---|
| `neuralinverse whoami [--json]` | Account, device and policy state |
| `neuralinverse account show [--json]` (`/account`) | Your account as the Editor and the Agents app show it |
| `neuralinverse policy` (`/policy`) | Organisation, policy version, issue and expiry dates, signing key, state and locked settings |
| `neuralinverse policy refresh` (`/policy refresh`) | Fetch the policy now |
| `neuralinverse doctor` | Includes a Keystone section |

When you are signed in, the welcome header shows `signed in as <email> · <org>`.

`account show` prints your name and email; the **Account** (name and alias) and the 12-digit **Account ID**, shown in groups of four (`1234-5678-9012`); the **Account Region** (`<display name> · <region id>`); the server, the CLI's client id, when you signed in and where the session is kept; the policy state and version; the connection state; and links to the console's Profile, Security and Sessions pages. The account details come from `GET <api_url>/api/v1/account/resolve/<org_id>` and are shown only when the answer names a 12-digit Account ID of exactly your token's organisation; otherwise it shows `Account details unavailable (<code>)`. The organisation UUID and your user id are not printed.

`--json` prints one object with the schema `neuralinverse.account/v2` (`account.id`, `account.name`, `account.alias`, or `account.available: false` with `reason`; `region.id`, `region.name`). It never contains a token. Exit codes: `0` signed in, `1` signed out or the session has ended, `2` usage error.

One sign-in is one account. To use another account, sign out and sign in again.

## Sign out

```bash
neuralinverse logout
```

`logout` (`/logout`) reports the device inactive, revokes the refresh token (RFC 7009) and deletes the local tokens, the cached policy and the organisation content. If Govern cannot be reached, the refresh token is kept aside in the credential store and revoked at the next start.

The CLI follows the Build apps' shared account link (`~/.neural-inverse-shared/neural-inverse-account/link-<quality>.json`, no secrets): signing out of the Editor or the Agents app signs the CLI out at its next start, and the CLI's `login` and `logout` are recorded there so the apps follow it. Set `NEURALINVERSE_SHARED_DATA_DIR` or `NEURALINVERSE_IDE_QUALITY` (default `stable`) when the Editor uses a different folder or build quality.

## Where Govern is

The only address built into the CLI is the address of the service Neural Inverse hosts, the same one the Editor and the Agents app ship with. For a self-hosted deployment, set `keystone.baseUrl` in your user settings or in your organisation's managed settings. A project's settings cannot set it. `login --base-url <url>` uses another address for one sign-in, unless managed settings fix it.

From the base URL the CLI discovers the rest:

1. `GET <baseUrl>/.well-known/keystone-region` names the region, the issuer and the API origin.
2. `GET <issuer>/.well-known/openid-configuration` must name the same issuer, and says whether authorization responses carry `iss` (RFC 9207).

Discovery is refused, with the reason, when an answer is not https, redirects, is not strict JSON of the expected shape, or names a host outside the base URL's domain. A sign-in always discovers afresh. The last result is kept in `<config>/keystone/region.json` only so that a start without a network knows which issuer a stored sign-in belongs to; no credential is sent to an address read from that file. Every request declares `X-Keystone-Client: neuralinverse-cli/<version>`.

When the issuer advertises `iss`, the authorization response must carry exactly one `iss` equal to the discovered issuer. A different `iss` is treated as a mix-up attempt: the sign-in ends and the code is not exchanged.

The settings `keystone.issuer` and `keystone.apiUrl` and the `--issuer` flag are retired. An `issuer` set on its own is used as the base URL, and `doctor` says so. `--issuer` is refused with a message pointing to `--base-url`.

## The CLI's client id

Each Build app signs in with its own public OAuth client: `neuralinverse-editor`, `neuralinverse-agents` and `neuralinverse-cli`. Sessions, device-list entries and audit records therefore say which app they belong to. Govern registers `neuralinverse-cli` for the authorization code, device code and refresh grants, with loopback redirects on any port, no secret, and PKCE S256 required.

The CLI reads its client id only from managed settings, never from user settings, project settings or the environment (`NEURALINVERSE_KEYSTONE_CLIENT_ID` is retired). A Govern server older than the per-app clients answers `invalid_client`; the CLI then stops with a message telling the administrator to upgrade Govern or set `keystone.clientId` in the managed settings. It never tries another client id by itself.

## Where tokens are kept

Tokens go to the operating system's credential store, using the same module and service name (`Neural Inverse Models`) as the Editor:

| OS | Store |
|---|---|
| macOS | Keychain (`security`; the secret is passed on standard input) |
| Linux | Secret Service through `secret-tool` (GNOME Keyring, KWallet) |
| Windows | DPAPI for the current user (PowerShell), in an owner-only folder under `~/.neural-inverse-shared/neural-inverse-models/credentials` |

The CLI's entries are `cli:neural_inverse_auth_token`, `cli:neural_inverse_refresh_token` and `cli:neural_inverse_pending_revocation`. The CLI holds its own session, with its own refresh token and device entry.

When no credential store works, for example on a Linux server without a Secret Service, tokens are kept in memory for the running process only, and the CLI says so. Tokens are never written to `settings.json` or any plain file. `~/.neuralinverse/keystone/session.json` holds only non-secret facts: issuer, client id, email, organisation and sign-in time.

### Session refresh

The access token is refreshed 60 seconds before it expires and after any 401, and the request is retried once. Refresh tokens rotate, and the new pair is stored at once. If Govern refuses the refresh, the CLI does not sign you out; requests report `Keystone session expired, run neuralinverse login`.

## The device list

At sign-in, and hourly while a session runs, the CLI registers the device with `POST <api>/api/v1/account/devices`. It sends `machineId` (a SHA-256 of the operating system's machine id, salted with the issuer; the raw id never leaves the machine), `deviceName` (`Neural Inverse CLI on <OS>`), `version`, `os` (`macOS`, `Windows`, `Linux`), `lastActive` and `isActive`.

Only a `403` whose `error` is `device_blocked`, `device_revoked` or `device_limit_exceeded` signs the CLI out; over the device limit, the session is also revoked. The CLI shows why. Any other failure is logged and does not interrupt the session.

## The organisation policy

The CLI fetches your organisation's Ed25519-signed policy at start, at sign-in and hourly, from `GET <api>/api/v1/policy/bundle` with `If-None-Match`. A bundle is rejected as a whole when its signature does not verify with a pinned key, it has expired, it names another organisation, or its version is older than one already accepted. What a bundle contains and does is described in [Enterprise deployment](https://neuralinverse.com/docs/cli/enterprise/#signed-organisation-policy).

### Offline use

The last verified bundle is cached in `~/.neuralinverse/managed/keystone-policy.json` and verified again every time it is loaded; editing it removes it. When Govern cannot be reached, the cached bundle stays in force until `offlineGraceHours` after its last successful refresh, or until it expires. After that the policy is **locked**: the CLI starts and `/login`, `/policy refresh`, `/doctor` and `/help` work, but model calls and tools are refused with an explanation until a refresh succeeds. The cached restrictions stay applied while locked.

If keys are pinned or sign-in is required, and the policy request fails with no valid cached policy, the policy is locked too (fail closed). An answer of `404` (or `204`) means the organisation has no policy: the cache is cleared and nothing is restricted.

Changes between `online`, `cached` and `locked` are written to the audit log as `keystone_policy` events.

## What is sent to Govern

- To the authorization server: the OAuth parameters (no client secret).
- To the device list: the fields listed above.
- To the policy endpoint: the bearer token and the cached ETag.
- To the organisation model gateway: model requests, with your access token, and `X-Keystone-Context-Request-Id` when the prompt carries organisation context.
- To the Context Platform: the normalised repository address of the working folder, each chat prompt's text for retrieval (unless turned off), and what the `context` commands and the team work tools send. See [Organisation context](https://neuralinverse.com/docs/cli/organisation-context/#what-is-sent).

Your code goes only to the model provider you use.

## Inside the Editor's agent host

When the CLI runs inside a governing host such as the Editor's shared agent host, the host supplies the session. `login` and `logout` then report that sign-in is managed by the host.

## Known gaps

- **The policy route.** The current Govern server does not serve `GET /api/v1/policy/bundle`. When the server answers `404`, the CLI records that the organisation has no policy and runs unrestricted by one, even when policy keys are pinned (the fail-closed rule applies only when the request fails in another way). Organisation settings, `models.allowed`, the organisation model gateway (provider `keystone`), `personalModels` and organisation skills, agents, commands and rules do not reach the CLI through sign-in until the route exists. Use [managed settings or the remote policy](https://neuralinverse.com/docs/cli/enterprise/) to enforce settings today.
- **Governance bundles.** The policy bundle does not yet carry the organisation governance bundles that decide tool calls and supply checks rule packs; those come from managed settings, the shared governance folder or the repository. See [Permissions and modes](https://neuralinverse.com/docs/cli/permissions/#organisation-governance-comes-first).
- **Help text.** `neuralinverse --help` still lists `login [--issuer URL]`. The flag is retired: use `--base-url`.

## Administrator settings

The `keystone` block of the managed settings file:

```jsonc
{
  "keystone": {
    "baseUrl": "https://keystone.example.com",   // self-hosted or air-gapped Govern; the rest is discovered
    "clientId": "ni_...",                          // only for a Govern server older than the per-app clients
    "policyKeys": {                                // Ed25519 keys that may sign the organisation policy, by key id
      "ks-2026": "MCowBQYDK2VwAyEA...",
      "ks-2027": "MCowBQYDK2VwAyEA..."
    },
    "requireSignIn": true                          // refuse model calls and tools until the person signs in
  }
}
```

`policyKeys` and `requireSignIn` are read only from managed settings (the managed file or the remote policy). Managed settings lock the whole `keystone` block. Pin the next key before rotating to it; any pinned key may sign. `keystone.policy: false` turns the organisation policy off; lock it in managed settings if people must not turn it off.

Register the loopback redirect `http://127.0.0.1/callback` (any port) for the client. Govern's own first-party client registration already does this for `neuralinverse-cli`.

See also [Enterprise deployment](https://neuralinverse.com/docs/cli/enterprise/) and [Neural Inverse Govern](https://neuralinverse.com/docs/govern/).
