@kohai-ai/opencode
Prototype. An OpenCode plugin that adds Kohai as a model provider.
You sign in from OpenCode with a device code, and OpenCode then calls the three Kohai Coder
models on your organisation's credits. The contract is
ADR 0768.
RELEASE.md
has the release process and owner, supported OpenCode versions, and contract checks.
Requirements
- OpenCode 1.18.34 or later. That is the minimum ADR 0768 supports. The plugin is
type-checked against
@opencode-ai/plugin 1.18.34.
- A Kohai member account in a paid organisation that allows coding CLIs.
- A Kohai API environment serving the coding-CLI sign-in routes.
Install
Before the 27 October 2026 production release, testers use 0.1.0-beta.N
from the npm next dist-tag. There is one package, @kohai-ai/opencode, for all
environments. Install on OpenCode 2.x:
opencode plugin add @kohai-ai/opencode@next
For OpenCode 1.18.34–1.x, add the package to opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@kohai-ai/opencode@next"]
}
Set KOHAI_ENV=beta (or dev) in the process running the plugin before signing in.
Dev also needs KOHAI_CF_ACCESS_CLIENT_ID and KOHAI_CF_ACCESS_CLIENT_SECRET for
Cloudflare Access. See Choosing the environment for
OpenCode 2 service setup. Package versions do not select an API environment.
On 27 October, the release owner publishes 0.1.0 to latest; production users
can then install @kohai-ai/opencode without @next and leave KOHAI_ENV unset.
Install for local testing
Build the plugin from the repository root:
pnpm --filter @kohai-ai/opencode build
Before the first publish, use the local build. OpenCode 2.x loads a directory
containing index.js, rather than a JavaScript file. In opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugins": ["file:///absolute/path/to/hirebase/packages/opencode-kohai/dist"]
}
Use dist, not the package root: the package root has no index.js. Check discovery with
opencode plugin list; it should list kohai. Restart the service after rebuilding if it
has not reloaded: opencode service restart.
OpenCode 1.18.34–1.x loads the same build by its file path:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["file:///absolute/path/to/hirebase/packages/opencode-kohai/dist/index.js"]
}
Both versions use the package's default export; no opencode manifest field is needed.
V1 calls server(), while v2 calls setup().
Then sign in and pick a model:
opencode auth login
opencode run -m kohai/kohai-coder "hello"
OpenCode prints the sign-in page and a code such as BCDF-GHJK. Open the page, type the
code yourself and choose the organisation that pays. Nothing in the link fills the code in,
so you always type it. If OpenCode runs on the same machine as your browser, it finishes as
soon as you approve. Over SSH or in a container it finishes on its next poll, within a few
seconds.
Choosing the environment
KOHAI_ENV selects the API: prod (the default) → https://app.kohai.co,
beta → https://beta.kohai.co, or dev → https://dev.kohai.co. Unknown values
raise a KohaiConfigError. These origins match
scripts/ci/environments.json.
An explicit KOHAI_API_URL overrides KOHAI_ENV, including for a local API.
For beta testing:
export KOHAI_ENV=beta
opencode auth login
opencode
Dev also sits behind Cloudflare Access, which redirects requests without a service
token to its login page. Load KOHAI_CF_ACCESS_CLIENT_ID and
KOHAI_CF_ACCESS_CLIENT_SECRET from your own secret store, never a file in the
repository, then run:
export KOHAI_ENV=dev
opencode auth login
opencode
In OpenCode 2, plugins run in the background service, which may have started before
these variables were exported. Setting variables only on auth login does not change an
already running service. For an environment inherited directly from your secret-loading
shell, stop the service and run it from that shell:
opencode service stop
opencode serve --service
Leave that terminal running and use opencode auth login from a second terminal. The
service receives KOHAI_ENV, any KOHAI_API_URL override, and both
KOHAI_CF_ACCESS_* variables. For automatic service
startup, OpenCode also supports opencode service set env NAME VALUE followed by
opencode service restart; configure the same variables in the service environment.
For a one-command private server, opencode auth login --standalone inherits the invoking
shell's variables, but a later normal OpenCode session still uses its service environment.
OpenCode 1 runs the plugin in the CLI process and inherits its shell environment directly.
The plugin sends the two Access headers only to the Kohai API origin, on sign-in and on model calls. Production needs neither.
With KOHAI_API_URL, plain http is accepted only for an API on this machine, such as
http://127.0.0.1:8787.
What it adds to OpenCode
- A provider,
kohai. It is OpenAI-compatible, at <api>/api/coding-inference/v1. If
you define a kohai provider in your own config, the plugin leaves it alone.
- Three models:
kohai-coder (Kohai Coder), kohai-coder-fast (Kohai Coder Fast) and
kohai-coder-pro (Kohai Coder Pro).
- One sign-in method, "Sign in with Kohai (beta)". It asks for a device code. It also listens
on
127.0.0.1 for the browser's hand-back, and whichever path finishes first wins.
Where the sign-in is stored
OpenCode 1 keeps the initial access and refresh tokens in its own
auth store, $XDG_DATA_HOME/opencode/auth.json, with mode 0600. By default that is
~/.local/share/opencode/auth.json, under the key kohai.
OpenCode 2 keeps the pair in its native credential store; the plugin reads the active Kohai
connection through the integration API. The pair is bound to the API origin that issued it,
recorded as enterpriseUrl in v1 and metadata.enterpriseUrl in v2. The plugin only sends it there: if
the selected API environment changes (including leaving both KOHAI_ENV and
KOHAI_API_URL unset, which means production, after signing in to dev), requests fail with a message naming both origins, and nothing is sent. Sign in
again for the new environment.
The access token lasts an hour, and the plugin refreshes it a minute before it expires. Each
refresh rotates the pair. The plugin keeps rotations in memory, bound to the refresh token
of the stored sign-in. It commits a rotation only while that sign-in is still current, and
never writes over OpenCode's entry. A logout or replacement sign-in discards the old rotation;
an in-flight rotation that loses this check is revoked. Failed revocations retry in memory for
up to 30 days while OpenCode stays open, with delays capped at one minute; at most 128 are held.
An unreadable store withholds the access
token until the sign-in can be confirmed. A session lasts 30 days from sign-in. When it ends or is revoked, requests fail with "Run
opencode auth login and choose Kohai to sign in again." opencode auth logout forgets the
tokens on this machine, but it does not revoke the session on the server: OpenCode's plugin API
has no logout hook. To end it on the server, revoke it in Kohai under Settings → Security →
Coding CLIs. The auth client's revokeSession (POST /api/coding-cli/v1/revoke) is the call
a sign-out command makes; the kohai login helper (BAS-3611) wires it to one.
Known limits of the prototype
-
Sign in again after restarting OpenCode if it refreshed. The plugin cannot conditionally
replace OpenCode's stored credential, so persisting a rotation could overwrite a concurrent
logout or sign-in.
Rotations therefore stay in this plugin instance's memory. The original token in
OpenCode’s credential store is spent after the first refresh: restarting OpenCode loses the rotated pair,
and trying to refresh with the original token ends that server session through reuse
detection. Run opencode auth login before making requests in the restarted process.
A durable fix needs a conditional write in OpenCode or a separately owned credential store.
Logout cannot be overwritten by this plugin, but a request already authorised before
logout can finish, and OpenCode provides no server-side logout hook. Revoke in Kohai
(Settings → Security → Coding CLIs) to end the server session.
-
One refresh at a time. The server treats two refreshes of the same token as reuse and
revokes the session, so the plugin never runs two at once. Two OpenCode processes that
share one auth.json can still race each other. Run one at a time.
-
A refresh answer that never arrives costs the session. If the server rotated the pair
but the response was lost, the next attempt presents a spent token, and you have to sign in
again.
-
No context or output limits are declared for the three models yet.
-
The browser may show "connection refused" after approval if the device-code poll won the
race and the listener had already closed. The sign-in has still succeeded.
Development
pnpm --filter @kohai-ai/opencode typecheck
pnpm --filter @kohai-ai/opencode test
The tests make no network calls beyond 127.0.0.1. The Kohai API, the clock and the
loopback listener are all injected.