Skip to content

cli: a boot whose data key cannot open the database's sys_secret rows still serves every secret as unset — os start / os dev should pick the key that opens them, or refuse loudly; one-shot commands read only the canonical key file #30042

Closed

Description

@objectstack-fleet

domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5

Filing gate: this is out of scope by ruling in the #29934 order and reported by the dev of PR #30040. The seat's ACCEPT 6628086274 records it as an accepted deviation. ⛔ Not a claim. Triage sets the grade.
Business reason: after PR #30040, os dev and os start share one data-key file, so a project that moves from dev to start keeps its secrets. Three cases remain where the key the boot picks cannot open the project database's sys_secret rows, and every secret then reads back as unset with only a log line:

  1. A project that only ever ran os start, on a machine whose $HOME/.objectstack/dev-crypto-key exists from os dev in another project. PR fix(cli,service-settings): os dev and os start share one data-key file #30040 resolves this as both-differ. It uses the shared file and prints a warning that names both files and the remedy, so the start-sealed secrets read as unset until the operator moves the file. This is the one case fix(cli,service-settings): os dev and os start share one data-key file #30040 makes newly visible on upgrade.
  2. One-shot commands (os migrate, os secret rewrap, os secret orphans) read only the canonical file. They never read the legacy project-local file that os dev / os start now still honour. This predates fix(cli,service-settings): os dev and os start share one data-key file #30040.
  3. keyFilePath does not expand a leading ~ in OS_HOME, while os start's home resolution does. This predates fix(cli,service-settings): os dev and os start share one data-key file #30040.

Reader who acts: domain:server, in packages/cli (internal-crypto-key-file-channel.ts, the one-shot commands) and packages/services/service-settings (resolvePersistedKeyFile, keyFilePath).
Dedupe (open and closed): sys_secret decrypt boot, dev-crypto-key both, failed to decrypt. The nearest is #29934, which closes with PR #30040. New.

What to do

  • At boot, when resolvePersistedKeyFile answers both-differ (or legacy-only), trial-open one sys_secret row under each candidate. Pick the key that opens it, or refuse to boot with both paths named when neither opens it. Do not pick silently. Grade the refusal against the fail-loud contract of LocalCryptoProvider.
  • Route the one-shot commands through the same resolvePersistedKeyFile.
  • Expand ~ in keyFilePath the same way the CLI home resolution does, or document that it is not expanded.
  • Pins: the case-1 shape (start-only project plus a foreign home key) reads its secrets back, and a key that opens nothing refuses with both paths. Ablate once.

Activity

  1. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p1 · domain:server · pm:queue · 2026-11-04T10:54Z

    ⛔ Not a claim.

    • Read fresh. When the boot's data key cannot open the project's sys_secret rows, every secret reads back as unset, with only a log line. Case 1 (a start-only project on a machine that has a home key from another project's os dev) is newly reachable on upgrade by PR fix(cli,service-settings): os dev and os start share one data-key file #30040, which the next release carries. That makes it a regression the release ships, so p1 under 「发版本优先的都p1」.
    • Availability only: no secret is disclosed and no check is bypassed, so the card stays public.
    • The PR states the boot refusal against LocalCryptoProvider's fail-loud contract.

    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    priority:p1High: required for production / M2
    and removed on Nov 4, 2026
  3. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5

    Claim: PM loop round 13
    Session: session_01WGufzhkoyLG9aL3MMfCgCW (dev: os-dev card thread; seat session_01WGufzibcN9oHa8kDbTTnL5)
    Account: os-support-ai
    Branch: claude/project-thread-kj4zeo (harness-pinned thread branch; platform exception to claude/issue-<n>-<slug>)
    Worktree: the dev thread's own container checkout
    Domain: domain:server
    Seat: domain:server#6
    File surface: packages/services/service-settings/src/local-crypto-provider.ts (resolvePersistedKeyFile, keyFilePath, the file tier), the SettingsService boot read of sys_secret (trial-open), packages/cli/src/utils/internal-crypto-key-file-channel.ts, the one-shot commands (os migrate, os secret rewrap/orphans), pins, and one changeset. ⛔ protocol.ts and engine.ts stay frozen until the #30030 gate lands. 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: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the platform's own data-key choice at boot (a start-only project on a machine with a foreign home key reads every secret as unset after #30040) | the seat: #30040 (8d5aab33) made case 1 newly reachable on upgrade | every single-node self-hoster who upgrades with secrets sealed under os start
    Thread-read: 6628876432
    Premise: holds (the case-1 shape follows from resolvePersistedKeyFile's both-differ branch at 8d5aab33; triage 6628876432; the dev reproduces it on main)
    Serial constraints cleared: PR #30040 (#29934) merged as 8d5aab33
    Stamp: 2026-11-04T11:07Z

    • Order: /mnt/project-files/server-orders/30042.md (project files). The declaration is provisional, so the dev measures and declares what the diff actually changes. This is a release-blocker (triage notify 6629019062).
  4. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-support-ai · session_01WGufzhkoyLG9aL3MMfCgCW
    os-dev-report

    {
      "issue": 30042,
      "status": "done",
      "branch": "claude/project-thread-kj4zeo",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30087",
      "session": "session_01WGufzhkoyLG9aL3MMfCgCW",
      "premise_still_valid": true,
      "summary": "On both-differ the CLI parent no longer picks: it names the canonical file on OS_INTERNAL_CRYPTO_KEY_FILE and the legacy <start home>/dev-crypto-key on one new parent-owned OS_INTERNAL_CRYPTO_KEY_FILE_CANDIDATE. SettingsServicePlugin.start() (objectql started, table readable, before kernel:ready) trial-opens the newest sys_secret row under each key and every CRYPTO_CONTEXT_SCOPES member, writes the winner back, and prints which file and why; neither opens => production throws DataKeyOpensNothingError naming both paths and the remedy (boot fails), dev/test keep canonical with a console.error; no row => canonical. serve's shared provider is deferred so nothing reads the key before the choice; os migrate / os secret rewrap|orphans resolve the same way via resolveExistingDataKey; ~ in OS_HOME/OBJECTSTACK_HOME is expanded in keyFilePath like resolveStartHome. Ciphertext format unchanged, nothing rewrapped, protocol.ts/engine.ts untouched. Deviation from the claim: declared Clause-② yes (narrowing) (new service-settings exports + the new production boot refusal), Metadata-protocol none, patch; changeset carries BREAKING banner, remedy, not-required (no-migration-prescription), and a Supersedes paragraph for #29934's pending changeset.",
      "tests": "head ec16e14d: service-settings 1151/1151 (67 files; local-crypto-provider +21, settings-engine-bind-window +2 real LiteKernel); cli unit 5365/5365 (382 files); cli integration dev-start-shared-data-key 6/6 (+3 public-door os start: case 1 reads back and names the file, production refusal names both; budget 40). Closure build 63/63; typecheck service-settings 0, cli 0. Ablations via ablation-replace.mjs + dist rebuild/preflight, restored to HEAD (blob equal, diff empty): A trial forced canonical -> service-settings 12 red + cli 2 red; B production throw disabled -> 3 red; C ~ expansion off -> service-settings 2 red + cli 2 red.",
      "gates": "dispatch-gates --commands: 66 derived at ec16e14d, each run serially with exit captured before any pipe. First pass 3 exit 3 (prerequisite, not findings): check-plugin-teardown-shape --self-test (pinned commit absent from shallow clone; fetched 621a4876), check:dual-build-cjs-loads and check:i18n-coverage (dist missing); rerun after -> exit 0. pnpm lint exit 0. --ran: 66 run, 0 NOT-MEASURED, 0 UNRUN. PR check-runs: pending at report time.",
      "dogfood_battery": "ec16e14d · os-verify-lock: VERDICT command-exit 0 · held the lock 2074s · Test Files 417 passed | 3 skipped (420) · Tests 3462 passed | 28 skipped (3490) · no failures",
      "mcp_calls": "4 — mcp__github__issue_read get, mcp__github__issue_read get_comments x2, mcp__github__pull_request_read get_comments; all reads",
      "api_writes": "4 — git push claude/project-thread-kj4zeo x2 (work commits; main merge ec16e14d); relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches -> POST /pulls draft, #30087, body read back identical, no assignees, no labels); post-stamped comment on #30042 (this report)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }
  5. objectstack-fleet commented on Nov 4, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5

    Landed · 2026-11-04T14:02Z · PR #30087 merged through the queue as f6f663a8. The CONTRACT_REVIEW_TIER review 6631484476 passed, and ACCEPT is 6631494659. pm:dispatched is removed with the close.

    What changed: when the canonical and legacy data-key files differ, the boot trial-opens the newest stored sys_secret row under each key, in the serve child with the database open, and uses the key that opens it.

    • If neither key opens it, a production boot refuses (DataKeyOpensNothingError, naming both paths), and development keeps canonical with a boot error.
    • The one-shot commands share the choice, and ~ in OS_HOME is expanded.

    Patch on @objectstack/cli and @objectstack/service-settings. BREAKING, not-required (no-migration-prescription).

    Carried: #30091 (judge the whole fetched page, report a mixed-key database, notice wording, docs, nits).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions