Connection registry is process-global: a multi-project server reuses one project's warehouse config (and, since #1204, its resolved store path) for another
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 38/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- sql, typescript
調査の方向性
packages/opencode/src/altimate/native/connections/registry.ts から始め、特にモジュールスコープの configs/connectors/pending/loaded 宣言と、load()、ensureLoaded()、resolveStorePaths()、get()、list()、test()、add()、remove()、reload()、reset() を確認します。issue における done は、レジストリの状態が Instance.directory/InstanceState ごとにスコープされ、インスタンスがなくなったときにキャッシュされたコネクタがクリーンアップされ、プロセス全体で共有されないことを意味します。
索引モデルが issue の本文から書いたものです。
説明
Summary
The native connection registry keeps its entire loaded-config state at module scope, with a one-shot loaded latch. In a long-lived server process that handles requests for more than one project directory, the first request's load() wins permanently — every later request for a different project reuses the first project's connection configs and connectors, and never reads the second project's own .altimate-code/connections.json.
If two projects define a connection under the same name (a plausible default such as warehouse or local), project B's sql_execute silently runs against project A's database.
Where
packages/opencode/src/altimate/native/connections/registry.ts:
registry.ts:24 let configs = new Map<string, ConnectionConfig>()
registry.ts:27 const connectors = new Map<string, Connector>()
registry.ts:30 const pending = new Map<string, Promise<Connector>>()
registry.ts:33 let loaded = false
load() (:85-103) clears and repopulates configs from Instance.directory-relative config files, then latches loaded = true at :103. ensureLoaded() (:107) is a no-op once the latch is set. In a server that serves multiple project directories from one process, the first load() result is reused for all subsequent projects — including connection names, credentials, and (see below) resolved absolute store paths.
Pre-existing, but recently made worse
This sharing mechanism is pre-existing — confirmed by diffing origin/main before PR #1204 (commit d9292ecfe7): the identical module-scope configs/connectors/loaded pattern with the identical one-shot latch already existed (keyed off process.cwd() instead of Instance.directory). #1204 did not create it.
What #1204 (merged as 1caa234ff9) changed is the failure mode. Before #1204, a leaked config's relative store path was resolved lazily at connect() time against the then-current process.cwd() — ambiguous, and in a server that never chdirs this usually just errored or opened nothing meaningful. After #1204, resolveStorePaths() (:109-129, called at :175 and :620) pre-resolves and caches an already-absolute store path into configs at first-load time. So the same pre-existing leak now hands project B's request a deterministic absolute path to project A's real store — project B silently reads and writes project A's actual data rather than failing ambiguously.
The leak mechanism is old; the escalation from "fails ambiguously" to "silent cross-project read/write" landed with #1204.
Scope of impact
- Not triggered by normal single-project CLI usage (one process serves one project; the registry only ever holds that project's config).
- Triggered when a long-lived process serves multiple project directories through this registry and same-named connections exist across them.
Minimal fix (needs its own design PR)
Move configs, connectors, pending, and loaded off module scope into InstanceState, keyed by Instance.directory, with connector cleanup wired into the instance finalizer so cached native handles (DuckDB/SQLite file locks, SSH tunnels) are closed when an instance goes away rather than leaking for the process lifetime. ensureLoaded() / load() / get() / list() / test() / add() / remove() / reload() / reset() all need to read/write the current instance's slice rather than the shared module map — a real refactor, not a patch.
Citations
registry.ts:24,27,30,33 (module state), :69-75 (projectRoot()), :85-103 (load()), :107 (ensureLoaded()), :109-129 (resolveStorePaths()), :175 and :620 (call sites).
Surfaced by review threads on #1204 (cubic + codex + coderabbitai all flagged the same pattern, "make registry state instance-scoped"). #1204 merged before these were addressed; this issue tracks the follow-up.
- 主要言語
- TypeScript
- スター
- 813
- フォーク
- 134
- 平均マージ
- 2日 5時間
- マージ済み PR(30日)
- 62
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
AltimateAI/altimate-code のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
AltimateAI/altimate-code#1323 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
AltimateAI/altimate-code#1288 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
AltimateAI/altimate-code#1285 ·
-
privacy: Altimate Base consent dialog no longer discloses persistent per-installation identifier オープン
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
AltimateAI/altimate-code#1284 ·
-
難易度 2/5 1時間未満 初心者へのやさしさ 72/100
AltimateAI/altimate-code#1283 ·
AltimateAI/altimate-code の issue をすべて見る
似ている issue
-
Browser Waiting for: Product Owner
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
getsentry/sentry-javascript#24577 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
agilepathway/label-checker#640 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
copse-dev/agent-pane#2953 ·
-
agentic-workflows
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
githubnext/rig#534 ·
-
automation missing-model model-sync provider:pioneer
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
anomalyco/models.dev#7701 ·