Skip to content

fix(cli): bind hub status credentials to connection snapshots - #4476

Merged
lidge-jun merged 1 commit into
devfrom
codex/260913-carry-4382-hub-status
Sep 13, 2026
Merged

fix(cli): bind hub status credentials to connection snapshots#4476
lidge-jun merged 1 commit into
devfrom
codex/260913-carry-4382-hub-status

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

Carries #4382 by @luvs01.

ocx status on a connected client read the connection snapshot and the current service-api-token independently. collectRemoteHubStatus awaits a dynamic import before it reads credentials, so a reconnect or a key rotation inside that window could send the new token to the snapshot's hub, or send the snapshot's hub a token that no longer belongs to it.

The collector now rereads the persisted connection and passes a token only when that connection still matches the snapshot's serverUrl, apiKeyId and connectedAt, and the token fingerprint matches that connection's tokenFingerprint. Otherwise it skips the live request and falls back to the snapshot owner's cached hub state, or reports unavailable.

Beyond the source branch, a withheld token now names its own cause. resolveHubState reported every null token as "this client has no usable data-plane token". That is false for a client that reconnected and holds a perfectly good token for a different hub, and it sends the operator to re-enroll a credential that is not the problem. The caller supplies the cause through the new withheldTokenReason, so a changed connection, a missing token file and a fingerprint mismatch read differently:

before: State unavailable - this client has no usable data-plane token
after:  State unavailable - the saved connection no longer matches the one this status reports

That folds the maintainer review finding on #4382 ("reason 문자열을 한 번 읽어 보면 좋습니다"), and it is the same misdiagnosis class as #4169 in the stop path.

This is the bottom link of lane L in devlog/_plan/260913_contributor_carry_train/. Its head commit carries [skip ci]; the lane tip runs the matrix for the whole lane.

Verification

  • bun test tests/cli/cli-status-hub-state.test.ts — 20 pass
  • bun test tests/clients/client-hub-state.test.ts tests/server/v1-hub-state.test.ts — 35 pass
  • bun run typecheck — clean
  • bun run structure:check — passed
  • bun run privacy:scan — passed
  • cd docs-site && bun install --frozen-lockfile && bun run build — 441 pages built. This is the one actionable CodeRabbit finding on the source pull request, and docs-site/AGENTS.md requires it for docs-site/** changes.

Local full suite was not run, per the lane's instruction. Hosted CI on the lane tip is the suite proof.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • New Features

    • ocx status now verifies connection and token details before requesting live Remote Hub state.
    • When live state cannot be safely retrieved, status uses matching cached data or reports unavailable.
    • JSON output identifies whether state came from the hub, cache, or was unavailable.
  • Documentation

    • Added guidance describing credential validation, cache fallback behavior, and status diagnostics in English and Korean documentation.
    • Documented token fingerprint participation in status credential binding.
  • Tests

    • Expanded coverage for credential mismatches, token rotation, rejected requests, and cache fallback behavior.

`ocx status` on a connected client read the connection snapshot and the current
`service-api-token` independently, so a reconnect or a key rotation between the two reads
could send the new token to the snapshot's hub, or the snapshot's hub the new connection's
token. The window is real: `collectRemoteHubStatus` awaits a dynamic import before it
reads credentials.

It now rereads the persisted connection and passes a token only when that connection still
matches the snapshot's `serverUrl`, `apiKeyId` and `connectedAt` AND the token fingerprint
matches that connection's `tokenFingerprint`. Otherwise it skips the live request and falls
back to the snapshot owner's cache, or reports `unavailable`.

A withheld token now carries its own cause. `resolveHubState` reported every null token as
"this client has no usable data-plane token", which is false for a client that reconnected
and holds a perfectly good token for a different hub - the operator would go re-enroll a
credential that is not the problem. The caller supplies the reason through the new
`withheldTokenReason`, so a changed connection, a missing token file and a fingerprint
mismatch are named separately. That folds the maintainer review finding on #4382; it is the
same misdiagnosis class as #4169 in the stop path.

Verification: bun test tests/cli/cli-status-hub-state.test.ts (20 pass),
tests/clients/client-hub-state.test.ts + tests/server/v1-hub-state.test.ts (35 pass),
bun run typecheck, bun run structure:check, bun run privacy:scan, and the docs-site
build required by docs-site/AGENTS.md (441 pages) - the one actionable CodeRabbit finding
on the source pull request.

Carried from #4382 by @luvs01.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 13, 2026 06:57
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 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-13T07:00:42.803657Z 80e07d0 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.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 13, 2026
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 205b61b2-851f-4ba1-8bc7-f570a66692ea

📥 Commits

Reviewing files that changed from the base of the PR and between b4dec34 and 80e07d0.

📒 Files selected for processing (9)
  • docs-site/src/content/docs/guides/remote-hub.md
  • docs-site/src/content/docs/ko/guides/remote-hub.md
  • src/cli/status.ts
  • src/client/hub-state.ts
  • structure/clients/claude-desktop.md
  • structure/config.md
  • structure/ops/docs-and-release.md
  • structure/runtime.md
  • tests/cli/cli-status-hub-state.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

ocx status now validates the saved connection and service-token state before requesting live hub state. It withholds mismatched credentials, reports specific reasons, and falls back to matching cached state or unavailable.

Changes

Remote Hub status handling

Layer / File(s) Summary
Hub-state fallback contract
src/client/hub-state.ts:164-169, src/client/hub-state.ts:204-206
resolveHubState accepts withheldTokenReason and uses it when no token is available before checking cached state.
Status token binding
src/cli/status.ts:21, src/cli/status.ts:328-356, src/cli/status.ts:373-382
collectRemoteHubStatus re-reads connection and token state. It sends the token only when the connection owner and token fingerprint match the status snapshot.
Validation and documentation
tests/cli/cli-status-hub-state.test.ts:30,92-112,128-136,145-216, docs-site/src/content/docs/guides/remote-hub.md:95-98, docs-site/src/content/docs/ko/guides/remote-hub.md:39-45, structure/clients/claude-desktop.md:72-74, structure/config.md:212-214, structure/ops/docs-and-release.md:311-314, structure/runtime.md:217-227
Tests cover stale connection data, token rotation, fingerprint mismatches, authenticated requests, and cache fallback. Documentation describes credential binding and hub, cache, and unavailable results.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 80e07

The status command safely withholds credentials when connection or token ownership changes and falls back to matching cached state or unavailable output. The change is ready to merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 3 files. (6 skipped: 6… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: binding CLI hub-status credentials to connection snapshots.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 3 files. (6 skipped: 6 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/260913-carry-4382-hub-status

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 71 / 80

설명
이 PR은 @luvs01의 #4382를 tip으로 캐리한다. 연결된 클라이언트의 ocx status가 connection snapshot과 service-api-token을 따로 읽었다. collectRemoteHubStatus가 hub-state를 dynamic import하는 사이에 재연결·키 회전이 일어나면, 스냅샷 허브에 새 토큰을 보내거나, 다른 연결의 멀쩡한 토큰을 “토큰 없음”으로 오인할 수 있다. #4169 stop 경로와 같은 종류의 소유권 불일치다.

수정은 src/cli/status.tsboundHubStateToken을 둔다. 현재 connection이 스냅샷 owner와 같은지, 토큰 fingerprint가 connection과 같은지를 본 뒤에만 토큰을 넘긴다. 막았으면 withheldTokenReason으로 진짜 이유를 넘긴다. src/client/hub-state.ts는 null 토큰일 때 그 이유를 캐시/unavailable 메시지에 쓴다. docs-site remote-hub(영/한)와 structure runtime/config 문서에 계약을 적었다. 테스트는 tests/cli/cli-status-hub-state.test.ts.

우선순위 71은 원격 허브 상태의 자격 증명 혼선(보안·운영)을 직접 막고, 변경이 status/hub-state로 한정되기 때문이다. tip b4dec3469 / 2.53.0, types/config 분할 무관.

src/cli/status.ts boundHubStateToken / collectRemoteHubStatus - snapshot owner·fingerprint 바인딩 후에만 live hub read.

src/client/hub-state.ts withheldTokenReason - “토큰 없음”과 “다른 연결용 토큰을 안 보냄”을 구분.

docs-site/.../remote-hub.md (en/ko) + structure/runtime.md - 운영자 계약. remoteHub.stateSource가 hub|cache|unavailable.

심볼 #4382 / #4169 - 같은 결함 가족. 캐리 후 원본 닫기.

메인테이너의 판단이 필요한 지점

너의 추천
테스트 초록이면 머지하세요. 머지 후 #4382를 Landed via #4476으로 닫으세요.

이 댓글은 grok-bot이 작성했습니다

lidge-jun added a commit that referenced this pull request Sep 13, 2026
…og-pull

Lane L of the contributor carry train: hub status credential binding (#4382 by luvs01) and the authenticated remote catalog pull (#4413 by rrmlima).

Tip-only CI by owner authorization for this batch. Run 34744284722 concluded success on 394b96d, the exact head merged here, and it covers both links because the lane is cumulative. #4476 carries no ci check of its own.
@lidge-jun
lidge-jun merged commit 80e07d0 into dev Sep 13, 2026
9 checks passed
@lidge-jun
lidge-jun deleted the codex/260913-carry-4382-hub-status branch September 13, 2026 07:24
@lidge-jun

Copy link
Copy Markdown
Owner Author

Merged into dev as part of lane L, under the tip-only CI economy the repository owner authorized for this batch.

Three things this record names explicitly, so the deviation stays a recorded decision rather than an inferred one:

Landed via merge commit d865aac, verified as an ancestor of origin/dev.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant