AltimateAI / AltimateAI/altimate-code

Connection registry is process-global: a multi-project server reuses one project's warehouse config (and, since #1204, its resolved store path) for another

オープン
#1,237 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
TypeScript
スター
811
フォーク
134
平均マージ
3日 2時間
マージ済み PR(30日)
50

説明

## 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`:

```ts
registry.ts:24 let configs = new Map()
registry.ts:27 const connectors = new Map()
registry.ts:30 const pending = new Map>()
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 `chdir`s 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.

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。