You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
runtime(standalone host): the unsupported-database-URL-scheme refusal prints the whole OS_DATABASE_URL, so a password in the URL reaches the console and the boot log #23764
Filing gate ①, class (a), with the security exception (a credential in a boot log). reach:measured at the public door by #23633's dev (os-dev-report on #23633, out-of-scope finding 1), on PR #23761's branch, where packages/runtime is unchanged from main.
Filed by domain:cli seat 1 (#6024) · os-bill · session session_01FLwgbTSmGMrqsXkBDsiwfG, in the same act as the seat's review of PR #23761. ⛔ Not a claim. Grade and lane are triage's.
What was measured
OS_DATABASE_URL=redis://admin:PASSWORD@127.0.0.1:6399/0 os dev --no-watch over a plain config (the runtime's standalone host):
os dev's own Database: row redacts the credential;
then the boot refusal prints [StandaloneStack] Unsupported database URL scheme: redis://admin:PASSWORD@127.0.0.1:6399/0 in full.
Why, read on origin/main
detectDriverFromUrl (packages/runtime/src/standalone-stack.ts:399) builds its refusal with `[StandaloneStack] Unsupported database URL scheme: ${dbUrl}. …`, the raw URL.
The CLI host's refusal for the same URL, added by PR #23761 (#23633), names the scheme only, "never the URL, so credentials in the URL are not printed". After that PR lands, the two hosts refuse the same URLs but only one of them leaks.
Reader who acts
Triage grades it and routes it. The landing is detectDriverFromUrl's throw in packages/runtime/src/standalone-stack.ts: name the scheme (or a redacted URL, as redactConnectionUrl does for the banner), never the raw URL. Pin it with a credential-bearing URL whose password must not appear in the error.
Dedupe
MCP search_issues on objectstack-ai/objectstack, closed included: standalone-stack unsupported database URL scheme refusal prints password credentials detectDriverFromUrl gave 5 hits. The nearest is #8082 (closed; a [Decision] on URL-embedded credentials in driver config.url at publish). It is about stored config, not this refusal's text. None names this throw.
Dedupe words: unsupported database URL scheme prints password; standalone-stack refusal credential leak; detectDriverFromUrl full URL in error.
A credential in the database URL reaches the console and the boot log, which a deployment may ship to a log aggregator. It is the framework-side sibling of objectstack-ai/cloud#2894's class (a DSN with its password in a log).
It fires only when the URL's scheme is unsupported, which is why this is p1 and not p0.
Claim: PM loop round 27 (#23764: the standalone host's unsupported-database-URL-scheme refusal names the scheme, never the raw OS_DATABASE_URL, so a password in the URL no longer reaches the console or the boot log) · 2026-10-16T01:58Z
Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-23764-unsupported-scheme-no-url
Worktree: objectstack-issue-23764
Domain: domain:cli
Seat: domain:cli#2 (seat post #22648)
Scope: batch 1, by the maintainer's 「当前任务处理完,合并后就改为串行」. #23050 slice 7 landed at 2026-10-16T01:54Z and was released, so nothing of this seat's is in flight. This is the lane's only priority:p1 card in pm:queue, ahead of the two p2 cards (#23737, #23050), read at this act.
Order, read at this act: triage's grade 6205637664 (p1, security) puts no order on it. PR #23761 (#23633, seat 1's) leaves packages/runtime unchanged, and no open PR touches packages/runtime/src/.
File surface (read on origin/mainec44cf60bf; stop on a breach and explain it in the report):
packages/runtime/src/standalone-stack.ts:detectDriverFromUrl's unsupported-scheme throw (about :433), and any sibling message in the same host that echoes a raw database URL, if the dev measures that it can carry a credential (refuseRetiredMemoryEngine's URL phrases about :540-:543, the sqlite non-path refusal about :557).
Pins: beside the change, with a credential-bearing placeholder URL whose password must not appear in the error. Plus one row at the public door the card measured (os dev / os start over the standalone host), if the door can be driven in a test.
One .changeset/23764-*.md (@objectstack/runtimepatch).
os-dev-report
{
"issue": 23764,
"status": "done",
"branch": "claude/issue-23764-unsupported-scheme-no-url",
"pr": "#23791",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching PM session, the harness-stamped Claude-Session; this run is mode:subagent)",
"premise_still_valid": true,
"summary": "Premise held, measured on base ec44cf6. With OS_DATABASE_URL=redis://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN, all five public doors printed each placeholder once in the refusal: os dev --no-watch, os start, os serve, os migrate plan, and the stdout error field of os migrate recorded-by --json. detectDriverFromUrl (packages/runtime/src/standalone-stack.ts) now refuses with 'Unsupported database URL scheme "redis://"' and keeps its supported list unchanged. The scheme comes from a new urlSchemeOf helper, which uses the regex the arm already tested and so cannot hold a userinfo, host, path or query. Both siblings were measured to carry a credential and were changed the same way: the retired in-memory engine refusal (its explicit, env and declared-datasource phrases) and the sqlite non-path refusal (OS_DATABASE_DRIVER=sqlite beside a credentialed postgres:// URL). After the change, every door counts 0 for every placeholder. Every detection arm returns the same kind as before, and the URL handed to the driver keeps its credentials. Changeset: @objectstack/runtime patch, with FROM/TO.",
"tests": "HEAD c61b136 (git rev-parse --short HEAD), all runs under os-verify-lock with OS_VERIFY_LOCK_SLOT=issue-23764. | New: packages/runtime/src/standalone-stack-url-refusal-credentials.test.ts, 30 cases: each refusal through each door (the databaseUrl option, OS_DATABASE_URL, a declared default datasource, databaseDriver and OS_DATABASE_DRIVER); a plain Error with no cause; names the scheme and the remedy; no own property of the error (message, stack) carries PLACEHOLDER-USER, PLACEHOLDER-SECRET or PLACEHOLDER-TOKEN. CONTROL: 16 detection-arm rows on the same kind, and 3 rows where a credentialed postgres/mysql/mongodb URL resolves with url byte-identical. standalone-stack.test.ts: the memory:// boot pin now expects the scheme and no URL tail, plus one new createStandaloneStack row over a credentialed redis:// URL. | pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/standalone-stack-url-refusal-credentials.test.ts src/standalone-stack.test.ts src/standalone-stack.libsql.test.ts src/standalone-stack.mysql.test.ts src/resolve-project-database.test.ts: Test Files 5 passed (5), Tests 155 passed (155), VERDICT command-exit 0. The first run was 10 red on a test-helper bug: a JSON-escaping positive control, fixed in 1d02ffc, not a product failure. | pnpm --filter @objectstack/runtime test (project local): Test Files 399 passed (399), Tests 5979 passed | 19 skipped (5998), VERDICT command-exit 0. | pnpm --filter @objectstack/runtime typecheck: exit 0. check:test-typecheck OK with 27 files / 190 errors held in the ledger, so the new test file adds no debt. tsc --listFiles -p tsconfig.test.json counts both changed test files (2). | Ablation, via node scripts/ablation-replace.mjs in wrap mode plus a shell trap restoring from HEAD with a blob-hash check. The subject is imported from src by relative path, so there is no dist leg. (1) Raw URL put back into the unsupported-scheme refusal, scheme wording kept: anchor 1 to 0, blob 787073f6c5 to cb57a854f7; 5 red (the 4 unsupported-scheme rows and the boot row, all on the placeholder assertion), 116 green (every memory, sqlite and CONTROL row, and the libsql and mysql suites); restored with blob equal to HEAD and git diff HEAD empty. (2) Env memory phrase: 1 red on the placeholder assertion, 63 green. The first attempt inserted the URL inside the asserted wording and went red on the wording assertion, so it was void and was redone. (3) sqlite non-path refusal: 2 red on the placeholder assertion, 62 green. Every leg was restored and verified. | Door readings with the built CLI and runtime and a plain defineStack fixture: before (standalone-stack.ts at base, runtime rebuilt) each door printed 1/1/1 of USER/SECRET/TOKEN; after, 0/0/0 on all five; exit 1 throughout. The runtime dist was rebuilt from HEAD afterwards (dist marker fixed=1, raw=0).",
"gates": "Local, at c61b136: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 62 commands over 4 paths. All 62 were run, each exit captured before any pipe: 61 exit 0 on the first pass. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: 9 packages without dist) and exited 0 on a re-run once those dist trees existed (110 entries / 68 packages / 722 CJS files). | --ran reconciliation (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE, with every line in the command :: exit N form): 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero. | Lint, narrowed: eslint --no-inline-config over the 3 changed .ts files, exit 0. Evidence: (1) they are the derived change set's lintable files; (2) --print-config shows 6 per-file syntactic rules and no typed-lint parserOptions.project; (3) positive control: an injected max-len 40 fires on all three (101/333/700). Full pnpm lint is left to CI's Lint and Repo Gates. | Not run locally: test:repo (no repo-project test changed, declared to CI); packages/cli suites (not touched); local-only check:dev-prereqs and check:skill-frame-freshness (no packages/spec and no frame files touched). | PR #23791 check-runs on c61b136, read once at report time: 23 runs. No other open PR may claim the same issue: success. Packed-tarball smoke (opt-in): skipped. In progress, not yet concluded (honest value, not awaited): Type Check · source gates, Type Check · consumer gates, Type Check · workspace, Type Check · debt ledger, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Lint · scoped · query-options erasure, Lint · CI, release & engine gates, Lint · repo & tooling gates, Flag docs affected by code changes, The card this PR closes must claim this branch, filter, Check PR Size, Governed Surface Queue Guard, No other open PR may claim the same single-writer path, Check Changeset, Part-of PR must not also close its card, Check Documentation Links. Test Core and Build Core had not yet appeared.",
"mcp_calls": "0: no MCP GitHub tool was called (reads used gh api REST GETs; writes went through scripts/pm/with-fleet.sh --via dispatch)",
"api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, each executed by fleet-write.yml as objectstack-fleet[bot]): (1) pr_create, which sent POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/23791/assignees [sunhaolin], run 38841652804, success; read-back 11469 bytes sent and stored, identical; (2) comment, POST /repos//issues/23764/comments (this os-dev-report). Plus git push of the branch (not a REST write). label-write: 0 (none named by the dispatch, and skip-changeset does not apply). No claim post and no card-assignee write.",
"open_questions": [
{
"question": "Mechanism (b) found a drivable door: packages/cli/test/migrate-exit-code.e2e.test.ts already spawns os migrate recorded-by --json over wat://nope. The pin list asks for a door-level row, but the ruling forbids editing packages/cli. Should a credential-absence door row be added in the CLI lane?",
"options": [
"A: No door row. The unit pins sit at the one throw every door reaches (measured: all 5 doors print that message and nothing else carrying the URL), the boot row covers createStandaloneStack, and the existing e2e keeps reading "Unsupported database URL scheme" from the --json payload.",
"B: A CLI-lane follow-up adds a placeholder-credential row to migrate-exit-code.e2e.test.ts (about 4 to 9 s per spawn) that asserts the payload carries no placeholder."
],
"recommendation": "A, because the door adds no second copy of the URL (measured 1/1/1 before, 0/0/0 after, no cause, no stack, no file), so a door pin would re-assert the unit pin through a slower path. Choose B only if the seat wants the leak class pinned end-to-end."
}
],
"out_of_scope_findings": [
"class: a · reach: exception: security, plus the public door measured with the built CLI at c61b136: OS_MODE=off OS_DATABASE_URL=memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN os serve exits 1, printing the whole URL in "OS_DATABASE_URL "memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@..." selects the in-memory (mingo) engine", each placeholder once · evidence: packages/cli/src/utils/storage-driver.ts:279 on ec44cf6, the inferDriverTypeFromUrl memory arm, interpolates the raw value u into retiredMemoryEngineMessage. PR #23761 rewrites this function and leaves this arm unchanged (its new unknown-scheme arm names the scheme only). This is the CLI-host sibling of this card, in packages/cli, which this card does not edit · dedupe words: CLI memory scheme refusal prints OS_DATABASE_URL password; inferDriverTypeFromUrl memory:// full URL in error; os serve OS_MODE=off memory URL credential",
"class: a · reach: public door measured with the built CLI at c61b136: OS_DATABASE_URL=FILE:./urd.db os serve in a plain project created a directory literally named "FILE:." in the working directory, holding a 708 KB SQLite file urd.db; lower-case file:./urd.db opens ./urd.db. The boot then stopped on an unrelated production prerequisite (OS_SECRET_KEY) · evidence: packages/runtime/src/standalone-stack.ts, where detectDriverFromUrl matches file: case-insensitively but sqliteFilenameFromUrl strips only a lower-case file: prefix; API reading: resolveStandaloneDatabase({ databaseUrl: "FILE:/tmp/x.db" }).sqliteFile is "FILE:/tmp/x.db". This is a different defect class from this card, so it was not fixed here · dedupe words: upper-case FILE: database URL; sqliteFilenameFromUrl case-sensitive file prefix; FILE: directory created in working directory",
"carrier: none · noted, not filed: unsupportedDriverMessage (standalone-stack.ts) echoes the raw OS_DATABASE_DRIVER value. That is a driver spelling by contract and carries a URL only if an operator writes one into the driver variable. It is listed in the PR's sibling table, not changed",
"carrier: none · noted, not filed: the resolve-project-database.ts docstring of retiredMemoryEngineMessage still shows The database URL "memory://" as an example selection phrase. It is a comment outside the card's file surface and is in the PR's Acceptance notes"
],
"deviations": [
"The pins spell the username PLACEHOLDER-USER, not the dispatch's literal user:PLACEHOLDER-SECRET@, so the username-absence assertion cannot collide with an ordinary word in a message.",
"No door-level pin: its home is packages/cli, which the ruling holds off. The doors were measured once, before and after, instead (open_questions[0]).",
"Lint: the dispatch asks for pnpm lint or the proven narrowing, and the role file assigns repo-wide scans to CI. The narrowing, with its three pieces of evidence, satisfies both. Stated here rather than chosen silently.",
"The memory phrases are reworded ("A database URL with the "memory://" scheme", "An OS_DATABASE_URL with the …", "URL scheme "memory://""), not only substituted, so they do not claim the URL was exactly the scheme. The sqlite refusal moved from ": "URL"" to "(its scheme is …)" / "(it names no file after "file:")". The existing memory:// boot pin was updated from the whole URL to the scheme.",
"The first memory ablation attempt was void (it tripped the wording assertion) and was redone.",
"check:dual-build-cjs-loads: first pass exit 3 (prerequisite), then re-run exit 0. Recorded as exit 0 in --ran, with both runs reported.",
"Commit trailers use the model-free AGENTS.md pair (Claude-Session plus Co-authored-by: Claude). The harness reminder's model-named Co-Authored-By was not used, per AGENTS.md's no-model-identifier rule."
],
"files_changed": [
"packages/runtime/src/standalone-stack.ts",
"packages/runtime/src/standalone-stack-url-refusal-credentials.test.ts (new)",
"packages/runtime/src/standalone-stack.test.ts",
".changeset/23764-unsupported-scheme-no-url.md (new; @objectstack/runtime patch, Clause-②: no)"
],
"reading_a": "Hypothesis (a) holds, and there is no second copy. Before the change, each of the 5 doors printed each placeholder exactly once: the refusal line. os dev and os start's Database: row already redacts (redis://127.0.0.1:6399/0). No door printed a stack, the error carries no cause, and no file written under OS_HOME or the project held a placeholder. After the change, every door counts 0/0/0. The pins also serialise every own property of the thrown error.",
"reading_b": "Hypothesis (b) holds and is wider than stated. Five public doors reach the throw through the standalone host: os dev --no-watch, os start, os serve (each over a plain defineStack config), os migrate plan, and os migrate recorded-by --json (in the stdout JSON error field). The doors are drivable (packages/cli/test/migrate-exit-code.e2e.test.ts already spawns the last one), but a door pin would live in packages/cli, which is held off. See open_questions[0].",
"sibling_messages": [
"detectDriverFromUrl unsupported-scheme refusal → yes (any scheme://user:password@…?token=…) → fixed (the card)",
"refuseRetiredMemoryEngine explicit/env/declared-datasource phrases (:540-543) → yes (memory:// and mingo:// accept anything after //; measured echo of all three placeholders via databaseUrl and OS_DATABASE_URL, and via a declared datasource whose url is memory://…) → fixed",
"sqliteFilenameFromUrl non-path refusal (:557) → yes (OS_DATABASE_DRIVER=sqlite or databaseDriver: sqlite beside a credentialed postgres:// URL echoed all three placeholders) → fixed",
"unsupportedDriverMessage (:178, OS_DATABASE_DRIVER value) → not a URL by contract; carries one only if an operator puts a URL in the driver variable → listed, not changed",
"assertUrlNamedForRemoteDriver (:505) → no (driver kind only) → n/a",
"legacy-file notice console.warn (:707) → no (state-directory file path) → n/a",
"unreachable-kind throw (:801) → no (kind only) → n/a"
],
"before_after": [
"redis://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN → before: "[StandaloneStack] Unsupported database URL scheme: redis://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN. Supported schemes: …" → after: "[StandaloneStack] Unsupported database URL scheme "redis://". Supported schemes: …"",
"https://PLACEHOLDER-USER:PLACEHOLDER-SECRET@… (non-Turso) → before: the whole URL → after: Unsupported database URL scheme "https://"",
"memory://PLACEHOLDER-USER:… (databaseUrl) → before: The database URL "memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@…" → after: A database URL with the "memory://" scheme (mingo:// likewise)",
"memory://PLACEHOLDER-USER:… (OS_DATABASE_URL) → before: OS_DATABASE_URL "memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@…" → after: An OS_DATABASE_URL with the "memory://" scheme",
"declared default datasource url memory://… → before: (driver: memory, URL "memory://…") → after: (driver: memory, URL scheme "memory://")",
"postgres://PLACEHOLDER-USER:… with OS_DATABASE_DRIVER=sqlite → before: sqlite driver was selected but the URL does not look like a file path: "postgres://PLACEHOLDER-USER:PLACEHOLDER-SECRET@…" → after: … does not look like a file path (its scheme is "postgres://")",
"file: → before: … does not look like a file path: "file:" → after: … does not look like a file path (it names no file after "file:")"
]
}
domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU · holder of claim 6206304815, released by this landing. Review: ACCEPT 6207095188 on the PR (no contract review owed).
Reading
Value
PR
merged: true, merged_at 2026-10-16T03:36Z, through the merge queue (added_to_merge_queue 2026-10-16T03:07Z)
Queue branch gh-readonly-queue/main/pr-23791-*
absent (git ls-remote, 0 refs)
origin/main
9d4fb3c306 is an ancestor; one parent (609d07a7df); 4 files, +285 / −8, as reviewed at c61b1366e1
Closing keywords in the landed commit
Fixes #23764 only
Lane reconciliation
15 open domain:cli cards (read at this stamp), #23764 no longer among them
State change: #23764 closed completed by the merge; pm:dispatched removed in this act. The standalone host's database-URL refusals (an unsupported scheme, the retired in-memory engine's three phrases, and the sqlite non-path refusal) now name the URL's scheme and never print the URL. So a password or token in OS_DATABASE_URL no longer reaches the console or the boot log at os dev, os start, os serve or os migrate. Which URLs resolve, and the URL handed to the driver, are unchanged.
Filed from this card's report:#23792 (the CLI host's own memory:// refusal still prints the whole URL; the CLI-host sibling of this card) and #23794 (an upper-case FILE: URL creates a FILE:. directory).
Activity
objectstack-fleet commentedon Oct 16, 2026
First grade ·
domain:cli· p1 ·security·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T01:03ZRead in this act: the body in full, 0 comments.
domain:cli(packages/runtime/src/standalone-stack.ts#detectDriverFromUrl).security.redactConnectionUrl, as the CLI host does after PR fix(cli): os serve refuses an OS_DATABASE_URL whose scheme no driver recognises, and reads pg:// and a scheme-less path as the runtime host does #23761.pm:queue. It has no order with PR fix(cli): os serve refuses an OS_DATABASE_URL whose scheme no driver recognises, and reads pg:// and a scheme-less path as the runtime host does #23761 (cli/serve --dev: an OS_DATABASE_URL whose scheme the CLI does not recognise is ignored in silence, and the boot serves an in-memory SQLite store instead of the database the operator named #23633): that PR leavespackages/runtimeunchanged, per the body.finding→bug, andsecurityadded.⛔ Not a claim.
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 27 (#23764: the standalone host's unsupported-database-URL-scheme refusal names the scheme, never the raw
OS_DATABASE_URL, so a password in the URL no longer reaches the console or the boot log) · 2026-10-16T01:58ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23764-unsupported-scheme-no-urlWorktree:
objectstack-issue-23764Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)Scope:
batch1, by the maintainer's 「当前任务处理完,合并后就改为串行」. #23050 slice 7 landed at 2026-10-16T01:54Z and was released, so nothing of this seat's is in flight. This is the lane's onlypriority:p1card inpm:queue, ahead of the two p2 cards (#23737, #23050), read at this act.Order, read at this act: triage's grade
6205637664(p1,security) puts no order on it. PR #23761 (#23633, seat 1's) leavespackages/runtimeunchanged, and no open PR touchespackages/runtime/src/.File surface (read on
origin/mainec44cf60bf; stop on a breach and explain it in the report):packages/runtime/src/standalone-stack.ts:detectDriverFromUrl's unsupported-scheme throw (about:433), and any sibling message in the same host that echoes a raw database URL, if the dev measures that it can carry a credential (refuseRetiredMemoryEngine's URL phrases about:540-:543, the sqlite non-path refusal about:557).os dev/os startover the standalone host), if the door can be driven in a test..changeset/23764-*.md(@objectstack/runtimepatch).packages/cliis not edited; PR fix(cli): os serve refuses an OS_DATABASE_URL whose scheme no driver recognises, and reads pg:// and a scheme-less path as the runtime host does #23761 (cli/serve --dev: an OS_DATABASE_URL whose scheme the CLI does not recognise is ignored in silence, and the boot serves an in-memory SQLite store instead of the database the operator named #23633) owns the CLI host's own refusal. The runtime does not importredactConnectionUrlfrom@objectstack/cli(dependency direction). runtime/cli: os start on an OS_DATABASE_URL sqlite file whose directory cannot be made hangs with no timeout: createStandaloneStack's synchronous recursive mkdirSync loops on ENOENT under an existing parent (the #23696 class, a second producer) #23737'smkdirSyncin the same file is not this card's.Container & model:
S(one throw and its pins),mode:subagent,model: default tier.Clause-②: no
Responsibility:
the standalone host's driver resolution (this lane, packages/runtime) | the CLI host's refusal after PR #23761, read as the wording precedent | any deployment that ships boot logs to an aggregator with a credential-bearing OS_DATABASE_URLThread-read: 6205637664
Serial constraints cleared: no open PR touches
packages/runtime/src/(read at this act), and nothing of this seat's is in flight.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23764,
"status": "done",
"branch": "claude/issue-23764-unsupported-scheme-no-url",
"pr": "#23791",
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching PM session, the harness-stamped Claude-Session; this run is mode:subagent)",
"premise_still_valid": true,
"summary": "Premise held, measured on base ec44cf6. With OS_DATABASE_URL=redis://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN, all five public doors printed each placeholder once in the refusal: os dev --no-watch, os start, os serve, os migrate plan, and the stdout error field of os migrate recorded-by --json. detectDriverFromUrl (packages/runtime/src/standalone-stack.ts) now refuses with 'Unsupported database URL scheme "redis://"' and keeps its supported list unchanged. The scheme comes from a new urlSchemeOf helper, which uses the regex the arm already tested and so cannot hold a userinfo, host, path or query. Both siblings were measured to carry a credential and were changed the same way: the retired in-memory engine refusal (its explicit, env and declared-datasource phrases) and the sqlite non-path refusal (OS_DATABASE_DRIVER=sqlite beside a credentialed postgres:// URL). After the change, every door counts 0 for every placeholder. Every detection arm returns the same kind as before, and the URL handed to the driver keeps its credentials. Changeset: @objectstack/runtime patch, with FROM/TO.",
"tests": "HEAD c61b136 (git rev-parse --short HEAD), all runs under os-verify-lock with OS_VERIFY_LOCK_SLOT=issue-23764. | New: packages/runtime/src/standalone-stack-url-refusal-credentials.test.ts, 30 cases: each refusal through each door (the databaseUrl option, OS_DATABASE_URL, a declared default datasource, databaseDriver and OS_DATABASE_DRIVER); a plain Error with no cause; names the scheme and the remedy; no own property of the error (message, stack) carries PLACEHOLDER-USER, PLACEHOLDER-SECRET or PLACEHOLDER-TOKEN. CONTROL: 16 detection-arm rows on the same kind, and 3 rows where a credentialed postgres/mysql/mongodb URL resolves with url byte-identical. standalone-stack.test.ts: the memory:// boot pin now expects the scheme and no URL tail, plus one new createStandaloneStack row over a credentialed redis:// URL. | pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/standalone-stack-url-refusal-credentials.test.ts src/standalone-stack.test.ts src/standalone-stack.libsql.test.ts src/standalone-stack.mysql.test.ts src/resolve-project-database.test.ts: Test Files 5 passed (5), Tests 155 passed (155), VERDICT command-exit 0. The first run was 10 red on a test-helper bug: a JSON-escaping positive control, fixed in 1d02ffc, not a product failure. | pnpm --filter @objectstack/runtime test (project local): Test Files 399 passed (399), Tests 5979 passed | 19 skipped (5998), VERDICT command-exit 0. | pnpm --filter @objectstack/runtime typecheck: exit 0. check:test-typecheck OK with 27 files / 190 errors held in the ledger, so the new test file adds no debt. tsc --listFiles -p tsconfig.test.json counts both changed test files (2). | Ablation, via node scripts/ablation-replace.mjs in wrap mode plus a shell trap restoring from HEAD with a blob-hash check. The subject is imported from src by relative path, so there is no dist leg. (1) Raw URL put back into the unsupported-scheme refusal, scheme wording kept: anchor 1 to 0, blob 787073f6c5 to cb57a854f7; 5 red (the 4 unsupported-scheme rows and the boot row, all on the placeholder assertion), 116 green (every memory, sqlite and CONTROL row, and the libsql and mysql suites); restored with blob equal to HEAD and git diff HEAD empty. (2) Env memory phrase: 1 red on the placeholder assertion, 63 green. The first attempt inserted the URL inside the asserted wording and went red on the wording assertion, so it was void and was redone. (3) sqlite non-path refusal: 2 red on the placeholder assertion, 62 green. Every leg was restored and verified. | Door readings with the built CLI and runtime and a plain defineStack fixture: before (standalone-stack.ts at base, runtime rebuilt) each door printed 1/1/1 of USER/SECRET/TOKEN; after, 0/0/0 on all five; exit 1 throughout. The runtime dist was rebuilt from HEAD afterwards (dist marker fixed=1, raw=0).",
"gates": "Local, at c61b136: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) derived 62 commands over 4 paths. All 62 were run, each exit captured before any pipe: 61 exit 0 on the first pass. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: 9 packages without dist) and exited 0 on a re-run once those dist trees existed (110 entries / 68 packages / 722 CJS files). | --ran reconciliation (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE, with every line in the command :: exit N form): 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero. | Lint, narrowed: eslint --no-inline-config over the 3 changed .ts files, exit 0. Evidence: (1) they are the derived change set's lintable files; (2) --print-config shows 6 per-file syntactic rules and no typed-lint parserOptions.project; (3) positive control: an injected max-len 40 fires on all three (101/333/700). Full pnpm lint is left to CI's Lint and Repo Gates. | Not run locally: test:repo (no repo-project test changed, declared to CI); packages/cli suites (not touched); local-only check:dev-prereqs and check:skill-frame-freshness (no packages/spec and no frame files touched). | PR #23791 check-runs on c61b136, read once at report time: 23 runs. No other open PR may claim the same issue: success. Packed-tarball smoke (opt-in): skipped. In progress, not yet concluded (honest value, not awaited): Type Check · source gates, Type Check · consumer gates, Type Check · workspace, Type Check · debt ledger, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Lint · scoped · query-options erasure, Lint · CI, release & engine gates, Lint · repo & tooling gates, Flag docs affected by code changes, The card this PR closes must claim this branch, filter, Check PR Size, Governed Surface Queue Guard, No other open PR may claim the same single-writer path, Check Changeset, Part-of PR must not also close its card, Check Documentation Links. Test Core and Build Core had not yet appeared.",
"mcp_calls": "0: no MCP GitHub tool was called (reads used gh api REST GETs; writes went through scripts/pm/with-fleet.sh --via dispatch)",
"api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, each executed by fleet-write.yml as objectstack-fleet[bot]): (1) pr_create, which sent POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/23791/assignees [sunhaolin], run 38841652804, success; read-back 11469 bytes sent and stored, identical; (2) comment, POST /repos//issues/23764/comments (this os-dev-report). Plus git push of the branch (not a REST write). label-write: 0 (none named by the dispatch, and skip-changeset does not apply). No claim post and no card-assignee write.",
"open_questions": [
{
"question": "Mechanism (b) found a drivable door: packages/cli/test/migrate-exit-code.e2e.test.ts already spawns os migrate recorded-by --json over wat://nope. The pin list asks for a door-level row, but the ruling forbids editing packages/cli. Should a credential-absence door row be added in the CLI lane?",
"options": [
"A: No door row. The unit pins sit at the one throw every door reaches (measured: all 5 doors print that message and nothing else carrying the URL), the boot row covers createStandaloneStack, and the existing e2e keeps reading "Unsupported database URL scheme" from the --json payload.",
"B: A CLI-lane follow-up adds a placeholder-credential row to migrate-exit-code.e2e.test.ts (about 4 to 9 s per spawn) that asserts the payload carries no placeholder."
],
"recommendation": "A, because the door adds no second copy of the URL (measured 1/1/1 before, 0/0/0 after, no cause, no stack, no file), so a door pin would re-assert the unit pin through a slower path. Choose B only if the seat wants the leak class pinned end-to-end."
}
],
"out_of_scope_findings": [
"class: a · reach: exception: security, plus the public door measured with the built CLI at c61b136: OS_MODE=off OS_DATABASE_URL=memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN os serve exits 1, printing the whole URL in "OS_DATABASE_URL "memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@..." selects the in-memory (mingo) engine", each placeholder once · evidence: packages/cli/src/utils/storage-driver.ts:279 on ec44cf6, the inferDriverTypeFromUrl memory arm, interpolates the raw value u into retiredMemoryEngineMessage. PR #23761 rewrites this function and leaves this arm unchanged (its new unknown-scheme arm names the scheme only). This is the CLI-host sibling of this card, in packages/cli, which this card does not edit · dedupe words: CLI memory scheme refusal prints OS_DATABASE_URL password; inferDriverTypeFromUrl memory:// full URL in error; os serve OS_MODE=off memory URL credential",
"class: a · reach: public door measured with the built CLI at c61b136: OS_DATABASE_URL=FILE:./urd.db os serve in a plain project created a directory literally named "FILE:." in the working directory, holding a 708 KB SQLite file urd.db; lower-case file:./urd.db opens ./urd.db. The boot then stopped on an unrelated production prerequisite (OS_SECRET_KEY) · evidence: packages/runtime/src/standalone-stack.ts, where detectDriverFromUrl matches file: case-insensitively but sqliteFilenameFromUrl strips only a lower-case file: prefix; API reading: resolveStandaloneDatabase({ databaseUrl: "FILE:/tmp/x.db" }).sqliteFile is "FILE:/tmp/x.db". This is a different defect class from this card, so it was not fixed here · dedupe words: upper-case FILE: database URL; sqliteFilenameFromUrl case-sensitive file prefix; FILE: directory created in working directory",
"carrier: none · noted, not filed: unsupportedDriverMessage (standalone-stack.ts) echoes the raw OS_DATABASE_DRIVER value. That is a driver spelling by contract and carries a URL only if an operator writes one into the driver variable. It is listed in the PR's sibling table, not changed",
"carrier: none · noted, not filed: the resolve-project-database.ts docstring of retiredMemoryEngineMessage still shows The database URL "memory://" as an example selection phrase. It is a comment outside the card's file surface and is in the PR's Acceptance notes"
],
"deviations": [
"The pins spell the username PLACEHOLDER-USER, not the dispatch's literal user:PLACEHOLDER-SECRET@, so the username-absence assertion cannot collide with an ordinary word in a message.",
"No door-level pin: its home is packages/cli, which the ruling holds off. The doors were measured once, before and after, instead (open_questions[0]).",
"Lint: the dispatch asks for pnpm lint or the proven narrowing, and the role file assigns repo-wide scans to CI. The narrowing, with its three pieces of evidence, satisfies both. Stated here rather than chosen silently.",
"The memory phrases are reworded ("A database URL with the "memory://" scheme", "An OS_DATABASE_URL with the …", "URL scheme "memory://""), not only substituted, so they do not claim the URL was exactly the scheme. The sqlite refusal moved from ": "URL"" to "(its scheme is …)" / "(it names no file after "file:")". The existing memory:// boot pin was updated from the whole URL to the scheme.",
"The first memory ablation attempt was void (it tripped the wording assertion) and was redone.",
"check:dual-build-cjs-loads: first pass exit 3 (prerequisite), then re-run exit 0. Recorded as exit 0 in --ran, with both runs reported.",
"Commit trailers use the model-free AGENTS.md pair (Claude-Session plus Co-authored-by: Claude). The harness reminder's model-named Co-Authored-By was not used, per AGENTS.md's no-model-identifier rule."
],
"files_changed": [
"packages/runtime/src/standalone-stack.ts",
"packages/runtime/src/standalone-stack-url-refusal-credentials.test.ts (new)",
"packages/runtime/src/standalone-stack.test.ts",
".changeset/23764-unsupported-scheme-no-url.md (new; @objectstack/runtime patch, Clause-②: no)"
],
"reading_a": "Hypothesis (a) holds, and there is no second copy. Before the change, each of the 5 doors printed each placeholder exactly once: the refusal line. os dev and os start's Database: row already redacts (redis://127.0.0.1:6399/0). No door printed a stack, the error carries no cause, and no file written under OS_HOME or the project held a placeholder. After the change, every door counts 0/0/0. The pins also serialise every own property of the thrown error.",
"reading_b": "Hypothesis (b) holds and is wider than stated. Five public doors reach the throw through the standalone host: os dev --no-watch, os start, os serve (each over a plain defineStack config), os migrate plan, and os migrate recorded-by --json (in the stdout JSON error field). The doors are drivable (packages/cli/test/migrate-exit-code.e2e.test.ts already spawns the last one), but a door pin would live in packages/cli, which is held off. See open_questions[0].",
"sibling_messages": [
"detectDriverFromUrl unsupported-scheme refusal → yes (any scheme://user:password@…?token=…) → fixed (the card)",
"refuseRetiredMemoryEngine explicit/env/declared-datasource phrases (
:540-543) → yes (memory:// and mingo:// accept anything after //; measured echo of all three placeholders via databaseUrl and OS_DATABASE_URL, and via a declared datasource whose url is memory://…) → fixed",:557) → yes (OS_DATABASE_DRIVER=sqlite or databaseDriver: sqlite beside a credentialed postgres:// URL echoed all three placeholders) → fixed","sqliteFilenameFromUrl non-path refusal (
"unsupportedDriverMessage (:178, OS_DATABASE_DRIVER value) → not a URL by contract; carries one only if an operator puts a URL in the driver variable → listed, not changed",
"assertUrlNamedForRemoteDriver (:505) → no (driver kind only) → n/a",
"legacy-file notice console.warn (:707) → no (state-directory file path) → n/a",
"unreachable-kind throw (:801) → no (kind only) → n/a"
],
"before_after": [
"redis://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN → before: "[StandaloneStack] Unsupported database URL scheme: redis://PLACEHOLDER-USER:PLACEHOLDER-SECRET@127.0.0.1:6399/0?authToken=PLACEHOLDER-TOKEN. Supported schemes: …" → after: "[StandaloneStack] Unsupported database URL scheme "redis://". Supported schemes: …"",
"https://PLACEHOLDER-USER:PLACEHOLDER-SECRET@… (non-Turso) → before: the whole URL → after: Unsupported database URL scheme "https://"",
"memory://PLACEHOLDER-USER:… (databaseUrl) → before: The database URL "memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@…" → after: A database URL with the "memory://" scheme (mingo:// likewise)",
"memory://PLACEHOLDER-USER:… (OS_DATABASE_URL) → before: OS_DATABASE_URL "memory://PLACEHOLDER-USER:PLACEHOLDER-SECRET@…" → after: An OS_DATABASE_URL with the "memory://" scheme",
"declared default datasource url memory://… → before: (driver: memory, URL "memory://…") → after: (driver: memory, URL scheme "memory://")",
"postgres://PLACEHOLDER-USER:… with OS_DATABASE_DRIVER=sqlite → before: sqlite driver was selected but the URL does not look like a file path: "postgres://PLACEHOLDER-USER:PLACEHOLDER-SECRET@…" → after: … does not look like a file path (its scheme is "postgres://")",
"file: → before: … does not look like a file path: "file:" → after: … does not look like a file path (it names no file after "file:")"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23791 comment
6207095188(no contract review owed) ·domain:cliseat 2 · 2026-10-16T03:04ZGenerated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed: PR #23791 →
9d4fb3c306· 2026-10-16T03:37Zdomain:cliseat 2 (#22648) ·sunhaolin· sessionsession_01Q9PZ5Pzqcus4woVvTZwWNU· holder of claim6206304815, released by this landing. Review: ACCEPT6207095188on the PR (no contract review owed).merged: true,merged_at2026-10-16T03:36Z, through the merge queue (added_to_merge_queue2026-10-16T03:07Z)gh-readonly-queue/main/pr-23791-*git ls-remote, 0 refs)origin/main9d4fb3c306is an ancestor; one parent (609d07a7df); 4 files, +285 / −8, as reviewed atc61b1366e1Fixes #23764onlydomain:clicards (read at this stamp), #23764 no longer among themState change: #23764 closed
completedby the merge;pm:dispatchedremoved in this act. The standalone host's database-URL refusals (an unsupported scheme, the retired in-memory engine's three phrases, and the sqlite non-path refusal) now name the URL's scheme and never print the URL. So a password or token inOS_DATABASE_URLno longer reaches the console or the boot log atos dev,os start,os serveoros migrate. Which URLs resolve, and the URL handed to the driver, are unchanged.Filed from this card's report: #23792 (the CLI host's own
memory://refusal still prints the whole URL; the CLI-host sibling of this card) and #23794 (an upper-caseFILE:URL creates aFILE:.directory).Generated by Claude Code