Metadata-Version: 2.4
Name: third-option
Version: 0.1.0
License-File: LICENSE-APACHE
License-File: LICENSE-MIT
Summary: Sign Claude Code in to a Third Option gateway from anywhere: the gateway's device-grant login, and the apiKeyHelper and otelHeadersHelper that keep the session fresh.
Keywords: gateway,oidc,device-flow,enterprise,cli
Home-Page: https://third-option.com
License: MIT OR Apache-2.0
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://third-option.com
Project-URL: Source Code, https://github.com/3rd-option/monorepo

# third-option

Signs Claude Code in to a Third Option gateway from anywhere, and keeps the
session fresh.

Claude Code's own gateway login accepts only a gateway on a private network:
every address the host resolves to must be RFC 1918, CGNAT or loopback, and
no setting widens that. A gateway with a public address — a hosted one, or a
company's reached without the VPN — has no interactive way in. This is that
way in.

## Install

```sh
cargo install third-option
npm install -g third-option
uv tool install third-option        # or: pipx install third-option
```

Prebuilt for Linux x64 and arm64 (glibc) and Apple Silicon; `cargo install`
builds anywhere Rust does.

## Use

```sh
third-option enroll https://gateway.example.com
```

That opens the gateway's sign-in page, waits for you to confirm the code it
printed, then writes Claude Code's `settings.json` (`$CLAUDE_CONFIG_DIR`,
else `~/.claude`) so that:

- `env.ANTHROPIC_BASE_URL` is the gateway and model discovery is on, so the
  model picker shows what the gateway serves;
- `apiKeyHelper` is `third-option token`, which prints the session token and
  renews it through the gateway's refresh grant before it runs out;
- whatever the gateway pushes from `/managed/settings` — model, permissions,
  the telemetry exporter — is merged in, the operator's keys winning and
  lists such as `permissions.deny` unioned;
- when telemetry is pushed, `otelHeadersHelper` is `third-option
  otel-headers`, so exports to the gateway carry the session and land.

Nothing else in the file is touched. Run `enroll` again to pick up a changed
policy. `third-option status` shows the session, when it ends, and what Claude
Code points at; `third-option models` lists what this identity may use.

| Verb | Does |
| --- | --- |
| `enroll [url]` | sign in if needed, then point Claude Code at the gateway |
| `login <url>` | sign in only — for a scripted client that sets `ANTHROPIC_BASE_URL` itself |
| `token` | print a fresh session token (`apiKeyHelper`) |
| `otel-headers` | print `{"Authorization": "Bearer …"}` (`otelHeadersHelper`) |
| `status`, `models`, `logout [--all]` | |

`--gateway <url>` picks a gateway; otherwise `$ANTHROPIC_BASE_URL` — which
Claude Code sets from the `env` block when it runs a helper — then the last
login. Sessions live in `$THIRD_OPTION_HOME`, else `~/.config/third-option`,
readable only by you. `--no-browser` prints the link instead of opening it.

## Two modes, one gateway

| | Public — this CLI | Private network — Claude Code's own login |
| --- | --- | --- |
| Reaches the gateway over | any TLS route | private addresses only |
| Enrollment | `third-option enroll` | `/login`, pinned by a machine policy file |
| Credential | `apiKeyHelper` | the pinned gateway session |
| Managed settings | merged into your `settings.json` by `enroll` | pushed live, not editable |
| Telemetry | signed by `otelHeadersHelper` | signed by the session |

They are exclusive on one machine: the policy file that turns on Claude
Code's own gateway login (`/etc/claude-code/managed-settings.json`;
`/Library/Application Support/ClaudeCode/` on macOS) makes it refuse every
helper credential. `enroll` and `status` say so when they see one. The
private-network mode is the stronger one, so a fleet on its own network
should use it; this CLI is for everyone else.

## Scripted use

```sh
export ANTHROPIC_BASE_URL=https://gateway.example.com
export ANTHROPIC_AUTH_TOKEN="$(third-option token)"
```

`token` renews the session when it has under fifteen minutes left, so a
long-running script can call it again rather than cache the value.

## License

MIT OR Apache-2.0.

