Skip to content

fix(cli): the startup banner reads a DSN-declared datasource, and stops printing credentials (#3793) - #3819

Merged
os-zhuang merged 1 commit into
mainfrom
claude/startup-banner-dsn-datasource-6u731r
Jul 28, 2026
Merged

fix(cli): the startup banner reads a DSN-declared datasource, and stops printing credentials (#3793)#3819
os-zhuang merged 1 commit into
mainfrom
claude/startup-banner-dsn-datasource-6u731r

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #3793.

现象

  Driver:  SqlDriver(pg)  → (unknown)

driver 认出来了,地址没了 —— 而且恰好在数据库连不上、最需要看地址的时候没了。

根因

describeRegisteredDriver 认识 config.connection 四种形状里的三种(DSN 字符串、{ filename }、离散 { host, port, database }),唯独不认 { connectionString } —— 也就是 defaultDatasourceDriverFactory 为「用 DSN 声明的 pg datasource」产出的那一种。driver.config 保持作者传入的原形状(driver-sql 有测试钉住),所以正如 issue 里说的,#3791 既不是起因也没让它变严重。

顺着查出同一个根因的另外两处 (unknown):那个 reader 只要 driver 有 config 就直接 return,于是只有 SqlDriver 能成功。

  • MongoDB 的 DSN 在顶层 config.url,而 MongoDBDriver 恰好 config —— 下面那条「mongo/turso 把 URL 挂在实例上」的分支根本到不了,每次 mongo 启动横幅都是 (unknown)
  • In-memoryconfig 在没传参时是 {},truthy —— (in-memory) 那条分支同样到不了,preset 装配的 memory driver 显示成 com.objectstack.driver.memory → (unknown)

还有一个同处的潜伏 bug:SqliteWasmDriver 传给 knex 的 client而不是字符串,SqlDriver(${cfg.client}) 会把整个类源码贴进横幅。

处置

按 issue 的建议,两件事收进一个 renderer —— packages/cli/src/utils/connection-display.ts:

  • describeDriverConnection(config) 认全部四种 connection 形状,外加 mongo 系的 { uri } / { url } 拼法;同一套字段优先级也应用到顶层地址上(覆盖 MongoDBDriver)。对函数形式的 connection(host 每次取连接时现造)返回 undefined 而不是瞎猜。
  • redactConnectionUrl(value) 直接丢弃凭据而不是打码。原来的 //user:****@ 掩码漏了两处:没有用户名的密码(postgres://:secret@host/db)不匹配那个正则,原样打印;Turso 的 DSN 把 secret 放在 query string 里(?authToken=…),从来没被碰过。现在 userinfo 和 query 一起丢掉;非 URL 的值(:memory:、sqlite 路径、(in-memory))原样透传;函数幂等。

serve / start / dev 里三份逐字节相同的旧掩码,加上 schema-migrate 里的第四个变体,一起收敛到这个 helper。schema-migrate 那份有同样的 DSN 盲区,但后果更重:它喂的是 Apply N change(s) to …? 这个确认提示,{ connectionString } 配置在那里渲染成一个光秃秃的 pg —— 等于让操作者对着一个没名字的数据库批准破坏性迁移。

变更清单

文件 变更
packages/cli/src/utils/connection-display.ts 新增:redactConnectionUrl + describeDriverConnection
packages/cli/src/commands/serve.ts describeRegisteredDriver 按形状读地址(不再「有 config 就 return」);label 只在 client 是字符串时插值;导出以便测试
packages/cli/src/commands/start.ts / dev.ts 删掉各自的 redactDbUrl 副本
packages/cli/src/utils/schema-migrate.ts describeDb 复用同一个 renderer;删掉 redactUrl
.changeset/banner-dsn-connection-display.md patch changeset

验证

  • 新增单测 26 个(connection-display.test.ts + serve-driver-banner.test.ts),CLI 全量 vitest run 72 files / 734 tests 全绿;tsc -p tsconfig.build.json + eslint src 干净。

  • 端到端跑通 issue 的复现路径:在 examples/app-todo 里用 preset 装配一个 SqlDriver({ connection: { connectionString: 'postgres://admin:hunter2@127.0.0.1:59437/nope' } }),OS_ALLOW_DRIVER_CONNECT_FAILURE=1 os serve,同一份 app、同一条命令:

    Driver 行
    修复前 SqlDriver(pg) → (unknown)
    修复后 SqlDriver(pg) → postgres://127.0.0.1:59437/nope

    admin:hunter2 不见了。另外单独跑了 OS_DATABASE_URL 那条路径(走 createStorageDriver,不走 probe),横幅同样输出脱敏后的地址。

注意

纯展示层改动,不动任何配置或 API surface。横幅/提示的输出串本身有变化:postgres://admin:hunter2@db:5432/app 原来显示为 postgres://admin:****@db:5432/app,现在是 postgres://db:5432/appschema-migrate 的提示也随之统一:sqlite 从 sqlite:./a.db 变成 ./a.db,离散字段的 pg 从 pg://host/db 变成 host:5432/db(DSN 情形保留 scheme)。


Generated by Claude Code

…ps printing credentials (#3793)

`OS_DATABASE_URL="postgres://u:p@127.0.0.1:59437/nope" os serve` bannered
`Driver: SqlDriver(pg)  → (unknown)` — the driver identified, the address
gone, at exactly the moment (an unreachable database) when the address is
the one thing worth reading.

`describeRegisteredDriver` knew three of the four shapes a driver's
`config.connection` arrives in — a DSN string, `{ filename }`, and discrete
`{ host, port, database }` — but not `{ connectionString }`, which is what
`defaultDatasourceDriverFactory` builds for a pg datasource declared with
`config.url` / `config.connectionString`. `driver.config` keeps the shape its
author passed (pinned by a driver-sql test), so #3791's knex-side rewrite
neither caused nor worsened this.

Two more `(unknown)`s had the same cause: the reader returned as soon as a
driver had a `config` at all, so only a SqlDriver could ever succeed.
MongoDBDriver keeps its DSN in a top-level `config.url` and *does* have a
`config`, so the "mongo/turso expose the URL on the instance" arm below was
unreachable; InMemoryDriver's `config` is `{}`, which is truthy, so the
`(in-memory)` arm was unreachable too. Both are now read by shape. A latent
label bug goes with them: SqliteWasmDriver passes knex a dialect *class* as
`client`, and `SqlDriver(${cfg.client})` would have pasted the class source
into the banner — only a string `client` is interpolated now.

Both halves are one renderer, `utils/connection-display.ts`:

- `describeDriverConnection(config)` knows every `connection` shape plus the
  `{ uri }` / `{ url }` spellings, applies the same precedence to a top-level
  address, and returns `undefined` — not a guess — for a function-valued
  `connection` the host builds per pool checkout.
- `redactConnectionUrl(value)` drops credentials rather than masking them.
  The old `//user:****@` mask left two holes: a password with no username
  (`postgres://:secret@host/db`) did not match its regex and printed verbatim,
  and a Turso DSN carries its secret in the query string, which was never
  touched. Userinfo and query are both dropped; non-URL values (`:memory:`, a
  sqlite path) pass through; the function is idempotent.

Three byte-identical copies of the old mask (serve / start / dev) and a fourth
variant in `schema-migrate` collapse into it. The `schema-migrate` copy had the
same DSN blindness with higher stakes — it feeds the `Apply N change(s) to …?`
confirm, where a `{ connectionString }` config rendered as a bare `pg`, asking
an operator to approve a destructive migration against an unnamed database.

Verified end to end on `examples/app-todo` with a preset-wired
`SqlDriver({ connection: { connectionString } })`: `→ (unknown)` before,
`→ postgres://127.0.0.1:59437/nope` after, with `admin:hunter2` gone.

Display only — no configuration or API surface moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1DiRxoPdcTSGkzywm5zN4
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 7:17am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/l labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli.

18 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/cli)
  • content/docs/api/client-sdk.mdx (via @objectstack/cli)
  • content/docs/api/data-flow.mdx (via @objectstack/cli)
  • content/docs/api/environment-routing.mdx (via @objectstack/cli)
  • content/docs/api/error-catalog.mdx (via @objectstack/cli)
  • content/docs/automation/hook-bodies.mdx (via packages/cli)
  • content/docs/deployment/backup-restore.mdx (via @objectstack/cli)
  • content/docs/deployment/cli.mdx (via @objectstack/cli)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/cli)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/cli)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/cli)
  • content/docs/kernel/runtime-services/index.mdx (via packages/cli)
  • content/docs/permissions/authentication.mdx (via @objectstack/cli)
  • content/docs/plugins/packages.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/cli)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/cli)
  • content/docs/releases/implementation-status.mdx (via @objectstack/cli)
  • content/docs/releases/v16.mdx (via @objectstack/cli)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 07:19
@os-zhuang
os-zhuang merged commit 4c0df37 into main Jul 28, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/startup-banner-dsn-datasource-6u731r branch July 28, 2026 07:32
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 size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

启动横幅对 DSN 形式的 datasource 显示 → (unknown):describeRegisteredDriver 不认 connectionString

2 participants