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
security(settings): an observation outside platform-core.settings-hub-roundtrip's clauses (settings audit trail) at 316be321e — detail withheld pending maintainer #21792
QA-source: #21784 · platform-core.settings-hub-roundtrip · outside the item's clauses
While running platform-core.settings-hub-roundtrip (rev 3) in the 17.7 pre-release run #21784 (subject 316be321e), the runner found a defect in how the settings audit trail records secret-valued settings. The item's own clauses all pass.
Class: authorization / secret handling (exposure across a permission boundary). Under RUNNER rule 2 the reproduction and the mechanism are withheld pending maintainer; they are held in the runner session (Claude Code session session_018zT8d8NpiQ1ExhuNd5TxY6) and reported to the maintainer there.
Verification: found by the runner and confirmed independently by a verifier (RUNNER rule 7) from code and a live boot: CONFIRMED.
Needs a maintainer ruling: the behaviour follows a documented contract, so the fix direction is a contract decision, not only a code change.
Severity as judged by the verifier: medium.
Not a 17.7 regression: the code involved predates 17.6.0.
Owning repo:objectstack. No existing card covers it (open issues searched).
Path: the road — run it, platform settings changed in the console | 缺项 (found outside platform-core.settings-hub-roundtrip's clauses; no item asserts the settings audit trail) | P2
Triage: first grade — bug · security · priority:p2 · domain:services · area:access · needs-user-decision. The documented contract itself is in question, so the direction is the maintainer's
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-05T02:11Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Classes and positions only. The withheld reproduction stays withheld (RUNNER rule 2); this seat does not hold it.
Triage: lands in the settings audit trail (plugin-audit and the settings service, by class) ⇒ domain:services; rationale: audit trails and secret handling across a permission boundary are services-lane boundaries, which the lane always escalates.
Why needs-user-decision, not pm:queue. The card says the behaviour follows a documented contract, so the fix is a contract decision, not only a code change. Changing what a security-relevant contract records is on the human floor. If the ruling touches packages/spec, the claim routes that half to the spec seat.
Why p2. The verifier graded it medium. It predates 17.6.0.
Ruled: the settings audit trail records secret-valued settings with a keyed digest (the platform's existing keyed digest, #21292), never an unkeyed one; non-secret settings are unchanged. The crypto-provider contract text in packages/spec is revised to say so. Recorded by the PM seat from the maintainer's answer in Claude Code session session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-05, choosing the option 「密钥类改用加密钥摘要」. Class level only; the held detail stays withheld.
Claim: PM loop round 1 (17.7 pre-release defect from QA run #21784, dispatched on the maintainer's direct order)
Session: session_018zT8d8NpiQ1ExhuNd5TxY6
Account: hotlong (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-21792-settings-audit-digest
Worktree: objectstack-issue-21792
Domain: domain:services (card label, as triage set it); the packages/spec half is a contract-comment revision only, no exported surface changes
File surface: packages/services/service-settings/src, packages/spec/src/contracts, .changeset/
Container & model: M, mode:subagent, model: opus (default tier; no path-derived mandate)
Clause-②: no
Thread-read: 5986925364
Serial constraints cleared: none named
Provenance: the maintainer, in Claude Code session session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-05, verbatim: 「鉴权类问题的细节,这几个任务你直接接受派发处理」, with the ruling above; landing per the standing order 「开发完整就进队列合并」. This session holds the withheld reproduction; the dispatch carries it privately.
{
"issue": 21792,
"status": "done",
"branch": "claude/issue-21792-settings-audit-digest",
"pr": "https://github.com/objectstack-ai/objectstack/pull/21809",
"session": "session_018zT8d8NpiQ1ExhuNd5TxY6",
"premise_still_valid": true,
"summary": "Per the recorded ruling, a secret-valued setting's audit fingerprint on both ledgers (sys_audit_log config_change valueDigest, sys_setting_audit newHash) is now ICryptoProvider.keyedDigest, on the sys_secret path and the legacy inline-adapter path. A new private helper SettingsService.secretAuditDigest computes it. When no keyed digest is available (no provider wired, or the provider refuses), the rows record no fingerprint (the bare encrypted marker / null) and a warn is logged once per key. The write is never refused and there is never an unkeyed fallback. Non-secret settings are unchanged. The crypto-provider contract text in packages/spec (digest doc and keyedDigest requirement 3) is revised to state this, comment-only. No non-test consumer compares these digests (git grep over packages/**). Three existing tests that pinned the unkeyed spelling for secrets were updated. The optional oldHash rider was not done: it needs a decrypt of the previous ciphertext per write, so it is not small. PR assignee was NOT set (see api_writes).",
"tests": "At HEAD f34f69594c (test code unchanged since fc5c86e1c6). (1) Dependency closure build pnpm --filter '@objectstack/service-settings^...' build: VERDICT command-exit 0. (2) pnpm --filter @objectstack/service-settings test: Test Files 34 passed (34), Tests 612 passed (612); typecheck tsc --noEmit (include src, so tests are covered): exit 0, all through os-verify-lock. (3) New settings-audit-secret-digest.test.ts, 7 cases. (4) Ablation via scripts/ablation-replace.mjs, two legs. The tests import src, so no dist rebuild was needed. Leg 1 put back cryptoProvider.digest on the sys_secret branch: anchor 1 -> 0, blob a148f503e350 -> 4364c83199d6, Tests 2 failed / 5 passed. Leg 2 put back crypto.digest on the legacy branch: blob -> 781b5f2d0a20, 2 failed / 5 passed. Each leg restored with blob == HEAD a148f503e350 and git diff HEAD empty. Expected direction: red. Observed: red. (5) dispatch-gates --commands derived 87 families: 85 exit 0. check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET (needs a whole-repo build). check:type-check-debt was killed twice by the foreground cap (exit 124). Both are NOT MEASURED and left to CI. --ran reconciliation: 87 accounted, 0 UNRUN, exit 0. The changeset gates, issue-citations, doc-authoring and nul-bytes were re-run at f34f69594c, all exit 0. (6) Lint, narrowed: eslint --no-inline-config --format json over the 5 touched .ts files reported 5 files, 0 errors, 0 warnings. The population is eslint.config.mjs's TS block, files **/*.{ts,...}. Type-aware linting is not enabled (no parserOptions.project), so this diff cannot move any untouched file's verdict.",
"mcp_calls": "0",
"api_writes": "2 -- git push of the branch (not REST); relay repository_dispatch pr_create (POST /repos/objectstack-ai/objectstack/pulls via fleet-write run 37261923931; read back byte-identical); this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/21792/comments via relay). Deviation: label-write.mjs --issue 21809 --assign hotlong was refused by the session's auto-mode classifier (External System Writes) before anything was sent. Not retried on another route, so the seat still needs to set the PR assignee to hotlong. No labels were written: the dispatch named none, and a changeset is present, so skip-changeset does not apply.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) - sys_setting_audit old_hash/new_hash field descriptions in packages/platform-objects/src/system/sys-setting-audit.object.ts say 'SHA-256 of the ... value'. That was already inexact (the default adapter writes fnv32) and is now inexact for secrets (hmac-sha256). Noted in Acceptance notes, not filed.",
"carrier: none (承接者:无) - sys_setting_audit oldHash is still hard-coded null at the settings write site. The optional rider was not taken because recording a secret's previous keyed fingerprint needs a decrypt per write. Noted in Acceptance notes, not filed."
]
}
Activity
objectstack-fleet commentedon Oct 5, 2026
Path: the road — run it, platform settings changed in the console | 缺项 (found outside
platform-core.settings-hub-roundtrip's clauses; no item asserts the settings audit trail) | P2Triage: first grade —
bug·security·priority:p2·domain:services·area:access·needs-user-decision. The documented contract itself is in question, so the direction is the maintainer'sTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-05T02:11Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Classes and positions only. The withheld reproduction stays withheld (RUNNER rule 2); this seat does not hold it.Triage: lands in the settings audit trail (
plugin-auditand the settings service, by class) ⇒domain:services; rationale: audit trails and secret handling across a permission boundary are services-lane boundaries, which the lane always escalates.Why
needs-user-decision, notpm:queue. The card says the behaviour follows a documented contract, so the fix is a contract decision, not only a code change. Changing what a security-relevant contract records is on the human floor. If the ruling touchespackages/spec, the claim routes that half to the spec seat.Why p2. The verifier graded it medium. It predates 17.6.0.
维护者速读
平台设置的审计留痕在记录"值是密钥"的设置时,有一处跨权限边界的暴露。现在的行为是照文档契约做的,所以要改的是契约本身,要你来定。细节和选项都在 QA 跑测会话里(
session_018zT8d8NpiQ1ExhuNd5TxY6),已经报给你了,这里不重述。请你在那个会话里裁,裁定以Ruled:记到本卡,只写到类别一级。四棱(os-decision-facets)
Prior rulings read: the settings-hub contract the card cites → not named publicly; ADR unresolved (the held detail names it); thread: none
Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
Ruled: the settings audit trail records secret-valued settings with a keyed digest (the platform's existing keyed digest, #21292), never an unkeyed one; non-secret settings are unchanged. The crypto-provider contract text in
packages/specis revised to say so. Recorded by the PM seat from the maintainer's answer in Claude Code sessionsession_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-05, choosing the option 「密钥类改用加密钥摘要」. Class level only; the held detail stays withheld.Claim: PM loop round 1 (17.7 pre-release defect from QA run #21784, dispatched on the maintainer's direct order)
Session:
session_018zT8d8NpiQ1ExhuNd5TxY6Account:
hotlong(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-21792-settings-audit-digestWorktree:
objectstack-issue-21792Domain:
domain:services(card label, as triage set it); thepackages/spechalf is a contract-comment revision only, no exported surface changesFile surface:
packages/services/service-settings/src,packages/spec/src/contracts,.changeset/Container & model:
M,mode:subagent,model: opus(default tier; no path-derived mandate)Clause-②: no
Thread-read: 5986925364
Serial constraints cleared: none named
Provenance: the maintainer, in Claude Code session
session_018zT8d8NpiQ1ExhuNd5TxY6, 2026-10-05, verbatim: 「鉴权类问题的细节,这几个任务你直接接受派发处理」, with the ruling above; landing per the standing order 「开发完整就进队列合并」. This session holds the withheld reproduction; the dispatch carries it privately.Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
os-dev-report
{ "issue": 21792, "status": "done", "branch": "claude/issue-21792-settings-audit-digest", "pr": "https://github.com/objectstack-ai/objectstack/pull/21809", "session": "session_018zT8d8NpiQ1ExhuNd5TxY6", "premise_still_valid": true, "summary": "Per the recorded ruling, a secret-valued setting's audit fingerprint on both ledgers (sys_audit_log config_change valueDigest, sys_setting_audit newHash) is now ICryptoProvider.keyedDigest, on the sys_secret path and the legacy inline-adapter path. A new private helper SettingsService.secretAuditDigest computes it. When no keyed digest is available (no provider wired, or the provider refuses), the rows record no fingerprint (the bare encrypted marker / null) and a warn is logged once per key. The write is never refused and there is never an unkeyed fallback. Non-secret settings are unchanged. The crypto-provider contract text in packages/spec (digest doc and keyedDigest requirement 3) is revised to state this, comment-only. No non-test consumer compares these digests (git grep over packages/**). Three existing tests that pinned the unkeyed spelling for secrets were updated. The optional oldHash rider was not done: it needs a decrypt of the previous ciphertext per write, so it is not small. PR assignee was NOT set (see api_writes).", "tests": "At HEAD f34f69594c (test code unchanged since fc5c86e1c6). (1) Dependency closure build pnpm --filter '@objectstack/service-settings^...' build: VERDICT command-exit 0. (2) pnpm --filter @objectstack/service-settings test: Test Files 34 passed (34), Tests 612 passed (612); typecheck tsc --noEmit (include src, so tests are covered): exit 0, all through os-verify-lock. (3) New settings-audit-secret-digest.test.ts, 7 cases. (4) Ablation via scripts/ablation-replace.mjs, two legs. The tests import src, so no dist rebuild was needed. Leg 1 put back cryptoProvider.digest on the sys_secret branch: anchor 1 -> 0, blob a148f503e350 -> 4364c83199d6, Tests 2 failed / 5 passed. Leg 2 put back crypto.digest on the legacy branch: blob -> 781b5f2d0a20, 2 failed / 5 passed. Each leg restored with blob == HEAD a148f503e350 and git diff HEAD empty. Expected direction: red. Observed: red. (5) dispatch-gates --commands derived 87 families: 85 exit 0. check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET (needs a whole-repo build). check:type-check-debt was killed twice by the foreground cap (exit 124). Both are NOT MEASURED and left to CI. --ran reconciliation: 87 accounted, 0 UNRUN, exit 0. The changeset gates, issue-citations, doc-authoring and nul-bytes were re-run at f34f69594c, all exit 0. (6) Lint, narrowed: eslint --no-inline-config --format json over the 5 touched .ts files reported 5 files, 0 errors, 0 warnings. The population is eslint.config.mjs's TS block, files **/*.{ts,...}. Type-aware linting is not enabled (no parserOptions.project), so this diff cannot move any untouched file's verdict.", "mcp_calls": "0", "api_writes": "2 -- git push of the branch (not REST); relay repository_dispatch pr_create (POST /repos/objectstack-ai/objectstack/pulls via fleet-write run 37261923931; read back byte-identical); this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/21792/comments via relay). Deviation: label-write.mjs --issue 21809 --assign hotlong was refused by the session's auto-mode classifier (External System Writes) before anything was sent. Not retried on another route, so the seat still needs to set the PR assignee to hotlong. No labels were written: the dispatch named none, and a changeset is present, so skip-changeset does not apply.", "open_questions": [], "out_of_scope_findings": [ "carrier: none (承接者:无) - sys_setting_audit old_hash/new_hash field descriptions in packages/platform-objects/src/system/sys-setting-audit.object.ts say 'SHA-256 of the ... value'. That was already inexact (the default adapter writes fnv32) and is now inexact for secrets (hmac-sha256). Noted in Acceptance notes, not filed.", "carrier: none (承接者:无) - sys_setting_audit oldHash is still hard-coded null at the settings write site. The optional rider was not taken because recording a secret's previous keyed fingerprint needs a decrypt per write. Noted in Acceptance notes, not filed." ] }Generated by Claude Code
fix(service-settings): the settings audit trail records secret-valued…
docs(qa): checklist items for the 17.7 pre-release security fixes (ob…