Skip to content

docs(remote-hub): warn about shared-host unauthenticated loopback companion - #5444

Closed
luvs01 wants to merge 3 commits into
lidge-jun:devfrom
luvs01:fix/493-remote-hub-doc
Closed

luvs01 wants to merge 3 commits into
lidge-jun:devfrom
luvs01:fix/493-remote-hub-doc

Conversation

@luvs01

@luvs01 luvs01 commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

  • The remote-hub guide enables an unauthenticated loopback companion without warning that every process and OS user on the host can use the hub's provider credentials and account quota, or starve authenticated remote clients.

Description

  • Add a :::danger admonition to the English and Korean remote-hub guides telling operators to use a dedicated single-tenant host and to omit unauthenticatedLoopbackListener on a shared or multi-tenant host.
  • Add a docs test asserting both locales carry the dedicated-host warning.

Testing

  • bun test tests/ci-workflows/docs-remote-hub-claims.test.ts: 53 tests pass.

@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 21, 2026
@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed.

Hygiene

✅ Deterministic PR hygiene checks passed.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 31 / 80

이 PR은 프로그램 동작을 바꾸지 않습니다. 원격 허브 설치 가이드에 빨간 경고만 넣습니다.

허브를 깔 때 예시에는 unauthenticatedLoopbackListener를 켜는 명령이 있습니다. 이 스위치를 켜면 그 컴퓨터의 127.0.0.1이 열쇠 없이 모델 호출을 받습니다. 그 컴퓨터의 다른 프로그램과 다른 로그인 사용자는 허브에 들어 있는 유료 계정과 사용량을 쓸 수 있고, 멀리서 로그인한 사람이 응답을 못 받을 수도 있습니다. 영어 가이드와 한국어 가이드의 설치 예시 바로 위에, 혼자 쓰는 컴퓨터에서만 켜고 공유 컴퓨터에서는 그 명령을 빼라고 적었습니다. 테스트는 그 문장이 두 파일에 있는지만 봅니다.

이 위험은 새 사실이 아닙니다. 설정 레퍼런스 docs-site/src/content/docs/reference/configuration/server.md:315에 이미 같은 경고가 있습니다. 거기에는 한 줄이 더 있습니다. 127.0.0.1은 밖에서의 접속은 막지만 브라우저가 그 주소로 붙는 것까지 막지는 못하고, 그래서 Host와 Origin 검사를 한다고 적혀 있습니다. 이번 가이드 경고는 그 문장을 빼서, 모든 프로세스가 그대로 계정을 쓴다고만 읽힙니다.

라인 tests/ci-workflows/docs-remote-hub-claims.test.ts:128 - 테스트 이름은 전용 호스트가 필요하다고 하는데, "dedicated"나 "전용"은 검사하지 않습니다. 영어는 "shared or multi-tenant host"만 있으면 통과합니다. "켜지 마세요"가 빠져도 통과합니다. 한국어는 "활성화하지 마세요"까지 봅니다. 같은 경고인데 영어 쪽이 더 헐겁습니다. :::danger 상자가 있는지도 안 봅니다.

라인 docs-site/src/content/docs/guides/remote-hub.md:223 - 포트를 나눠 쓰는 예시 {"enabled":true,"port":10104}도 같은 열쇠 없는 주소입니다. 경고는 114행 첫 예시 위에만 있습니다. 이 줄만 보고 따라 하면 경고가 눈에 안 들어옵니다. 한국어 가이드 119행도 같습니다.

라인 skills/ocx/references/05_remote_hub.md:18 - 에이전트가 읽는 설명은 여전히 "enabled": true만 보여 주고, 공유 컴퓨터에서는 켜지 말라는 문장이 없습니다. 가이드만 고치면 스킬을 따르는 안내는 예전과 같습니다.

라인 docs-site/src/content/docs/ko/guides/remote-hub.md:55 - "인증된 원격 클라이언트를 굶길 수 있습니다"는 영어 starve를 그대로 옮긴 말입니다. 설정 레퍼런스가 말하는 뜻은, 한 번에 받을 수 있는 요청 수를 이 주소가 먼저 다 써 버릴 수 있다는 것입니다.

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

가이드 경고를 설정 레퍼런스 315행과 같은 내용으로 맞출지입니다. 브라우저 Host/Origin 검사를 빼면 경고가 실제보다 넓어집니다. 스킬 문서에도 같은 한 줄을 넣을지도 같이 보면 됩니다.

ja, zh-cn, zh-tw, fr, ru, tr 가이드는 이 켜는 명령을 아직 안 가르치므로, 이번 PR에서 그 파일까지 고칠 필요는 없습니다.

너의 추천

설치 예시 위에 경고를 둔 방향은 맞습니다. 병합 전에 영어 테스트가 "Do not enable"과 "dedicated"를 보게 하고, 포트를 나누는 예시 옆에도 같은 위험이라고 한 줄 남기세요. 가이드 문장은 설정 레퍼런스처럼 브라우저 검사가 있다는 한 줄을 남기는 편이 정확합니다. 스킬 문서에 경고를 안 넣을 거면 그 빈칸을 PR 설명에 적어두세요. 베이스는 이미 dev이고, types.ts/config.ts 분할과 겹치지 않아서 중복으로 닫을 이유는 없습니다. 아직 draft이고 본문 체크리스트는 0/4입니다. 문장을 고친 다음 준비됨으로 바꾸면 됩니다.

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

@luvs01

luvs01 commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

Review feedback applied on b00ed1ebf2:nn- Both locale danger boxes now match the reference: shared turn capacity exhaustion (not just "starve"), plus the browser Host/Origin check sentence.n- The ported-form command ("port":10104) now carries an explicit same-surface warning in both locales.n- skills/ocx/references/05_remote_hub.md gained the same dedicated-host warning, so agent-facing guidance is no longer the gap.n- The test now asserts dedicated/Do not enableper locale, the:::danger box itself, and that a warning appears near the ported-form command.nnTests: docs-remote-hub-claims` 53 pass.

lidge-jun added a commit that referenced this pull request Sep 22, 2026
Review nits on the carried #5248 and #5444 units: document that the
count-based estimator takes non-negative integer counts, pin the
pure-CJK and empty-model-id cases against the replacement-string
formula, and require the dedicated-host warning to sit inside the
danger callout in both locales rather than anywhere in the page.
@lidge-jun

Copy link
Copy Markdown
Owner

Carried into #5598 as a cherry-pick (a0233ca, b00ed1e), with your authorship kept; the claims test now requires the warning inside the danger callout. Thank you, @luvs01. Closing in favor of #5598.

@lidge-jun lidge-jun closed this Sep 22, 2026
lidge-jun added a commit that referenced this pull request Sep 23, 2026
…ck proxy, OAuth, remote hub docs) (#5598)

* fix(kiro): avoid mixed-script estimator allocations

(cherry picked from commit 24c3cd3)

* test(kiro): register kiro-wire-estimate in test layout

(cherry picked from commit e796b5c)

* fix(crash-guard): count unparenthesized JS frames as real throw sites

isBenignAbortTeardown only recognised parenthesized "(file:line:col)"
frames, so a genuine TypeError whose stack carries "at /abs/x.ts:1:2",
"at file:///x.ts:1:2" or a Windows drive path was folded into the
rate-limited benign-teardown summary. Scan every "at ..." frame and
treat any non-native line:col location as a JS source frame.

Hidden JSC fields (sourceURL/line/column) deliberately do not veto the
benign class: Bun can attach them to errors raised from builtin frames,
and the benign summary already records them through diagnose().

Reimplements #5286.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(proxy): keep loopback traffic outside inherited SOCKS

(cherry picked from commit 3746991)

* fix(proxy): pin non-loopback SOCKS ownership and document the ambient noProxy scope

(cherry picked from commit 7dd72d1)

* fix(oauth): preserve hashes in command code callback JSON

(cherry picked from commit 225eb81)

* test(oauth): drive Command Code callback JSON through the shared submit path

The provider parser, not the shared code#state gate, owns the state check
for pasted Command Code callback JSON. Pin both halves end to end: a
wrong-state paste is accepted by submitManualLoginCode, rejected by the
provider loop and re-prompted without a whoami call, and the right-state
paste keeps "#" inside its fields intact through to the stored key.

Follow-up to the carried #5413 fix.

* docs(remote-hub): warn about shared-host unauthenticated loopback companion

(cherry picked from commit a0233ca)

* docs(remote-hub): align the loopback warning with the reference and cover the ported form

(cherry picked from commit b00ed1e)

* fix(oauth): drop legacy credential backup on destructive mutations

auth.json.pre-multiauth copies the whole legacy store for downgrade recovery, but removeCredential/removeAccount left it behind — and a legacy-shaped store re-created it — so logout and account deletion kept a file holding the very refresh tokens the user destroyed.

mutateStore gains a removeLegacyBackup option: it skips the one-time create and unlinks the backup after persist. Removal is best-effort (ENOENT ignored, other failures warn) because it runs after the store is persisted — a failed unlink must not report a failed logout for an account that is already gone. A stale uninstall-manifest entry is harmless: removeOwnedConfigState skips missing paths.

Covers the destructive-migration, pre-existing-backup, and stale-backup-on-migrated-store cases in oauth-store-multi tests.

(cherry picked from commit f456a4f)

* docs(structure): note legacy backup removal on destructive auth mutations

(cherry picked from commit b00133c)

* fix(oauth): only drop the downgrade backup when a credential was actually removed

removeCredential and removeAccount passed removeLegacyBackup unconditionally,
so a stale or concurrent request that returned "not-found" or false still
unlinked auth.json.pre-multiauth and still skipped creating it for a legacy
store. That backup is a whole-store copy, so a removal that deleted nothing
destroyed downgrade recovery for every provider in it.

Decide from the mutation result instead. The decision moves to just after the
mutation body, which only edits the in-memory store, so nothing has touched
disk when it is taken, and the create and remove paths stay mutually
exclusive as before. Cover both no-op results.

(cherry picked from commit 160d3ab)

* fix(oauth): drop the downgrade backup when a provider is deleted

Deleting a provider from the dashboard clears its credentials through
replaceProviderAccountSet(name, null), which bypassed the carried
logout/account-deletion rule and kept (or first created) the
auth.json.pre-multiauth copy of the deleted tokens. Treat clearing a
provider that had credentials as destructive; a no-op clear and a
non-empty replacement keep the backup. Pin the warning path when the
backup cannot be removed after the credential is already gone.

Completes the reimplementation of #4949.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(crash-guard): log a new hidden throw site inside the benign fold window

Review of the carried #5286 frame fix found the remaining gap: a benign
teardown whose JSC hidden sourceURL/line/column names a different throw
site was still folded silently for five minutes, so a distinct fault
could vanish between summaries. Log the summary when that hidden throw
site differs from the last one logged; repeats of the same site, and
errors without one, still fold. Classification is unchanged.

* fix(proxy): merge loopback into an inherited lowercase no_proxy

Review of the carried #5430 fix: Bun's native fetch reads a non-empty
lowercase no_proxy before NO_PROXY, so with HTTP_PROXY and an inherited
no_proxy the loopback entries written to NO_PROXY never applied and
local calls could still go through the proxy. Merge the same configured
and loopback entries into an inherited no_proxy, keeping its own entries.

The non-loopback SOCKS test now uses a local proxy that drops every
connection and an IP-literal target, so it no longer waits on DNS.

* fix(oauth): keep other providers in the downgrade backup on deletion

Review of the carried #4949 change: deleting one provider removed the
whole auth.json.pre-multiauth file, so a legacy store with several
providers lost downgrade recovery for every provider the user kept
(the migrated auth.json is unreadable to an older loader). Destructive
mutations now remove only the affected provider's entry and delete the
file once it is empty. A backup entry that is not a regular file is
removed rather than followed, and the rewrite replaces the entry itself
without resolving a symlink.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(oauth): check an explicit #state on a raw Command Code paste

Review of the carried #5413 change found the direct CLI prompt path,
which does not go through submitManualLoginCode, accepting a raw
"key#state" paste whose state did not match: only URL- and query-shaped
input was compared. Treat a raw paste with an explicit #state suffix as
state-bearing, as the shared submit gate already does; a bare key still
needs no state. The end-to-end submit test now observes the login
promise from the start so an early failure cannot leave it unhandled.

* test: tighten the carried Kiro estimate and remote-hub warning checks

Review nits on the carried #5248 and #5444 units: document that the
count-based estimator takes non-negative integer counts, pin the
pure-CJK and empty-model-id cases against the replacement-string
formula, and require the dedicated-host warning to sit inside the
danger callout in both locales rather than anywhere in the page.

* fix(crash-guard): keep the hidden throw-site read inside the handler

Re-review found that reading the hidden JSC fields for the benign fold
could throw from an unusual accessor before the rejection was logged,
escaping the process unhandledRejection listener. Make the read
best-effort, as diagnose() already is, and pin it with a Proxy whose
sourceURL getter throws.

* fix(oauth): never write the downgrade backup through a link or claim it

Re-review of the backup scrub found two gaps. backupLegacyOnce used
existsSync, which reports a dangling symlink as absent, and then copied
through it, so credentials could land outside the config directory;
it now treats any existing entry as occupied and copies exclusively.
The scrub rewrite went through the shared atomic writer, which records
the path for uninstall, so a backup this install never registered was
claimed and later deleted; it now replaces the entry through an
exclusive private temp and a rename, leaving ownership unchanged.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* test(kiro): make the pure-CJK estimate case actually zero-Latin

* fix(oauth): rewrite the downgrade backup through the shared writer

Re-review: the local temp-and-rename used for the backup scrub skipped
what the shared atomic writer guarantees (Windows ACL hardening before
the temp holds a byte, an explicit 0600 on POSIX regardless of umask,
scrub-and-unlink of a failed temp, the Windows rename retry), which
structure/config.md forbids replacing. Add a no-follow variant of that
writer that leaves the owner manifest untouched, and use it, so a
backup an earlier install left unregistered still stays unclaimed.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* docs(structure): state when an existing OAuth downgrade copy is rewritten

* fix(proxy): scope the inherited-proxy loopback bypass to exact hosts

Security review of the carried #5430 change: on the no-config.proxy path
loopback names were added to NO_PROXY for any inherited proxy, and both
matchers treat entries as domain suffixes, so "localhost" also sent any
*.localhost name direct, which need not resolve to loopback.

Add the loopback entries only when an inherited SOCKS proxy owns HTTP(S)
traffic; that proxy is applied by the in-process matcher, which now
treats a bare localhost or IP-literal entry as one host (a leading dot
still means subdomains). An inherited HTTP(S) proxy is Bun's own and is
left as it was. Tests cover *.localhost staying on the SOCKS proxy, the
no-widening cases, and the exact-match rules.

* fix(proxy): key the loopback bypass on SOCKS precedence

Security re-review: with an inherited SOCKS proxy beside an inherited
HTTP(S) proxy the fetch wrapper still applies SOCKS first, so skipping
the loopback entries in that case let local requests pass through SOCKS.
Add them whenever an inherited SOCKS proxy exists; its exact matcher
decides first, so only a request already judged loopback reaches Bun's
suffix-matching proxy. Pin the mixed case.

* fix(proxy): keep 127.0.0.1 off an inherited HTTP proxy without suffix risk

Delta review: with no config.proxy and only an inherited HTTP(S) proxy,
loopback calls such as the CLI's own management and health requests to
127.0.0.1 could go to that proxy. Add the loopback addresses there (a
URL host ending in a numeric label parses as IPv4, so Bun's suffix
matching cannot widen them) but not "localhost", which Bun would match
as *.localhost. An inherited SOCKS proxy keeps the full list, and a
process with no inherited proxy is still left untouched.

* fix(proxy): add bare localhost only when SOCKS is the sole inherited proxy

Security re-review: beside an inherited HTTP(S) proxy, a bare localhost
in process-wide NO_PROXY is also read by Bun, whose suffix matching can
send an unwrapped fetch for any *.localhost name past that proxy. Write
the full loopback list only when an inherited SOCKS proxy is the only
inherited proxy; whenever Bun applies an inherited HTTP(S) proxy, add
only the loopback addresses. The mixed case keeps 127.0.0.1 direct
through the SOCKS wrapper and sends *.localhost to SOCKS.

* fix(proxy): never add a bare localhost to an inherited lowercase no_proxy

Review: with a configured HTTP(S) proxy and an inherited lowercase
no_proxy, which Bun reads first and matches by domain suffix, adding a
bare localhost newly let *.localhost names bypass the proxy. The
lowercase value now gains the configured entries and the loopback
addresses only.

* fix(proxy): add only loopback addresses to an inherited lowercase no_proxy

Delta review: configured noProxy entries were also copied into an
inherited lowercase no_proxy, which Bun reads first and matches by
suffix; on dev that inherited value always shadowed them, so a
configured "localhost" newly let *.localhost bypass a configured proxy.
The lowercase value now gains the loopback addresses only.

* test(oauth): assert a cleared provider set as null, as getAccountSet returns

Hosted CI shard 1/4 at 00e5111: getAccountSet returns null for a
missing provider, so the two new backup tests' toBeUndefined() failed.

* fix: bypass inherited HTTP ALL_PROXY for loopback

Cover both ALL_PROXY casings and repair review notes for hub docs, Kiro fallback coverage, and OAuth backup comments.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix: keep localhost direct with mixed inherited proxies

Force native direct fetch for exact localhost when the inherited SOCKS wrapper is active, and cover both ALL_PROXY casing combinations.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: Epinephrine <luvs01@hanmail.net>
Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants