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

# Platform mode (PlungeAI connectivity)

> oceancode ships in two builds. The default build is the standalone coding agent and contains no platform code at all. The platform build is produced with


`oceancode` ships in two builds. The **default build** is the standalone coding agent and contains
no platform code at all. The **platform build** is produced with

```bash
cargo build --release -p ocean-cli --features platform
```

and adds the three PlungeAI couplings: the OceanTunnel daemon (`crates/ocean-app/src/oceantunnel.rs`),
the enrich resolver (`crates/ocean-agent/src/systemcontext/remote.rs`) and the per-step remote
capability packs (`crates/ocean-agent/src/systemcontext/capability.rs`). Everything the `ocean`
config section describes below is inert in the default build.

The `platform` cargo feature is declared on three crates — `ocean-agent`, `ocean-app` and
`ocean-cli` — and is **off by default**. The `OceanInfo` schema itself (every `ocean.*` key)
stays in the leaf crate `ocean-config` in both builds, so an `oceancode.json` with an `ocean`
section parses either way; only the code that acts on it is gated.

## Which build do I have?

`node scripts/gates/check-platform-boundary.mjs --no-build` scans the two release binaries for three
byte-literal markers after a release build and reports `markers_local=0 markers_platform=3` when the
boundary holds; a routine `./check.sh` proves the same boundary on debug builds (`--debug`, which
builds `target/debug/oceancode` under both feature sets and scans between the builds — no release
build needed). At runtime, `oceancode inspect` reports a `platform` row: `compiled in` on a platform
build, `not compiled in` on the default build.

## The "platform: not compiled in" notice

A **default** build that finds `ocean.mode = "platform"` or a non-empty `ocean.endpoint` in
`oceancode.json` prints once at boot and in `oceancode inspect`:

```
platform: not compiled in (ocean.mode or ocean.endpoint is set; rebuild with --features platform)
```

It is a single warning, not an error: the agent still runs, exactly as it does for any user with no
`ocean` section. Rebuild with `--features platform` to make the section take effect. A config without
an `ocean` section prints nothing.

## Config keys

The `ocean.*` keys are serde-only and present in both builds; they take effect only in the platform
build (`crates/ocean-config/src/ocean.rs:15-88`).

| Key | Meaning | Default |
|---|---|---|
| `mode` | `local` or `platform`. The only switch that selects platform behaviour. | `local` |
| `endpoint` | Enrich URL (`GET ?agent=<name>&directory=<dir>` → `{baseline, version}`). | empty |
| `headers` | Headers sent on every platform request (auth lives here). | none |
| `ttl_seconds` | How often a session re-checks the enrich endpoint (0 → 300). | 300 |
| `agents` | Platform-resolved agent names; empty defaults to `ocean-build`, `ocean-plan`. | `ocean-build`, `ocean-plan` |
| `mcp_url` | Auto-registers the platform MCP gateway as a remote MCP server named `ocean`. | empty |
| `capability_endpoint` | Per-step capability pack endpoint. | empty |
| `gateway_base_url` | The inference gateway a provider `baseURL` should point at: the bare host or its `/v1` root, both work. | `https://api.plungeai.com/v1` in platform mode |
| `tunnel_url` | OceanTunnel dial-out URL; with a token, runs the tunnel daemon. | empty |
| `tunnel_token` | Inline OceanTunnel session token. | empty |
| `tunnel_token_file` | File holding the tunnel token (`~/` expands against `$HOME`). | empty |
| `tunnel_agent` | Local agent name registered with the tunnel. | `oceancode` |

**The switch is config-only.** No environment variable selects platform mode; `ocean.mode` in
`oceancode.json` is the only switch — there is no `OCEANCODE_MODE` / `OC_MODE`. Two platform-related
environment variables the binary reads never turn platform mode on by themselves:

- `OC_TUNNEL_TOKEN` — the tunnel session token, read at the **highest priority** (above the deprecated
  `OCEAN_TUNNEL_TOKEN`, then `tunnel_token_file`, then the inline `tunnel_token`). `OCEAN_TUNNEL_TOKEN`
  is still read as a legacy fallback and logs one deprecation warning per process.
- `OC_SSO_TOKEN` — a per-request gateway SSO session token. When set, oceancode attaches it as the `X-Ocean-SSO-Token` header (no `Bearer` prefix) on requests to the configured **https** gateway host only — never a direct-provider host, never over plaintext — and never on `Authorization` (the wallet bearer, your `ozk_` key, stays intact). Read at runtime, never persisted. Server-side reader is a pending platform handoff: until the inference gateway reads `X-Ocean-SSO-Token`, the header is ignored (harmless).

Every variable also has a row in [02 · Configuration](/oceancode/configuration#environment-variables).

## Per-step capabilities

In platform mode the local `<available_skills>` catalog is replaced by a **capability pack** sized to
the plan row the model is working. At each step boundary the resolver asks the capability endpoint for
a pack (eager skill bodies within a 24 KiB budget, a deferred index, and a version); `304` when the
pack is unchanged, so the retained prefix — and the provider's prompt cache — never changes because a
skill changed. Deferred bodies cost nothing until pulled:

- **`load_capability` tool** — a platform-build tool that pulls a deferred capability body by id
  (`load_capability {id}`) into the transcript as a normal tool result, never a file on disk. It is
  registered only in the platform build and refuses any id the platform never offered.
- **`capability` permission** — the permission family that governs `load_capability`. It **asks** by
  default in the read-only agent families (`ask`, `explore`, `security-review`) and is allowed for the
  `build`, `plan` and `general` families.
- **Registry skills** — platform mode can also discover skills straight from the registry with no local
  files; they surface with the provenance `platform:<id>` (tier `platform`, bound tool id
  `skill_platform_<id>`) and load with no base-directory line.

## Model routing and the wallet

**Signing in.** On a platform build, `oceancode login --provider ocean` (alias `plungeai`) signs you in
through the browser and stores your account key. It opens the dashboard's
`https://dashboard.plungeai.com/api/cli/authorize` page with a loopback port, you sign in with any method
the dashboard offers, the dashboard hands a one-time code back to `http://127.0.0.1:<port>/callback`, and
oceancode trades that code for your `ozk_` account key and stores it in `auth.json` under `ocean` — the
entry `oceancode key ocean` writes and platform mode uses first. The key never appears in a browser URL.
`--no-browser` prints the link without opening it; the sign-in gives up after three minutes;
`oceancode logout ocean` removes the key.

With an `ozk_` platform key stored — `oceancode key ocean ozk_…` (it lands in `auth.json`), or a credential
under the `ocean` integration — platform mode synthesizes an `ocean` provider whose `baseURL` is the
gateway's `/v1` root (`https://api.plungeai.com/v1` unless `gateway_base_url` says otherwise), so every model
call routes through the inference gateway. A legacy `sk-ocean-` key is found the same way; when both are
stored the `ozk_` key wins. A model of `auto` is resolved once per session from the gateway's
`/v1/discovery/recommend` (falling back to `anthropic/claude-sonnet-4-5` if the recommend call fails).
The `run --format json` result envelope then carries two extra objects for a platform-routed run:
`platform: { fallback_steps }` (how many steps failed open to the local route) and
`wallet: { balance, spent }` (present only when the gateway meters). A local run omits both.

## The ledger in platform mode

Two engine-owned sections in `SESSION.md` are fed by platform-mode durable events:

- **`## Plan changes by user`** — regenerated from `todo.updated` events, one summary line per event
  that carried user-authored plan rows (the model's own edits contribute nothing).
- **`## Capabilities used`** — regenerated from `pack.manifest` events, one
  `| step | ids | version | hash |` row per step; a fallback step lists its row with an empty id cell.

Both are bounded to the newest 20 rows and regenerated on every write, so they never crowd out
model-owned sections. Plan rows also carry cross-session history: when a row that completed or was
cancelled in an earlier session reappears, its projection shows how often it was seen and how often it
failed.
