OpenCode 1.x Configuration and Third-Party Harness Security Boundaries
An English security guide to OpenCode 1.x configuration, provider auth, model ID drift, Bun-installed plugins, and the boundary between official OpenCode and third-party harnesses like Oh My OpenCode / Oh My OpenAgent.
Treat OpenCode as the official boundary, not the whole ecosystem
OpenCode 1.x should be read as an official terminal coding agent with a documented configuration and plugin system. That matters because the surrounding ecosystem is already crowded with wrappers, harnesses, and rebrands. Oh My OpenCode and Oh My OpenAgent are third-party projects built on top of OpenCode, not official OpenCode features. If you confuse them, you will end up trusting packaging, model names, or secret handling rules that the core project never promised.
The practical rule is simple: keep the core platform and the add-on layer separate in your mental model. OpenCode owns the config schema, auth store, model routing, and plugin loader. Third-party harnesses may add shortcuts, presets, or opinionated defaults, but they also add their own supply-chain and rollback risk.
Configuration is layered, merged, and easy to misread
OpenCode supports both opencode.json and opencode.jsonc, and its configuration files are merged rather than replaced. Later files override earlier ones only when keys collide. That is a good operator feature, but it also means copying a sample config into the wrong layer can silently change behavior without obviously breaking anything.
The safe habit is to know which layer you are editing before you touch anything: global config, project config, custom path, or managed settings. If a third-party harness tells you to "just drop this file in place," check whether it is asking you to shadow a lower-priority config or to replace the whole policy surface. Those are not the same operation.
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-5",
"autoupdate": true,
"provider": {
"anthropic": {
"options": {
"apiKey": "{env:ANTHROPIC_API_KEY}"
}
}
}
}
That example is useful only because it shows the boundary, not because it is a universal template. A project-level config can be fine for one repo and a liability for another. The wrong default in a shared repository often becomes the first thing a new contributor copies into a personal file, and that is where leaks and drift begin.
Model IDs drift faster than blog posts
OpenCode 1.x resolves models by provider-qualified IDs, so the full identifier matters. In the docs, the recommended set changes over time, and the same vendor can expose both stable IDs and preview IDs. A blog post that hard-codes claude-3-5-sonnet-20241022 or gemini-3-pro-preview may already be stale by the time a reader copies it.
Use the model picker and the current docs as the source of truth, then pin your own chosen IDs in config once you have tested them. If you rely on a third-party harness, ask whether it rewrites model IDs, aliases them, or keeps its own internal mapping. That is where surprises happen: the UI says one thing, the provider sees another, and your cost or capability assumptions drift quietly.
/models
# then verify the exact provider/model ID you want to pin in config
# example shape: provider_id/model_id
The point is not to freeze innovation. The point is to avoid accidental upgrades through copied examples. If a guide was written for a previous OpenCode release, treat every model string as suspect until you re-check it against the live picker and the current docs.
Secrets belong in auth or environment variables, not in the article
OpenCode documents two safe secret paths that matter here. First, provider credentials added through /connect are stored in ~/.local/share/opencode/auth.json. Second, config values can reference environment variables with {env:VARIABLE_NAME} or file contents with {file:path}. That gives you a way to keep API keys out of the visible config surface.
What you should not do is copy old proxy credentials, shell exports, or vendor tokens into a new harness sample just because the sample "works." If the article you are writing is about security boundaries, then the safe example is one that shows the shape of a secret reference without publishing a real secret or encouraging a reader to paste one into a committed JSON file.
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"openai": {
"options": {
"apiKey": "{file:~/.secrets/openai-key}"
}
}
}
}
That pattern is useful because it separates configuration from secret material. It also makes rotation easier: update the env var or the file, not every repository that might have copied the old value. A third-party harness can still read the same boundary, but it should never become a license to duplicate credentials across tools.
Bun-based plugin execution is code execution, not just configuration
OpenCode plugins extend the agent with hooks, tools, and integrations. The docs say npm plugins are installed automatically using Bun at startup, and the packages plus dependencies are cached in ~/.cache/opencode/node_modules/. That means a plugin is not passive metadata. It is executable supply chain.
This is the sharpest safety line in the whole stack. A third-party plugin can change prompts, inject environment variables, intercept hooks, or call external services. If a harness ships a plugin bundle, you should treat that bundle like application code: inspect the repo, lock the version, and understand what it will do on first launch and on subsequent launches from cache.
npm package -> Bun install at startup -> cached under ~/.cache/opencode/node_modules/
local plugin -> loaded from .opencode/plugins/ or ~/.config/opencode/plugins/
The cache matters because it can make an old plugin keep working even after you thought you had removed the package source. When you are testing a rollback, clear the relevant cache or verify the startup path against a fresh environment. Otherwise you are not testing the new policy; you are testing the residue of the old one.
Official OpenCode plugins are not the same as Oh My OpenCode
OpenCode’s plugin system is just a mechanism. Oh My OpenCode and Oh My OpenAgent are one ecosystem built on top of that mechanism, with their own package ownership, release cadence, and naming choices. The npm registry entries show that oh-my-opencode and lazycodex-ai are maintained by the same third-party author, while oh-my-opencode-slim is a separate fork. That is enough reason to separate them from the official project in both naming and trust policy.
The official docs also warn that some plugins enable use of Claude Pro/Max models through OpenCode and that Anthropic explicitly prohibits that. That warning is the kind of boundary a responsible article should preserve. It is not enough to say "a plugin exists"; you should say whether the plugin is official, third-party, supported, unsupported, or contractually risky.
- Official OpenCode: config, providers, models, and plugin loading as documented by the project.
- Third-party harnesses: opinionated wrappers that may add presets, model mappings, or automation.
- Security rule: never assume a third-party package inherits the same support, update, or licensing boundaries as the core project.
Version pinning and rollback need a real escape hatch
If you accept third-party harnesses or plugins, pin versions deliberately. Pin the harness package, pin the plugin package, and pin the model IDs that your workflow depends on. Otherwise a silent upstream change can alter prompts, tool permissions, or model routing without any visible config diff in your repo.
Rollback should be boring. Keep the last known-good config, keep a note of which plugin versions were installed, and know how to clear caches when a package has to be downgraded. If the harness stores its own presets, do not overwrite them blindly; save a copy before replacing them. A rollback that depends on memory is not a rollback.
1. pin exact package versions
2. keep last-known-good opencode.json or opencode.jsonc
3. clear ~/.cache/opencode/node_modules/ when testing a downgrade
4. re-check /models and /connect after restore
This is also why you should avoid copying old agent credentials or legacy model strings into fresh instructions. Old values are often the hardest thing to notice in a review, and they are exactly the sort of thing that survives a downgrade or a copy-paste into a new project.
A practical boundary checklist
Before you trust any OpenCode-based setup, ask four questions. Who owns the binary and the plugin? Where are secrets stored? Which model IDs are actually selected? What happens if you need to remove the plugin or revert the config tonight, not next week?
- Confirm whether you are using official OpenCode or a third-party wrapper such as Oh My OpenCode / Oh My OpenAgent.
- Keep secrets in env vars, files, or OpenCode auth storage; do not paste them into the article or committed samples.
- Treat provider/model strings as live IDs, not as stable prose.
- Assume Bun-installed plugins can execute immediately and can remain cached after downgrade.
- Document the rollback path before you introduce the first non-official plugin.
That checklist is the difference between a setup that is merely convenient and one that can survive change. OpenCode gives you the primitives. Third-party harnesses can be useful, but they should sit inside a deliberately small trust envelope, not expand it by accident.