Skip to content

fix(drivers): keep OAuth credentials across restarts - #1108

Merged
frahlg merged 4 commits into
masterfrom
fix/driver-secrets-before-start
Sep 7, 2026
Merged

fix(drivers): keep OAuth credentials across restarts#1108
frahlg merged 4 commits into
masterfrom
fix/driver-secrets-before-start

Conversation

@frahlg

@frahlg frahlg commented Sep 7, 2026

Copy link
Copy Markdown
Member

After myUplink rotates token A to B, Core can restart the driver with A even when B is saved in SQLite. The initial driver loop ran before the secret callbacks were installed. myUplink also refreshes during driver_init, so that first rotation could not be saved.

Create the registry with both secret callbacks before any driver starts. Signed read-only OAuth drivers can save only keys declared in their verified metadata, in their own driver namespace. Keys have a fixed format and length; values are capped at 1 MiB. Control-v2 drivers receive no secret-persistence capability.

The signed myUplink manifest declares http.post and one auth path, but policy validation previously rejected that combination. Admit it only with read-only, http.get and the declared auth path. General writes remain denied, and auth POSTs cannot follow redirects. Official beta installs now resolve the same signed runtime policy through their existing beta trust source, even when Settings lists only the stable repository.

Validation:

  • The published ARM64 v3.0.2 image reproduces the startup failure in two isolated Core processes: config A remains, SQLite stores B, and the second process incorrectly selects A. Both processes exit cleanly and have no external network or published ports.
  • Real registry/Lua/SQLite regressions cover init and first-poll rotation, database reopening, and B overriding unchanged config A, for local and managed drivers.
  • Race tests cover signed beta installation and cache reload, signature failure, secret-key and size limits, missing permissions, actual Lua policy validation, and HTTP boundaries. Redirects 307/308 make one auth request and zero device-path requests.
  • make verify passes on the final source, including the fixed daytime native fixture from test(forecast): keep PV restore fixtures in daylight #1107.

No Lua artifact, driver pin or manifest schema changes. The overlapping open PRs #734, #735, #826, #1003 and #1052 touch other sections of main.go; this change leaves their work in place. Driver-host ownership remains with the repository owner under the delegated beta release process.


Note

High Risk
Changes driver credential persistence, OAuth HTTP sandboxing, and signed runtime policy for managed/beta drivers—security-sensitive paths where a bug could leak or overwrite secrets or widen network write access.

Overview
Fixes myUplink-style OAuth where Core could restart with a stale refresh_token from config even after a rotated token was saved in SQLite, because secret callbacks were wired after the first driver Add (including rotations during driver_init).

Startup wiring: newDriverRegistry installs SecretPersister / SecretOverride (per-driver driver_secret:<name>:<key> KV in state) before any driver init or poll. Registry comments now require this ordering.

Managed read-only OAuth: Signed manifests can grant http.post only with http.get and a declared AuthPostPath; ConfigSecrets lists which keys host.persist_secret may write. Keys are validated (length/format); values cap at 1 MiB. Control-v2 / non-OAuth managed drivers still cannot persist secrets. OAuth token POSTs must hit the exact path (no suffix/traversal) and cannot follow redirects (307/308).

Beta installs: RuntimePolicy resolves installs from the pinned beta trust source even when Settings lists only stable, with tests for cache reload, unknown repo, and bad signatures.

Reviewed by Cursor Bugbot for commit ab21200. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ee42340f-bd4a-44fa-b2b4-4faa1e83ea86)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-07T17:54:47.693154Z b16f803 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5974fc5a-60e4-4272-a306-d9c8e9a27c62)

@frahlg
frahlg merged commit d2f7fea into master Sep 7, 2026
17 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b16f803b90

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Rotations keep their own KV rows so they do not apply the whole config
// or restart the driver that just refreshed its credential.
driverSecretKey := func(driverName, key string) string {
return "driver_secret:" + driverName + ":" + key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Key rotated credentials by stable device identity

When a managed OAuth driver rotates token A to B and the operator later renames the YAML driver, this key changes even though the device does not. Because the source config intentionally remains at A, the next initialization misses B and presents the now-invalidated token, disconnecting the driver; reusing an old name can conversely apply that credential to the wrong device. Store and migrate the override under a stable hardware/account identity rather than cfg.Name.

AGENTS.md reference: AGENTS.md:L38-L38

Useful? React with 👍 / 👎.

Comment on lines +595 to +596
if repo == nil && m.betaRepo.ID != "" && installed.RepoID == m.betaRepo.ID {
repo = &m.betaRepo

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the beta trust identity across configuration changes

officialBetaRepository derives betaRepo.ID from the current configured repository IDs, so it can change between installation and restart. If a beta was installed under a suffixed ID because of a collision and that colliding repository is later removed, installed.RepoID matches neither the configured repositories nor the newly derived beta ID here, causing RuntimePolicy to return nil and the signed read-only artifact to run with the unrestricted legacy policy. The reverse change can bind an existing beta install to an unrelated configured repository and fail verification; recognize the immutable trust identity recorded at installation instead of only the current generated ID.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant