Skip to content

feat(platform-objects): secure-by-default posture for sensitive system objects (ADR-0066 ④) - #2691

Merged
os-zhuang merged 2 commits into
mainfrom
claude/auth-lifecycle-gaps-248nvb
Jul 8, 2026
Merged

feat(platform-objects): secure-by-default posture for sensitive system objects (ADR-0066 ④)#2691
os-zhuang merged 2 commits into
mainfrom
claude/auth-lifecycle-gaps-248nvb

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What & why

Lands the system-object slice of ADR-0066 ④ (secure-by-default rollout) from the authorization gap map (umbrella #2561). The D2 access: { default: 'private' } primitive has been enforced since #2690's predecessors — but no production object used it: the platform's raw secret/credential stores were still covered by member_default's wildcard '*': allowRead. This PR flips the clearly-sensitive, flip-safe ones.

Change

6 objects → access: { default: 'private' }

ObjectWhat its rows are
sys_secretencrypted settings/datasource secrets
sys_jwksJWT signing private keys
sys_verificationlive password-reset / verify tokens (reading one = account takeover)
sys_oauth_access_tokenlive bearer tokens (reading one = session hijack)
sys_oauth_refresh_tokenlive long-lived credentials
sys_device_codepending device-grant codes (reading one = CLI-login hijack)

An ordinary member's generic data-layer access now 403s; platform admins retain access via the posture-gated viewAllRecords/modifyAllRecords superuser bypass.

Flip-safety — every runtime consumer verified

  • better-auth reads/writes all identity tables via its adapter with isSystem: true → middleware short-circuits; auth flows unaffected.
  • engine.resolveSecret reads sys_secret at driver level (below the middleware).
  • SettingsService / datasource secret-binder read principal-less → middleware falls open for internal calls.
  • API-key auth (core/security/api-key.ts) reads with isSystem: true.

Deliberately excluded (member self-service)

sys_session, sys_api_key, sys_oauth_application, sys_two_factor keep the public posture: the Account app ("My Sessions" / "My API Keys" / "My Apps" / 2FA "My Enrollment") reads them through the generic data layer as the member — flipping them would break self-service. Row scoping (owner/tenant RLS + _self carve-outs) remains their guard. A test asserts the exclusion so it's a documented decision, not an oversight.

Also

  • sys_scim_provider (SCIM bearer-token config) gains requiredPermissions: ['manage_platform_settings'] — mirrors its sibling sys_sso_provider's D3 gate.
  • Setup nav nav_jwks is capability-gated like nav_api_keys (UX: non-admins no longer see a menu entry that can only 403).

Regression pinning

  • platform-objects.test.ts — "secure-by-default posture" suite asserts each flip and the self-service exclusions.
  • ADR-0056 D10 conformance matrix — new row secure-by-default-posture (dropping the flag from a secret store fails CI, not review).
  • ADR-0066 ④ + content/docs/permissions/authorization.mdx updated: system-object slice landed; still open = Studio posture surfacing (objectui) and the explicit-public reference-data convention (no such objects ship in framework today).

Tests / verification

  • platform-objects: 71 passed (incl. new posture suite)
  • plugin-auth: 230 passed (auth flows unaffected by the flips)
  • dogfood: conformance-matrix test ✓, showcase-anonymous-deny + two-doors-permission e2e (real kernel boot + login) 8 passed
  • service-settings129 + service-datasource93 passed (the sys_secret consumers)
  • Full workspace turbo build green

Refs #2561

🤖 Generated with Claude Code

https://claude.ai/code/session_0187GeNqezxV6g5jiLiggfbt


Generated by Claude Code

…m objects (ADR-0066 ④)
The platform's raw secret/credential stores no longer ride the wildcard `'*'`
permission grant. `sys_secret`, `sys_jwks`, `sys_verification`,
`sys_oauth_access_token`, `sys_oauth_refresh_token`, and `sys_device_code`
declare `access: { default: 'private' }` — an ordinary member's generic
data-layer access gets 403; platform admins retain access via the
posture-gated viewAllRecords/modifyAllRecords superuser bypass.
Every runtime consumer is unaffected (verified per reader): better-auth reads
via its adapter (system context), `engine.resolveSecret` reads at driver
level, SettingsService / the datasource secret-binder read principal-less
(middleware falls open for internal calls), API-key auth reads isSystem.
- `sys_scim_provider` gains `requiredPermissions: ['manage_platform_settings']`
(D3 capability gate), mirroring its sibling `sys_sso_provider`.
- Setup nav `nav_jwks` is capability-gated like `nav_api_keys` so non-admins
don't see a menu entry that can only 403.
- Member self-service objects (sys_session, sys_api_key,
sys_oauth_application, sys_two_factor) deliberately keep the public
posture — the Account app reads them as the member; row scoping is their
guard. A test asserts this exclusion too.
- Pinned against regression: platform-objects.test.ts posture suite + the
ADR-0056 D10 conformance-matrix row `secure-by-default-posture`.
Tests: platform-objects 71, plugin-auth 230, dogfood conformance +
anonymous-deny + two-doors e2e, service-settings 129, service-datasource 93 —
all green; full turbo build green.
Refs #2561
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187GeNqezxV6g5jiLiggfbt
@vercel

vercelBot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 8, 2026 3:49pm

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/m and removed documentation Improvements or additions to documentation tests tooling labels Jul 8, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/dogfood, @objectstack/platform-objects.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/permissions/authorization.mdx(via packages/dogfood)
  • content/docs/plugins/packages.mdx(via @objectstack/platform-objects)
  • content/docs/ui/setup-app.mdx(via @objectstack/platform-objects)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ADR-0066 ④)
The Advanced table listed Verifications and Device Codes, but those nav
entries were deliberately removed (sensitive ephemeral secrets — not
browsable; see setup-nav.contributions.ts). Also document that nav_jwks is
capability-gated and sys_jwks is private-posture.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0187GeNqezxV6g5jiLiggfbt
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Jul 8, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 8, 2026 16:03
@os-zhuang
os-zhuang merged commit 9fa84f9 into mainJul 8, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/auth-lifecycle-gaps-248nvb branch July 8, 2026 16:04
baozhoutao added a commit that referenced this pull request Jul 19, 2026
fix(app-shell,plugin-detail): record History tab renders display values, not raw audit payloads (#2691)
objectui@e164196801bd38e1396798877a69e0a09e80d8d3
baozhoutao added a commit that referenced this pull request Jul 19, 2026
…e164196801bd (#3294)
* ci(release): bump downstream-smoke HOTCRM_REF to v2.1.0 (agent visibility removal)
hotcrm v2.0.0 still authors the agent `visibility` field that #3216
removed from the spec (ADR-0056 D8), so the pre-publish downstream
smoke has been red and the release train is stalled. Point the gate at
hotcrm v2.1.0, which drops the field.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: bump objectui to e164196801bd
fix(app-shell,plugin-detail): record History tab renders display values, not raw audit payloads (#2691)
objectui@e164196801bd38e1396798877a69e0a09e80d8d3
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 22, 2026
The v16 sweep (#3358 §4 "Record History tab") couldn't exercise the tab because
no showcase object opted into it: the console gates the History tab on
object-level `enable.trackHistory` (RecordDetailView) — audit *capture* is
always on, but the *tab* is opt-in — and the showcase only set `trackHistory`
at the FIELD level (industry/status), which just selects which diffs are
summarized. So the tab never rendered anywhere and #2691/#3293 (display-value
diffs, computed-field exclusion) were undemonstrated.
Set `enable: { trackHistory: true }` on Account (which already declares
field-level `trackHistory` on `industry`/`status` for clean diffs).
Verified in the running app: the Account detail now shows a **历史 / History**
tab; after editing `industry`, it renders "Dev Admin · UPDATE · 行业: Retail →
Technology" — field label + select **display values** (not raw stored codes/ids),
no phantom value→null rows. `os validate` + `tsc --noEmit` pass.
Follow-up to #3364 / #3393, from the #3358 sweep §4.
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude