Skip to content

Commit 7d4ef6b

Browse files
authored
Merge pull request #23 from GonkaGate/codex/desktop-key-rotation-ux
Clarify OpenCode Desktop key rotation flow
2 parents cb6f04c + a33aada commit 7d4ef6b

7 files changed

Lines changed: 50 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@
145145
- renamed the managed install-state durability timestamp to
146146
`lastDurableSetupAt` while keeping legacy `lastSuccessfulSetupAt` readable as
147147
a backward-compatible migration path
148+
- clarified the safe API key rotation flow in CLI success output and docs,
149+
including the OpenCode Desktop restart needed after refreshing the managed
150+
secret file
148151

149152
### Bug Fixes
150153

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,16 @@ That rerun flow refreshes GonkaGate-managed config, secret storage, and
253253
install-state metadata. It also normalizes only installer-owned GonkaGate
254254
activation in the old target instead of deleting unrelated OpenCode settings.
255255

256+
To replace the GonkaGate API key later, rerun setup with a safe secret input:
257+
258+
```bash
259+
printf '%s' "$GONKAGATE_API_KEY" | npx @gonkagate/opencode-setup --api-key-stdin --scope project --yes
260+
```
261+
262+
If you use OpenCode Desktop, restart the desktop app after rerunning setup so
263+
its sidecar reloads the managed secret file. The Desktop custom-provider form
264+
does not replace the installer-managed `~/.gonkagate/opencode/api-key` file.
265+
256266
For `project` scope:
257267

258268
- user-level config still owns the provider definition and secret binding

docs/how-it-works.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ Current validated picker entries:
9292
`provider.gonkagate.options.apiKey` overrides are always blocked in v1
9393
because current upstream docs do not clearly prove equivalent inline
9494
`{file:...}` substitution parity for this secret-binding contract.
95-
15. Tell the user to run plain `opencode`.
95+
15. Tell the user to run plain `opencode`, and show the safe rerun command for
96+
replacing the managed API key later. OpenCode Desktop users should restart
97+
the desktop app after rerunning setup so its sidecar reloads
98+
`~/.gonkagate/opencode/api-key`.
9699

97100
## Why User-Level Provider Ownership
98101

docs/troubleshooting.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ The safe options are:
6565
- `GONKAGATE_API_KEY`
6666
- `--api-key-stdin`
6767

68+
## Why does OpenCode Desktop still say "Invalid credentials" after I pasted a new key?
69+
70+
If setup was installed by `@gonkagate/opencode-setup`, GonkaGate auth is owned
71+
by the managed secret file at `~/.gonkagate/opencode/api-key`. The OpenCode
72+
config points at that file with
73+
`provider.gonkagate.options.apiKey = {file:~/.gonkagate/opencode/api-key}`.
74+
75+
The OpenCode Desktop custom-provider form can show a new key while the running
76+
sidecar still resolves the installer-managed file. To replace the key, rerun
77+
setup instead of relying on the Desktop form:
78+
79+
```bash
80+
printf '%s' "$GONKAGATE_API_KEY" | npx @gonkagate/opencode-setup --api-key-stdin --scope project --yes
81+
```
82+
83+
Then restart OpenCode Desktop so it reloads the managed secret file.
84+
6885
## Why does non-interactive setup require `--scope` or `--yes`?
6986

7087
Because the installer needs an explicit safe way to choose between `user` and

src/cli/render.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { CommanderError } from "commander";
2+
import { CONTRACT_METADATA } from "../constants/contract.js";
23
import type { InstallFlowResult } from "../install/contracts/install-flow.js";
34
import { redactSecretBearingText } from "../install/redact.js";
45
import type {
@@ -96,6 +97,8 @@ function renderHumanResult(result: InstallFlowResult): string {
9697
`Model: ${result.modelDisplayName} (${result.modelRef})`,
9798
`Scope: ${formatScopeLabel(result.scope)}`,
9899
"Next: opencode",
100+
`Rotate key later: printf '%s' "$GONKAGATE_API_KEY" | ${CONTRACT_METADATA.publicEntrypoint} --api-key-stdin --scope ${result.scope} --yes`,
101+
"OpenCode Desktop: restart after rerunning setup so it reloads the managed key file.",
99102
"",
100103
].join("\n");
101104
}

test/cli.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ test("CLI emits structured JSON failed payloads for resolved-config mismatches",
477477
}
478478
});
479479

480-
test("human-readable success output ends with the minimal next step", async () => {
480+
test("human-readable success output includes next step and key rotation guidance", async () => {
481481
const fixture = await createCliFixture();
482482

483483
try {
@@ -492,7 +492,15 @@ test("human-readable success output ends with the minimal next step", async () =
492492
fixture.stdout.contents,
493493
/GonkaGate is configured for OpenCode\./,
494494
);
495-
assert.match(fixture.stdout.contents, /Next: opencode\n$/);
495+
assert.match(fixture.stdout.contents, /Next: opencode/);
496+
assert.match(
497+
fixture.stdout.contents,
498+
/Rotate key later: printf '%s' "\$GONKAGATE_API_KEY" \| npx @gonkagate\/opencode-setup --api-key-stdin --scope project --yes/,
499+
);
500+
assert.match(
501+
fixture.stdout.contents,
502+
/OpenCode Desktop: restart after rerunning setup so it reloads the managed key file\.\n$/,
503+
);
496504
} finally {
497505
await fixture.harness.cleanup();
498506
}

test/docs-contract.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ test("README captures the shipped runtime truth and current opencode contract",
5252
/WSL/i,
5353
/--api-key-stdin/,
5454
/GONKAGATE_API_KEY/,
55+
/OpenCode Desktop[\s\S]*restart[\s\S]*managed secret file/i,
5556
/~\/\.gonkagate\/opencode\/backups\/project-config/,
5657
]);
5758

@@ -139,6 +140,7 @@ test("implementation docs capture the shipped setup architecture and boundaries"
139140
/WSL/i,
140141
/inherited user-profile ACLs|does not attempt to rewrite Windows ACLs/i,
141142
/project scope writes only activation settings/i,
143+
/OpenCode Desktop[\s\S]*restart[\s\S]*~\/\.gonkagate\/opencode\/api-key/i,
142144
/~\/\.gonkagate\/opencode\/backups\/project-config/,
143145
]);
144146

@@ -164,6 +166,7 @@ test("implementation docs capture the shipped setup architecture and boundaries"
164166
/provider options|model options|headers|compatibility metadata/i,
165167
/provider\.gonkagate\.models[\s\S]*\/models|\/models[\s\S]*provider\.gonkagate\.models/i,
166168
/provider\.gonkagate\.options\.apiKey/i,
169+
/OpenCode Desktop[\s\S]*Invalid credentials[\s\S]*--api-key-stdin/i,
167170
/raw `opencode debug config` output/i,
168171
]);
169172

0 commit comments

Comments
 (0)