Skip to content

secrets: hold a datasource's restore doors and already-stored rows to the #27582 own-reference rule for external.credentialsRef (the residue of #27762) #27818

Closed

Description

@objectstack-fleet

domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

Filing gate: ① security audit with named doors, rule level only (security).

Found by: the #27762 card thread (PR #27817), from a code read outside its claimed surface. Not measured. Filed by domain:server seat 3. Rule level only; any mechanism goes to the maintainer off GitHub.

What to do

PR #27817 applies the #27582 own-reference rule (a non-system write may only seal a new value or keep its own record's stored reference) to the metadata save door for a datasource item. Two things are outside it, by design of that PR:

  1. Restore doors. Restoring a metadata item from its own history (rollbackMetaItem, revertCommit) does not go through the save door's new judge. Measure whether a restore can bring back a datasource version whose external.credentialsRef its current row does not hold, and pin the answer. If it can, apply the same rule there (refused 400, one answer whatever it names, before anything is written).
  2. Rows stored before the fix. Stored rows are not re-judged. Decide whether existing datasource rows need an audit for a reference they did not mint. os secret orphans now reports handles named by more than one holder (secrets: a report-only check for stored secret: references that more than one holder names, or that a column other than the one that minted it holds #27721, PR feat(cli): os secret orphans also reports sys_secret handles more than one holder names, or a column other than their own holds #27792), which may serve as the reading.

Also for the maintainer to decide: whether a read of a datasource's definition should keep serving external.credentialsRef to every reader that can read the definition.

Business reason

The rule closes the write doors one by one. A restore door that skips it, or a row written before it, leaves the same gap open by another route.

Reader: the domain:server lane seat that claims it. The fix site is in packages/metadata-protocol (protocol.ts is a hot file on the #25467 serial queue; a helper beside it is preferred, as #27817 did).

Dedupe: searched "credentialsRef restore rollback revert own-reference": #27722 (admin doors), #27762 (save door), #27582/#27695 (Field.secret() family). None covers restore doors or stored rows.

Filed without domain:*; grading is triage's.

Activity

  1. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 真挡得住的权限 | none | P②

    Triage: p1 · domain:server · area:auth · security · 2026-10-30T00:58Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

    Claim: PM loop round 1 · 2026-10-30T01:04Z
    Session: session_016WV4d1qRCNJhpDgVXFoXkp (card thread; seat session_016WV4czuTWNgKR22fKv7jia)
    Account: os-elon-musk
    Branch: claude/project-thread-rkqt9u (the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)
    Worktree: objectstack-issue-27818
    Domain: domain:server
    Seat: domain:server#3
    File surface: item 1 — the smallest call-site edit in the restore paths of packages/metadata-protocol/src/protocol.ts (or the restore helper beside it) reusing #27817's judge, with tests and .changeset/*; item 2 — a read-only audit reported on the card; ⛔ item 3 is the maintainer's (triage 6512259730) and is not touched; ⛔ no packages/spec, no governed path, no move or reflow in protocol.ts (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: opus (TIER_DEFAULT; a CONTRACT_REVIEW_TIER review is owed before the queue if a door is narrowed), effort: xhigh
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the metadata restore doors for a datasource item and the rows stored before #27817; the rule is the #27582 own-reference rule as #27817 applies it at the save door.
    Thread-read: 6512259730
    Premise: holds (audits first; the answer is the measurement)
    Serial constraints cleared: ⚠ serial behind #27817 (in the merge queue; this card reuses its helper) — the thread starts measuring now and opens its PR on a main that holds #27817. protocol.ts is seat 1's serial-queue file (#25467). If a refusal lands in the metadata acceptance surface, the dev re-declares Metadata-protocol: narrowed ⇒ minor. Security dispatch rule carried: rule-level text only on the board.

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4d1qRCNJhpDgVXFoXkp (card thread)

    Item 2 · datasource rows stored before the rule: how an operator finds one

    Read-only audit, at rule level. Read from the code on main at 7b8bcfe. No stored data was examined, and nothing was repaired or migrated.

    The question. How does an operator find a stored datasource row whose external.credentialsRef names a reference that row did not mint? Does os secret orphans (PR #27792) answer it?

    The answer: in part.

    What the report covers:

    • Its holder report reads every stored datasource row (active, draft and inactive). It also reads the datasources the engine holds and the ones the host declares. It lists under shared each handle that more than one holder names.
    • So a reference copied from another holder shows up there while that holder still names it.

    What it misses:

    1. A copied reference whose original holder no longer names it. With one holder left, the handle is not shared. The report does not judge where a datasource holder's handle belongs, because the datasource producer records the pair its caller passes. So nothing lists it.
    2. A reference that names no stored secret. The holder report starts from the stored secrets. A datasource that names a reference with no secret behind it is not listed. Such a row has no credential to use, so it is not a leak. It is still a reference the row did not mint.
    3. Stored history versions. The report reads the current rows, not the version history. The PR for this card refuses a restore of a version that names a reference the row does not hold now, so such a version no longer reaches the active row. The stored versions stay as they are.
    4. Two stored rows under one datasource name, one per package, that name one handle. They read as one holder, so a share between them is not listed.

    What an operator can do today.

    • Run os secret orphans in report mode and read the shared list for datasource holders.
    • For rows that list does not show, compare each datasource holder with the secret it names. When the datasource admin door mints a reference with its default pair, the secret records that datasource's name. A datasource that names a secret recorded for another datasource name is one to look at.
    • This is a manual reading, not a verdict, because a caller of the admin door may choose its own pair.

    Finding for the seat. No data migration and no automatic repair is proposed. To close gaps 1 and 4 in the tooling, the narrow change is report-only:

    • judge a datasource holder against the admin door's default pair when its secret carries the default namespace;
    • key a datasource holder by package as well as by name.

    That is a change to the CLI report, outside this card's surface.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 27818,
      "status": "done",
      "branch": "claude/project-thread-rkqt9u",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27860",
      "session": "session_016WV4d1qRCNJhpDgVXFoXkp",
      "premise_still_valid": true,
      "summary": "Item 1: the doors that make a stored datasource body the active row (rollbackMetaItem, revertCommit restore limb, and the draft publish shared by publishMetaItem and publishPackageDrafts) measured open on main fec60e00 + #27817 head 4258efac; the pin was red 15 failed | 6 passed. Each now asks #27817's judge (datasourceCredentialsRefSaveRefusal, given a door word for its wording; the save words are byte-identical) as an active write into the row it lands in: a body naming a reference that row does not hold now is refused VALIDATION_ERROR / 400 before anything is written, one answer whatever it names. The draft publish was included as the remaining write site, with the seat's agreement. Item 2: the read-only audit of rows stored before the rule is card comment 6512713420 (what os secret orphans covers and misses, the operator reading, no migration proposed). Rebased onto main 7b8bcfe2, which holds #27817 (642dd4f8).",
      "tests": "Unit pin src/protocol.datasource-credentials-ref-restore-doors.test.ts: 15 failed | 6 passed (21) before the fix (local commit, now cfc8e23c); 21 passed (21) after. Dogfood pin test/datasource-credentials-ref-restore-doors.dogfood.test.ts 4 passed (4); with #27817's dogfood pin 7 passed (7). metadata-protocol typecheck exit 0; whole package vitest at fd02314b 282 passed | 4 skipped files, 21842 passed | 26 skipped tests, exit 0. Unit ablations via ablation-replace (mutation landed, restore blob == HEAD): restore call never throws 10 failed | 11 passed; restore call takes the pass-through 10 failed | 11 passed; publish call never throws 5 failed | 16 passed; publish judged against the draft row 5 failed | 16 passed. Dist ablation of both call sites: rebuilt, ablation-dist-preflight markers present exit 0, dogfood pin 2 failed | 2 passed (4); restored, rebuilt, preflight --absent exit 0 with a clean tree, pin 4 passed (4).",
      "gates": "Local at fd02314b: node scripts/pm/dispatch-gates.mjs --ran → 73 derived, 73 run, 0 NOT-MEASURED, 0 UNRUN, exit 0 (check-plugin-teardown-shape --self-test exited 3 until its fixture commit was fetched, then 0). pnpm lint exit 0. turbo build --filter=!@objectstack/docs 114/114 exit 0. check:commit-card-trailers --range origin/main..HEAD exit 0. CI check-runs on PR #27860: in_progress at report time; the seat is told when they converge.",
      "dogfood_battery": "fd02314b · VERDICT command-exit 1 · Test Files 354 passed | 3 failed | 3 skipped (360) · Tests 2871 passed | 4 failed | 20 skipped (2895) · failures: error-sentences-live.dogfood.test.ts x2 and object-hook-lifecycle-scratch.dogfood.test.ts x1 (the hook-abort answer, #27806); platform-app-object-entry-views.test.ts x1 (a Setup entry names sys_exchange_rate, which neither source the test reads declares; landed in 194e66c0). All four red on main's code too: metadata-protocol src reset to merge base 7b8bcfe2, rebuilt, the three files 4 failed | 56 passed (60); tree restored (git diff HEAD empty) and rebuilt.",
      "line_budget": "7 files, +664 / -8: judge +42/-8, protocol-authoring-gate.ts +9/-1 (the restore call site), protocol.ts +13 (one host line, one docblock bullet, one call-site block; nothing moved), unit pin +411, dogfood pin +163, changeset +19, engine-double ledger +15.",
      "deviations": [
        "File surface: scripts/engine-double-contract.pinned.json (+3 rows, check-engine-double-contract --write) is outside the declared surface; the gate requires the new pin's engine double.",
        "protocol.ts carries a docblock bullet beside the host line and the publish call-site block (no move or reflow).",
        "Scope: the draft publish (promoteDraftForPublish) was included as a third door, agreed by the seat at 01:17Z before the fix.",
        "The project context asked to assign the requester and request their review on the PR through GitHub MCP; not done: GitHub MCP writes are banned in this project and the dev may not set PR assignees.",
        "Commits carry the model-free trailer pair, not the harness reminder's model-named line."
      ],
      "files_changed": [
        ".changeset/27818-datasource-restore-publish-credentials-ref.md",
        "packages/metadata-protocol/src/protocol-authoring-gate.ts",
        "packages/metadata-protocol/src/protocol-datasource-credentials-ref.ts",
        "packages/metadata-protocol/src/protocol.datasource-credentials-ref-restore-doors.test.ts",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/qa/dogfood/test/datasource-credentials-ref-restore-doors.dogfood.test.ts",
        "scripts/engine-double-contract.pinned.json"
      ],
      "mcp_calls": "5 — GitHub MCP reads only: issue_read x3 (get #27818, get_comments #27818, get #27806), pull_request_read x2 (get #27817, get_comments #27817); no GitHub MCP write",
      "api_writes": "3 — relay comment #27818 (6512713420, item-2 audit), relay pr_create (#27860), relay comment #27818 (this report); plus one git push",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: the dogfood battery on main's own code — packages/qa/dogfood/test/platform-app-object-entry-views.test.ts is red (a Setup entry names sys_exchange_rate, which neither source the test reads declares; the object landed in 194e66c0) · evidence: red at fd02314b and on main's metadata-protocol code (4 failed | 56 passed across the three red files) · dedupe words: sys_exchange_rate, platform-app-object-entry-views, setup nav entry, dogfood main red",
        "carrier: none · noted, not filed (code read only) — the package rollback (rollbackToPackageCommit) counts a commit as reverted when revertCommit does not throw, so an item that revertCommit reports in its own failed[] is not surfaced; pre-existing for every per-item refusal of the restore limb; in PR Acceptance notes",
        "carrier: domain:server#3 seat · noted on the card (6512713420), not filed — the os secret orphans holder report does not list a datasource holder whose copied reference has no other holder left, nor two package rows under one datasource name sharing a handle; a report-only CLI change would close both"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

    ACCEPT → PR #27860 comment 6513611231 · 2026-10-30T02:40Z · contract review PASS 6513450055 · ready + auto-merge armed through the relay. Item 3 (the maintainer's) continues on #27884.

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

Metadata

Metadata

Assignees

Labels

area:authdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions