Connect OpenCode
Route OpenCode through the Dynapps gateway: one installer flag writes the provider block and the API key, so every prompt is governed and audited instead of being refused with 403.
On this page
OpenCode supports many model wires. The bridge registers
it against the gateway's OpenAI-compatible endpoint, as a provider called
systemprompt pointed at a local loopback proxy — so OpenCode never holds a
model vendor's key, and every request is governed, priced, and audited like any
other.
Use your administrator's gateway HTTPS URL and trusted manifest public key. The Dynapps endpoint is https://dynapps.systemprompt.io. Install OpenCode using your organization's approved method before enrolment.
1. Get a connect code
Sign in and open your profile page, then click Generate a connect code to mint a one-shot code. It is single-use and expires ten minutes after it is issued, so get the installer command ready first and take the code last.
An administrator can mint one headlessly:
systemprompt admin bridge issue-code --user-id you@example.com
2. Install the bridge and enrol OpenCode
Linux or WSL: use the shell installer below. For macOS, follow Install on macOS, then run the client-specific enrolment command. The shell installer is not a native Windows command.
GATEWAY_URL="https://gateway.example.com"
curl -fSLo dynapps-install.sh "$GATEWAY_URL/files/downloads/install.sh"
less dynapps-install.sh
sh dynapps-install.sh --download-base "$GATEWAY_URL/files/downloads" \
--gateway "$GATEWAY_URL" --pubkey "ADMINISTRATOR_PROVIDED_BASE64_PUBLIC_KEY" \
--code "YOUR_CONNECT_CODE" --host opencode
The installer downloads the bridge, verifies its checksum, redeems the code for
a durable token, starts the loopback proxy, enrols the clients you named, and
finishes by running dynapps-bridge doctor.
--host is repeatable and accepts a comma-separated list, so a machine running
both clients takes --host claude-code,opencode. Left off entirely, the
installer enrols Claude Code and adds OpenCode only when the opencode binary
is already on your PATH — so if you install OpenCode later, re-run the
enrolment. The same list can come from $DYNAPPS_HOSTS for unattended
provisioning. The installer accepts --host only; --hosts all is a flag of
the bridge binary, below.
Already have the bridge? Enrol OpenCode on its own, without reinstalling:
dynapps-bridge install --host opencode
dynapps-bridge install also takes --hosts all to enrol every client this
build supports. Name either --hosts all or one or more --host <id>, never
both on the same line.
Native Windows PowerShell: complete the download, gateway trust, sign-in, and proxy-start steps in Install on Windows, then run:
& $Bridge sync
& $Bridge install --host opencode
& $Bridge doctor
opencode
Use the verified $Bridge executable path from that guide. Keep the app or proxy running. OpenCode enrolment is separate from Claude Code enrolment.
3. Verify
dynapps-bridge doctor reports the proxy, the credential, and the synced
manifest in one pass, and is the fastest way to tell a local problem from a
gateway one.
The loopback proxy authenticates every request with a bearer token local to this machine. Ask it for the model list:
curl -s -H "Authorization: Bearer $(cat ~/.config/dynapps/bridge-loopback.key)" \
http://127.0.0.1:48217/v1/models
A JSON list of model ids means the proxy is up, your gateway credential is valid, and the wire OpenCode uses is answering.
The curl example above uses Linux default paths and port; use the actual endpoint reported by dynapps-bridge diagnostics if they differ. Do not paste credentials or authentication-file contents into support logs.
On Linux, confirm OpenCode itself has the provider and the key:
grep -A12 '"systemprompt"' /etc/opencode/opencode.json ~/.config/opencode/opencode.json 2>/dev/null
grep -q systemprompt ~/.local/share/opencode/auth.json && echo "api key present"
On Windows, use & $Bridge doctor. After selecting a systemprompt model in OpenCode, send a short prompt and confirm its account, model, and timestamp in gateway History. Verify a read-only MCP tool separately.
4. Use it
Run opencode, open the model picker, and choose a model under the
systemprompt provider. Enrolment writes a default, so the first prompt works
without picking anything. The managed configuration also sets
enabled_providers to the systemprompt provider alone, so the picker offers
only models this gateway serves — never OpenCode's built-in free tier or a
provider connected by hand.
The desktop app
The OpenCode desktop app runs the same OpenCode server
inside the app, so it reads the same admin-managed opencode.json, the same
auth.json, and the same MCP connectors, skills and reporting plugin the bridge
writes for the CLI. Nothing extra is enrolled: install the app, keep the bridge
running, and pick a systemprompt model. It needs no opencode binary on your
PATH; the bridge recognises the app itself (OpenCode.app on macOS,
%LOCALAPPDATA%\Programs\@opencodedesktop\OpenCode.exe on Windows) and
offers an Open button once it is present.
Skills are not listed anywhere in the app. OpenCode exposes them to the model
through its skill tool, so ask the assistant what skills it has, or name one in
your prompt, and it loads the matching SKILL.md. If the app shows a model from
another provider, the managed file is missing or stale: run
dynapps-bridge install --host opencode (as administrator on Windows) and
restart the app.
What the bridge writes
| Piece | Where | Tier |
|---|---|---|
Provider block (provider.systemprompt), the default model and the enabled_providers allowlist |
/etc/opencode/opencode.json |
admin-owned |
Provider block, when /etc is not writable |
~/.config/opencode/opencode.json |
your own |
API key for the systemprompt provider |
~/.local/share/opencode/auth.json (0600) |
your own |
MCP connectors (mcp.<name>) |
~/.config/opencode/opencode.json |
your own |
| Managed skills | ~/.config/opencode/skills/ |
your own |
Skill-use reporting plugin (systemprompt-hooks.js) |
~/.config/opencode/plugin/ |
your own |
The reporting plugin is written for the plugin that owns governance hooks
(hooks.governance: true) and removed when none does. It listens to
OpenCode's chat.message and tool.execute.after events and posts a
Claude-Code-shaped hook event to the loopback proxy's track route whenever the
built-in skill tool runs, naming the skill by its plugin:skill identity.
The bearer it carries is a per-plugin hook token derived by the bridge, never
the loopback secret or your API key; the proxy mints the gateway credential and
stamps the enrolled device, exactly as it does for Claude Code hooks. Those
events are what the admin console's Skill conversations and Resource
effectiveness pages count for OpenCode sessions.
Session linking
The same plugin also registers a chat.headers hook. OpenCode identifies each
conversation by a ses_… id; the plugin derives a stable session id from it
and sends that id as an x-opencode-session header on every chat request
OpenCode makes to the loopback proxy. Its hook events carry the same id, plus
x-systemprompt-host: opencode. The proxy links each request to that session
before forwarding it, so a whole OpenCode conversation
— every request in it and the spend it incurs — is attributed to the skills it
invoked, the same way a Claude Code session is. Nothing in your OpenCode config
needs changing for this; the plugin is written by dynapps-bridge install --host opencode and refreshed by sync.
The device the proxy stamps on those requests is enrolled automatically when you sign in to the bridge, and appears on the administrator's Devices page.
The provider block names @ai-sdk/openai-compatible as its npm package,
points options.baseURL at the loopback proxy, and carries an
x-inference-protocol header that tells the gateway which model families to
advertise to this client. OpenCode fetches that npm package on first use, so a
fully air-gapped machine needs it pre-seeded.
The bridge writes opencode.json. If you keep an opencode.jsonc instead, the
bridge reads it when probing but never writes to it, so the block will not
appear there.
On macOS the admin tier is /Library/Application Support/opencode/ and on
Windows %ProgramData%\opencode\; the API key sits under the user's own
.local/share/opencode/ and %USERPROFILE%\.local\share\opencode\. macOS also
honours an MDM tier above the managed file
(/Library/Managed Preferences/ai.opencode.managed.plist), which the bridge
reads but never writes.
Every foreign key in these files survives. The bridge owns the
provider.systemprompt object, the top-level model when it names our
provider, and the MCP entries whose URL points at the loopback proxy. It
rewrites those and nothing else, so a config you wrote by hand is preserved
across syncs.
The admin tier is not enforcement
The provider block is written to the admin tier because OpenCode layers the managed directory above user and project config, so it is the tier a personal config will not casually override.
It is not governance. On Linux the file is mode 0644 with no lockdown and
no allowlist behind it, and where the bridge cannot write /etc/opencode it
falls back to your own config and says so. Nothing stops a determined user from
editing either tier or adding another provider. Governance is enforced at the
gateway — scope checks, secret scanning, blocklists, rate limits and the audit
trail all run there, on every request, regardless of what any local file says.
Note that sudo dynapps-bridge install --host opencode does not work today:
the bridge resolves your credentials from $HOME, which under sudo is
/root, where no enrolment exists. Run the enrolment as your normal user.
Choosing a model
Models are namespaced by provider, so they are written systemprompt/<model> —
for example systemprompt/claude-sonnet-5. Enrolment writes the list the
gateway currently offers this client and sets a default, so the picker shows
what your role is allowed. Ask for a model that is not on that list and the
gateway refuses it rather than silently substituting another.
The list is scoped to the model families the client advertises, so it is
narrower than the instance's full catalogue. To see everything the gateway can
serve you, call /v1/models without the protocol header:
curl -s -H "Authorization: Bearer <your-pat>" \
https://gateway.example.com/v1/models
If a model you expect is missing from OpenCode's picker but present there, it is a discovery filter rather than a permission problem — ask an administrator.
VS Code GitHub Copilot
Documented, not validated by Dynapps (REQ-054 / FR-17.3). The steps below follow Microsoft's and third-party gateway guides for Copilot's "bring your own model" Custom Endpoint provider as of October 2026. Dynapps has not run them end to end; check the setting names against your VS Code version.
Copilot Chat in VS Code 1.122 or later can send Chat requests to an OpenAI-compatible endpoint through a Custom Endpoint model group:
-
On Copilot Business or Enterprise, a GitHub organization administrator must first enable Bring Your Own Language Model Key in VS Code in the Copilot policy settings.
-
In VS Code run Chat: Manage Language Models, choose Add Models → Custom Endpoint, name the group (for example
Dynapps), paste the API key, and pick the API type Chat Completions. -
VS Code opens
chatLanguageModels.jsonwith the group created. Leave the generatedapiKeysecret reference alone and add the models:[ { "name": "Dynapps", "vendor": "customendpoint", "apiKey": "${input:chat.lm.secret...}", "apiType": "chat-completions", "models": [ { "id": "claude-sonnet-5", "name": "Claude Sonnet 5 (Dynapps)", "url": "http://127.0.0.1:48217/v1/chat/completions", "toolCalling": true } ] } ]Each model's
urlis the full path, not a base URL. Model ids are the gateway's bare ids (GET /v1/modelslists them), with nosystemprompt/prefix.
Which URL and key. The gateway's OpenAI-compatible base is https://<host>/v1, so
the direct URL is https://<host>/v1/chat/completions with a personal access token as
the API key; there is no /openai/v1 prefix (see
Gateway API). But a direct
call must also carry an x-session-id header minted at
POST /api/public/gateway/sessions, and the Custom Endpoint form documents no field
for extra headers, so a direct call is expected to fail with 400 missing required x-session-id header. Through the bridge's loopback proxy (the URL in the example, with
the loopback key from bridge-loopback.key as the API key) the proxy supplies the
session, as it does for OpenCode. Either way, only Copilot Chat and its tool calls
are routed; inline completions and next-edit suggestions still go to GitHub.
Troubleshooting
| What you see | What it means | What to do |
|---|---|---|
403 forbidden: no loopback credential presented |
OpenCode reached the proxy with no Authorization header. Almost always the provider block or auth.json was never written: sync installs MCP connectors and no credential, so the connectors appear and inference does not. |
dynapps-bridge install --host opencode, then restart OpenCode. |
403 forbidden: bad loopback secret |
The credential is real but belongs to a different bridge install answering on this port — most often Windows and WSL2 sharing 127.0.0.1:48217. The body names the config directory of the install that answered. |
Decide which install owns the port, stop the other, then re-run the enrolment from the one you keep. |
| MCP servers are listed but every prompt fails | The same missing-credential case as the first row; the two are written by different steps. | As above. |
connection refused on 127.0.0.1:48217 |
The proxy is not running. | Keep the bridge app running, or run dynapps-bridge proxy in a separate terminal (& $Bridge proxy in PowerShell). |
| The model list is empty, or a model is rejected | Your role does not reach that model, or the manifest has not synced yet. | dynapps-bridge sync, then check the list with the curl above. |
--host opencode: this build does not offer the 'opencode' host |
The bridge binary you installed was built without the OpenCode integration. | Re-download the bridge from this instance rather than reusing an older binary. |
| Enrolment says the instance does not enable this host | OpenCode is not in the host list your instance publishes for you. | Ask an administrator to enable the opencode host, then dynapps-bridge sync and re-run the enrolment. |
The provider block landed in your own config, not /etc |
/etc/opencode was not writable and Linux has no elevation path to offer. |
It works as is. To use the admin tier, have an administrator create /etc/opencode writable by you, then re-run the enrolment. |
| Upstream errors naming a missing API key | The gateway has no credential configured for the provider behind that model. | Nothing to fix locally — report the model id to an administrator. |
| Skills you run in OpenCode do not appear on the Skill conversations page | The reporting plugin is missing (~/.config/opencode/plugin/systemprompt-hooks.js), the proxy is not running, or no plugin on your instance owns governance hooks. |
dynapps-bridge sync, confirm the file exists, keep the proxy running, then re-run the skill. |
Removing it
Un-enrol OpenCode and leave everything else in place:
dynapps-bridge uninstall --host opencode
In Windows PowerShell, use & $Bridge uninstall --host opencode.
This strips the bridge-owned provider block from both tiers, removes the
systemprompt entry from auth.json and deletes the reporting plugin,
leaving every other provider, plugin and key you added untouched.
To remove the bridge itself, dynapps-bridge uninstall with no --host, or
dynapps-bridge uninstall --purge to take the cached credentials and synced
manifest with it.
Use a bridge build that supports OpenCode enrolment. A successful plugin sync alone does not configure inference.