Skip to content

Profiles & isolation ​

How isolation works ​

opencode reads its directories from environment variables. ocp sets three, scoped to the launched process:

WhatVariableResolves toIsolated
configOPENCODE_CONFIG_DIR<profile>/configyes
auth + sessionsXDG_DATA_HOME<profile>/datayes
prompt history + recent modelsXDG_STATE_HOME<profile>/data/stateyes
omo sectionOMO_PROFILE<name>yes, by section
model + package cache(untouched)sharedshared on purpose

Your agents, skills, plugins, auth, and session history are children of the first two directories, so they are isolated automatically.

State vs cache ​

opencode keeps two more directories outside config and data, and ocp treats them differently.

XDG_STATE_HOME is isolated. It holds your typed prompt history, frecency ranking, recent-model list, plugin metadata, and lock files. Shared, those bleed across accounts — one profile's prompts are recallable in another, and the model picker mixes providers from both. ocp nests it at <profile>/data/state, so ocp remove --purge-data still cleans it up. opencode creates the directory on first launch; there is nothing to migrate.

XDG_CACHE_HOME is left shared, on purpose. It holds public model catalogs and version-keyed package downloads — hundreds of megabytes, none of it account-bearing. Isolating it would re-download every plugin and language server per profile for no privacy gain.

oh-my-openagent (omo) ​

omo needs its own variable because it no longer stores config inside the opencode config directory.

Since omo 5.x it reads a single, home-anchored ~/.omo/omo.jsonc (plus any project-level .omo/omo.jsonc), and there is no environment variable to relocate that file. OPENCODE_CONFIG_DIR therefore no longer isolates it. Instead, omo selects a section from the file's top-level profiles object, and ocp exports OMO_PROFILE=<profile> so each profile gets its own.

Put shared settings at the top level and per-profile overrides under profiles.<name>:

jsonc
// ~/.omo/omo.jsonc
{
  "[opencode]": {
    "claude_code": { "skills": false }        // applies to every profile
  },
  "profiles": {
    "personal": {
      "[opencode]": {
        "agents": { "oracle": { "model": "anthropic/claude-opus-4-7" } }
      }
    },
    "work": {
      "[opencode]": {
        "agents": { "oracle": { "model": "llmgateway/claude-opus-4-7" } }
      }
    }
  }
}

The profile layer merges over the base, so a section only needs to declare what differs.

WARNING

If profiles.<name> is missing, omo does not error — it silently falls back to the base config, which can hand one profile another account's models. ocp create warns about this, and ocp resolve shows which section is in play.

To point a profile at a differently-named section, set OMO_PROFILE in the profile's env file.

An OMO_PROFILE inherited from the surrounding shell is deliberately ignored. Otherwise launching opencode from inside another profile's session would carry that session's section past an explicit -p, which is exactly the leak this is meant to prevent.

Layout ​

~/.config/ocp/
├── active                       # name of the default profile
└── profiles/<name>/
    ├── profile.env              # manifest: DESCRIPTION, WRAPPER, DEFAULT_ARGS
    ├── env                      # optional: sourced before launch
    ├── config/                  # OPENCODE_CONFIG_DIR (opencode.json, agents, skills, …)
    └── data/
        ├── opencode/            # XDG_DATA_HOME (auth.json, sessions, …)
        └── state/               # XDG_STATE_HOME (prompt history, recent models, …)

omo config lives outside this tree, in ~/.omo/omo.jsonc under profiles.<name>.

Override the root with the OCP_HOME environment variable.

Working with profiles ​

sh
ocp create work --description "Work account"   # scaffold a profile
ocp list                                        # show all profiles
ocp use work                                    # set the global default
ocp path work                                   # print a profile's directories
ocp remove work --purge-data                    # delete a profile (and its data)

ocp list marks the global default and the profile active in the current directory:

   client  Client ACME
-> work
   personal

default -> work

The -> arrow points to the profile that would launch right now; the default -> line shows the global default.

Seeding a profile ​

Copy an existing opencode config into a new profile with --from, and reuse your current login with --seed-auth:

sh
ocp create work --from ~/.config/opencode --seed-auth

Without --seed-auth, a new profile starts logged out — authenticate it once:

sh
ocp launch -p work -- auth login

Isolated opencode profiles.