---
name: co-env
description: 'See, set, remove and repair settings in the env file a `co` invocation selected — global ~/.co/keys.env by default, or the file named by `co --env-file PATH` — with `co env`. Use when a command says a key is missing, "Next: co env", "account not connected in <file>", or when the user asks which env file is in use or wants an API key saved.'
---

# co env

**Always read the output, not just the exit code.** `co env path` and
`co env get` print a bare value and no tip, so they compose with `$(...)`;
other human-output commands end with one `Next:` line. JSON output contains its next command as a field.

## Which command

| You want to | Run |
| --- | --- |
| See which file is in use and what it holds (all values hidden) | `co env` |
| See full values (never paste into shared logs) | `co env show --reveal` |
| The file's path, for a script | `co env path` |
| One value as a command would see it (whole provider record, otherwise process then file) | `co env get KEY` |
| Save a setting, creating the file if needed | `co env set KEY VALUE` |
| Save one that should not sit in the file in plain text | `co env set KEY VALUE --secret` |
| Change which key opens a stored secret | `co env rotate KEY` |
| Remove a setting | `co env unset KEY` |
| Connect a Google / Microsoft account | `co auth google` / `co auth microsoft` — not `co env set` |
| Use a Feishu/Lark app id or secret copied from the Developer Console | `co env set LARK_APP_SECRET VALUE --from-console` |
| Disconnect one from this file | `co env unset GOOGLE_EMAIL` (removes the whole record) |
| Do any of this on a project file | `co --env-file /abs/path/.env env …` (selector **before** `env`) |

## The 80% commands

```bash
co env                                   # file, settings, sources, next step
co env set OPENAI_API_KEY "$KEY"         # quote values; spaces are fine
co env get MODEL                         # bare value
co --env-file ./project.env env set MODEL co/gemini-3.7-flash
```

## Gotchas that change a result

- **SOURCE column.** `process (overrides file)` means the shell exports the same
  name; the file's value is not what commands see. `set` still saves it and
  prints the `unset KEY` you need in the shell.
- **Provider records are all-or-nothing.** `set` refuses the five `GOOGLE_*`
  and five `MICROSOFT_*` account fields (exit 2, names the auth command).
  `unset` on any one of them removes all of them and says so.
- **`AGENT_CONFIG_PATH` cannot be set here.** It chooses which directory is
  read; `set` refuses it and prints the shell `export` to use.
- **A broken file stops everything else.** Any other command exits 2 with
  `<file>: invalid syntax on line N. Next: co env`. `co env` still runs, repeats
  the line number, never the line, and refuses `set` until the line is fixed
  in an editor.
- **Nothing here selects a file.** `co env` shows the file the invocation
  already chose. To switch, put `--env-file` before the command.
- **`--secret` changes where a value rests, not what `get` returns.** The value
  is encrypted at `.co/keys/secrets/<name>.enc`; `co env get KEY` still prints it
  plainly, because it is the substitution form (`export K=$(co env get K)`) and a
  redacted answer would be a wrong one. `co env show` is the browsing command and
  redacts by default.
- **The encrypted store is consulted last** — after the process environment and
  the file — so storing a name that is already set elsewhere changes nothing
  about what commands see. `set --secret` says so when your shell exports it.
- **A stored secret is unlocked by this agent's own key**, derived from
  `.co/keys/agent.key`. Nothing is written to hold it, so there is nothing extra
  to back up; your recovery phrase reaches it on another machine. Deleting
  `.co/keys/recovery.txt` does not orphan it. Deleting `agent.key` does.
- **`rotate` re-encrypts at the next index and keeps the value.** What changes is
  which key opens it; anything holding the previous ciphertext can no longer read
  it. It is not a way to change the value — use `set` for that.
- **Feishu/Lark app credentials are refused by default** and named
  `co auth feishu` / `co auth lark`, because a hand-typed app secret has no source
  this command can check. `--from-console` is the way in when you already keep the
  application in the Developer Console. It is accepted on those names only;
  anywhere else it is an error, not a no-op.

## Exit codes

| exit | provoked by | next command (printed) |
| --- | --- | --- |
| 0 | done; or the global file does not exist yet | `co env set <KEY> <value>` · `co env get KEY` · `co init` |
| 1 | `get`/`unset` of a setting that is not there · `rotate` of a name with nothing stored · a stored secret that will not open | `co env set KEY <value>` · `co env set KEY <value> --secret` · `co env` |
| 2 | bad name · `AGENT_CONFIG_PATH` · provider record field · Feishu/Lark app credential without `--from-console` · `--from-console` on any other name · missing `--env-file` target · file does not parse | `co env set <KEY> <value>` · shell `export …` · `co auth google|microsoft` · `co auth feishu|lark` · `co env set KEY <value> --from-console` · `co --env-file … env set …` · `co env` |

`co env --json` and `co env show --json` return redacted provenance. JSON values are always hidden. Default human output also hides every value, including custom names and credential-bearing URLs; only `show --reveal` displays them. `get` explicitly returns one effective value and respects whole Google/Microsoft records: it never fills a missing process field from another account in the file.
