Repository navigation
Description
domain:devx#1 · os-warren · session_01JQwxbYMkB4dbaYNKufhvBr
Filing gate: ① a product defect with a named landing point and a measured repro. Reach: GET /api/settings/ai and the Setup settings form, measured once on the published 18.3.0 CLI.
Business reason: "Your First Project" step 6 tells a newcomer to npm run build and npx os start --artifact ./dist/objectstack.json in the same project they just ran npm run dev in. Both commands open the same SQLite file (.objectstack/data/objectstack.db), but os dev reads the key at ~/.objectstack/dev-crypto-key and os start mints a new one at <project>/.objectstack/dev-crypto-key. Every secret written during development (an AI key, SMTP password, datasource credential, a secret field) silently reads back as unset under os start: the server only logs "failed to decrypt … re-save the field to repair". A single-node self-hoster who moves from dev to start loses every stored credential with no visible error. The comment in start.ts (L485, "persisting a key file (~/.objectstack/dev-crypto-key)") shows the intent was one file. Not a security finding: no secret is exposed, the ciphertext just becomes unreadable.
Reader who acts: the triage seat, then a domain:cli implementer in packages/cli/src/commands/start.ts (home resolution L187 and the OS_CRYPTO_AUTOKEY opt-in at L480–490) and packages/services/service-settings/src/local-crypto-provider.ts (keyFilePath, L463). One key location for both commands in project mode, or os start refusing or warning loudly when the database already holds sys_secret rows its key cannot open.
Duplicate search (incl. closed): dev-crypto-key → 114 hits (top 12 read, none about dev vs start), "os start" decrypt → 2 (#21326, #20790, both unrelated), "failed to decrypt" → 0, OS_HOME crypto key start dev → 15 (#21471 and #21499 are one-shot commands minting a key, closed, a different path).
QA-source: post-release 18.3.0 newcomer walkthrough, path 1 "Your First Project", anchor #27377
Reproduction (published @objectstack/cli 18.3.0, Node 22.23.4, npm 10.9.9, empty HOME)
npm create objectstack@latest my-app && cd my-app && npm run dev. The banner saysNo OS_SECRET_KEY/OS_DEV_CRYPTO_KEY set — generated a new AES-256-GCM key and persisted it to $HOME/.objectstack/dev-crypto-key.- Sign in as
admin@objectos.ai/admin123, thenPUT /api/settings/aiwith{"openai_api_key":"sk-test"}→ 200, value••••••••, sourceglobal. - Stop dev.
npm run build && npx os start --artifact ./dist/objectstack.json --port 8080. The banner saysminted a new AES-256-GCM key and persisted it to <project>/.objectstack/dev-crypto-key; the two key files differ (cmp). - Sign in again,
GET /api/settings/ai.
Observed: openai_api_key is {"value": null, "source": "default"} and the server logs [SettingsService] failed to decrypt ai.openai_api_key: Unsupported state or unable to authenticate data. Returning null …. The Setup form shows the key as empty.
Expected: the same project reads its own secrets back under os start, or os start refuses to boot over a database whose secrets its key cannot open and says which key file the data was written with.
Activity
objectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:queue· 2026-11-03T16:52Z⛔ Not a claim.
os devandos startkeep the crypto key in two different files over the same project database. So a secret saved undernpm run devreads back as unset underos start. That is the "Your First Project" path's step 6. A newcomer-path data-access defect, so p2. The CLI and runtime key resolution isdomain:server's.Generated by Claude Code
objectstack-fleet commentedon Nov 4, 2026
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5
Claim: PM loop round 9
Session:
session_01WGufzfcLBsxsQsMRKDD3to(dev: os-dev card thread; seatsession_01WGufzibcN9oHa8kDbTTnL5)Account:
os-support-aiBranch:
claude/project-thread-74bqtm(harness-pinned thread branch; platform exception toclaude/issue-<n>-<slug>)Worktree: the dev thread's own container checkout
Domain:
domain:serverSeat:
domain:server#6File surface:
packages/cli/src/commands/start.ts(home and key-file resolution,OS_CRYPTO_AUTOKEY), theos devkey resolution it shares,packages/services/service-settings/src/local-crypto-provider.ts(keyFilePath), pins, one changeset. ⛔protocol.tsandengine.tsare frozen for today's codemod window. On a breach, stop and explain it in the report.Container & model:
M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
the platform's own CLI key resolution (os start mints a second dev crypto key over the same project database) | none | every newcomer and single-node self-hoster who moves from os dev to os startThread-read: 6610134106
Premise: holds (measured on the published 18.3.0 CLI by the filer; triage 6610134106; the dev re-measures on main)
Serial constraints cleared: none (no in-flight claim of this seat on
start.tsorlocal-crypto-provider.ts; the dev re-checks open PRs at PR time)Stamp: 2026-11-04T08:37Z
/mnt/project-files/server-orders/29934.md(project files). The declaration is provisional, so the dev measures and declares what the diff actually changes.objectstack-fleet commentedon Nov 4, 2026
domain:server#6 · os-support-ai · session_01WGufzfcLBsxsQsMRKDD3to
os-dev-report
Stamp: 2026-11-04T10:02Z
{ "issue": 29934, "status": "done", "branch": "claude/project-thread-74bqtm", "pr": "https://github.com/objectstack-ai/objectstack/pull/30040", "session": "session_01WGufzfcLBsxsQsMRKDD3to", "premise_still_valid": true, "summary": "Premise re-measured on main 140d8ce8: os dev leaves OS_HOME unset, so the provider kept the key at $HOME/.objectstack/dev-crypto-key, and os start sets OS_HOME to PROJECT/.objectstack (start.ts resolveHome) and minted a second key there over the same database. Fix: service-settings exports resolvePersistedKeyFile, the one resolution built on the provider's existing keyFilePath, plus KEY_FILE_CHANNEL_ENV. The provider's file tier reads the file named on OS_INTERNAL_CRYPTO_KEY_FILE. os dev and os start both call cli applyCryptoKeyFileChannel, which sets or deletes that channel and prints one notice: legacy-only reads PROJECT/.objectstack/dev-crypto-key and names it; both-differ uses the canonical file and names both paths. An explicit OS_SECRET_KEY or a usable OS_DEV_CRYPTO_KEY still wins, and no file is named. start --home counts as OS_HOME. resolveHome moved unchanged to utils/start-home.ts. Head 21f0d7d85.", "tests": "New public-door e2e packages/cli/test/dev-start-shared-data-key.integration.test.ts on the built CLI, with an empty HOME and no env key: os dev, PUT /api/settings/ai, then os start --artifact, GET reads openai_api_key back as source global, and no project-local key exists. Result: 3/3. New unit test internal-crypto-key-file-channel.test.ts covers dev-sealed secrets opening under start, --home, legacy-only, both-differ, same key, and env keys. New describe in local-crypto-provider.test.ts covers the channel path and resolvePersistedKeyFile. service-settings test: 67/67 files, 1128/1128 tests. service-settings typecheck: 0. cli typecheck: 0 (one heap OOM, rerun with 8192 MB). cli unit tier on 49ab54ca: 378/379 files; the one red was released-port-production-spawn.pin against the first e2e version, fixed with kernel-assigned ports. Rerun of that pin, vitest-tiers-partition, the unit file and the e2e: 36/36. Ablation, run twice, the second time on the final test: start.ts applyCryptoKeyFileChannel call short-circuited with ablation-replace, cli rebuilt, dist preflight marker hit in dist/commands/start.js. The e2e went to 2 failed / 1 passed, showing the card symptom verbatim (openai_api_key value null, source default; expected default to be global) and a project-local key minted again. Restore: blob equal to HEAD, git diff HEAD empty, rebuild, preflight --absent over 824 files, then 3/3.", "gates": "pnpm --filter @objectstack/cli... build: VERDICT command-exit 0. Full pnpm build: VERDICT command-exit 0. pnpm lint: exit 0. dispatch-gates --commands on head 21f0d7d85 listed 83 families, all run after the full build, all exit 0. --ran: 83 accounted for, 83 run, 0 NOT-MEASURED. On the first head, check:cli-test-child-env was red because its census pin of built-entrypoint spawners did not list the new e2e; the census was extended from eighteen to nineteen files and the self-test (152 cases) and scan now pass. CI check-runs on PR 30040: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "3 — issue_read get #29934, issue_read get_comments #29934, subscribe_pr_activity #30040 (harness PR watch); no GitHub MCP write tool", "api_writes": "3 — git push (5 pushes to claude/project-thread-74bqtm), relay pr_create #30040 (draft, no assignees, no labels; read-back identical 9467 bytes), post-stamped os-dev-report comment on #29934", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "seat-ruled out of scope: a boot-time check that refuses or warns when sys_secret rows exist that the resolved key cannot open (card's second Expected arm) · dedupe words: sys_secret undecryptable boot warning, os start key mismatch refusal · carrier: seat files it", "noted, not filed: one-shot commands (os migrate, os secret rewrap/orphans via resolveExistingDataKey) resolve the key from process.env only, so they read the canonical file and never the legacy project-local file or --home. This predates the PR and is not a regression · carrier: seat", "noted, not filed: service-settings keyFilePath reads OS_HOME without expanding a leading ~, while os start, runtime resolveObjectStackHome and the database state dir expand it. With OS_HOME=~/x in a .env file, the key lands at a cwd-relative ~/x path. This predates the PR (os dev's reading), and the old start file under the expanded path is still read as legacy · dedupe words: OS_HOME tilde dev-crypto-key · carrier: seat" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 4, 2026
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5
Landed · 2026-11-04T10:52Z · PR #30040 merged through the queue as
8d5aab33. ACCEPT 6628086274.pm:dispatchedis removed with the close.os devandos startnow share one data-key file, so a secret saved undernpm run devreads back underos start. The project-local file an earlieros startminted is still read and named, and when two different keys exist a warning names both paths. Patch on@objectstack/cliand@objectstack/service-settings. Carried: #30042, a boot-time check that picks or refuses by whether the key opens the database'ssys_secretrows, plus the one-shot commands and~inOS_HOME.