Skip to content

docs: align access, audit and approval pages with ObjectStack 17.0–17.7 - #331

Open
objectstack-fleet[bot] wants to merge 2 commits into
mainfrom
claude/issue-329-access-audit-17x
Open

objectstack-fleet[bot] wants to merge 2 commits into
mainfrom
claude/issue-329-access-audit-17x

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #329

Aligns the access, audit and approval pages with what ObjectStack 17.0 through 17.7 actually ship. Every row of the card was checked against the release notes (objectstack origin/main 54770208, content/docs/releases/v17/17-0.mdx through 17-7.mdx). Where a note and the current docs disagreed, the published 17.x packages decided (npm pack of @objectstack/plugin-audit, plugin-auth, plugin-approvals, service-settings and core at 17.7.0, and the pinned @objectstack/spec 17.7.0). objectstack main is 18.0.0-next.1, and its hand-written permission pages already describe unreleased v18 behaviour, so they were not taken as the 17.x truth.

Row by row

Release Card row Verdict Where Rests on
17.0 Approval requests readable by participants only Fixed build/automation/approvals.mdx ("Who can see a request"), use/approvals.mdx (the All row and "You see the requests you take part in") 17-0, "Approval requests are visible to participants, not the whole tenant (#3590)". Who counts as a participant comes from visibleRequestIds in plugin-approvals 17.7.0: the submitter, a current approver (a position slot included), anyone who has acted or commented, plus platform and organization admins through the override
17.0 Anonymous data access denied unconditionally Fixed reference/security.mdx item 2 17-0, "Anonymous access is denied unconditionally — api.requireAuth is gone (#3963)"
17.1 Deactivated permission set or position stops granting; its sharing-rule shares are revoked Fixed permission-sets.mdx "Deactivating a permission set", positions.mdx "Deactivating a position" 17-1 security corrections #8613 and #8710. This includes two points the card did not list: approval routing still reaches a deactivated position's holders, and the last-administrator guard refuses the lock-out
17.1 Partial field masking (maskingRule) Fixed field-level-security.mdx (a new "Masked fields" section, a Masked field column, and the explain engine), permission-sets.mdx appendix, permissions/index.mdx layer 5, reference/security.mdx item 4 17-1, "Partial field masking (#8993)". FieldSchema.maskingRule is in spec 17.7.0. Who sees what follows computePartialMaskRules: the field's requiredPermissions is the unmask gate, and a readable: false grant still hides the field
17.1 Record-view auditing Not reachable from ObjectOS, so not documented — See "Reachability" below
17.1 Read-only approval visibility (recordReaderVisibleObjects) Not reachable from ObjectOS, so not documented — See "Reachability" below. The approvals pages state the default instead: being able to read a record does not show its requests
17.1 API keys bound to the minter's active organization Fixed, with a correction to the card api-access.mdx "Keys belong to an organization" 17-1 #8287 and 17-4's notable fixes. The 17-4 note says both walled postures refuse an organization-less key. The code says otherwise: core 17.7.0 refuses it only under isolated (organization_required), and group still admits it. A key whose owner has left the organization (organization_membership_ended) is refused under both. The page follows the code
17.3 Self sign-up closed by default (invite_only) Fixed: reachable, and a separate decision from OS_AUTH_MEMBERSHIP_POLICY authentication.mdx "Who may sign up", users.mdx "Add people" 17-3, "One declared audience posture, defaulting to invite_only (4f24e9d)". The setting reaches ObjectOS through the auth settings namespace that ObjectOS already documents as Setup → Authentication → Membership. The published @objectstack/service-settings 17.3.0 and 17.7.0 carry the Audience group beside it (audience_posture). Admission logic: decideAudienceAdmission in plugin-auth 17.7.0, which shows the signup_enabled toggle cannot override invite_only
17.5 RLS refuses predicates it cannot enforce; a using-only policy gates writes Fixed record-access.mdx "Row-level security refuses what it cannot enforce", reference/security.mdx item 3 17-5, "Row-level security fails closed on what it cannot enforce"
17.6 Deny baseline for a caller with no permission set; guest grants nothing Fixed positions.mdx ("guest grants nothing" and the deny-baseline paragraph), permissions/index.mdx layer 2, reference/security.mdx item 2 17-6, "A caller that resolves no permission set gets the deny baseline". The 17-6 known-issues correction of 2026-10-06 and 17-7's notable fixes both record #21158 as closed, not planned, so no guest binding grants an anonymous caller anything on 17.6 or 17.7. The current ObjectStack permissions/positions.mdx agrees ("names no permission set")
17.6 FLS at every query door; filtering, sorting or grouping on a hidden or masked field answers 403 Fixed field-level-security.mdx ("On query" and a Filter, sort, group, aggregate row), reference/security.mdx item 4 17-6, "Field-level security reaches every query door". The guard for hidden fields on the data API itself predates 17.x: the v15 notes already name the anti-oracle predicate guard
17.6 Audit and activity rows served only for readable records, admins included; delete and sign-out rows no longer served Fixed, with a correction to the card operate/audit-logs.mdx ("What each reader is served", the Writes and Auth rows, "Reading a change"), operate/observability.mdx 17-6, "The compliance ledger and the activity stream serve only rows about records the reader can read (#21194, #21069)". 17.7 reversed "admins included" for platform administrators: view_all_audit_log lifts the narrowing, and ADMIN_FULL_ACCESS_CAPABILITIES carries it by default (17-7, "Auditing and access", #21296; spec 17.7.0). An organization admin does not hold it

Reachability of the two rows not documented

Both are open-framework plugin constructor options. Neither has a metadata key, an environment variable or a Setup field that an ObjectOS admin or operator can reach:

  • Record-view auditing is new AuditPlugin({ readAudit: { objects } }). The ObjectStack page permissions/record-view-auditing.mdx says there is "no object-metadata key and no environment variable". ObjectOS loads @objectstack/plugin-audit itself, from the artifact's requires (reference/runtime-capabilities.mdx). The requires provider table (packages/core/src/capability-providers.ts, audit) has no configKey, so that path builds the plugin with no options. A compiled objectstack.json cannot carry a plugin instance, and no ObjectOS environment variable or Setup field names the option.
  • recordReaderVisibleObjects is an ApprovalsServicePlugin option. The approvals provider entry has no configKey either. The object-level enable.approvalsVisibleToReaders exists only on objectstack main (18.0.0-next) and is absent from spec 17.7.0.

ObjectOS's own composition is private. This conclusion rests on the documented surfaces, and the maintainer can overturn it.

Locale siblings

The rule from #256 deletes a page's siblings when its English correction removes or reverses a capability claim. Two pages meet it:

  • configure/permissions/positions.{de,es,fr,ja,ko,zh-Hans,zh-Hant}.mdx carried "bind to guest to open something to anonymous visitors", which is now reversed.
  • configure/permissions/index.{de,es,fr,ja,ko,zh-Hans,zh-Hant}.mdx carried "guest … lets you bind baseline permission sets", which is now reversed.

gen-zh-hant.mjs pruned the two zh-Hant files. All other siblings stay: their pages gained content, or a claim gained a qualifier (the audit views still show everything to platform administrators), so they stay with the translation pass. No locale file was hand-edited.

Gates (local, on 6067fb5)

Gate Exit Reading
node apps/docs/scripts/gen-zh-hant.mjs --check 0 52 generated files match byte for byte
node .github/scripts/check-doc-samples.mjs 0 79 English pages, 99 pass, spec 17.7.0. The new masking sample passes as "fields of an object". A negative control with maskingRule: 'iban' in a scratch tree exits 1
node .github/scripts/check-translation-ownership.mjs 0 14 locale deletions and 13 English files. Enforcement is off until TRANSLATION_BOT_LOGIN is set
node .github/scripts/check-translations.mjs 0 translations gate passed
pnpm turbo run type-check --continue (shared lock) 0 cache miss, so it really ran on this content
pnpm turbo run build (shared lock) 0 cache miss. Dynamic OG fonts fail to fetch through this container's proxy, which is environmental
node .github/scripts/check-locale-surface.mjs 0 after the build
node .github/scripts/check-positioning.mjs 0 after the build
node .github/scripts/check-search-locales.mjs 0 after the build
pnpm turbo run test (shared lock) 0 cache hit, which is valid: none of its declared inputs (.github/scripts/**, apps/docs/lib/i18n.ts) changed
node scripts/pm/check-half-states.mjs --self-test · node .github/scripts/check-node-floor.mjs 0 · 0

The built HTML has every new anchor and every link target: #masked-fields, #who-may-sign-up-objectstack-173, #what-each-reader-is-served, #deactivating-a-position, #deactivating-a-permission-set, #keys-belong-to-an-organization-objectstack-171, #row-level-security-refuses-what-it-cannot-enforce, views#public-forms and environment-variables#multi-organization-deployments. The zh-Hans and de routes for positions and permissions render the corrected English. The render smoke-check and the size gate run in CI on this PR.

Acceptance notes (not filed)


Generated by Claude Code

… 17.0-17.7

Masking as a third field state, the query-door 403, the deny baseline and
the guest anchor, deactivation, RLS fail-closed, audit-row visibility and
view_all_audit_log, approval participant visibility, API-key organization
binding, and the closed-by-default sign-up audience.

Deletes the locale siblings of positions.mdx and permissions/index.mdx:
both English corrections reverse the claim that binding a set to `guest`
opens data to anonymous visitors.

Claude-Session: https://claude.ai/code/session_01CXCwzqwMhkphKoBC11oPSz
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT: PR #331 for #329 · 2026-10-15T13:16Z · seat repo:objectos, session session_01CXCwzqwMhkphKoBC11oPSz. Implemented-by: os-dev subagent, branch claude/issue-329-access-audit-17x. Reviewed-by: this seat.

Shape.

  • Draft, base main, head 6067fb5, mergeable_state: clean, assignee os-tesla.
  • The first line is Fixes #329. A full-body scan finds no other closing keyword.
  • Files: 27 — 13 English pages, all inside the claim's surface, and 14 deleted locale siblings. +330/−1628, under the 3000-line line. No governed path is touched.

Checks pinned to 6067fb5: build success (every step, including docs samples, zh-Hant current, type-check, build, locale surface, positioning, search locales, test, size budget, and the rendered-page smoke check), Node floor success, Ownership & freshness success. Deploy docs skipped (PR event).

Prose, checked sentence by sentence against objectstack @objectstack/*@17.7.0 (4e4e8814), not against the report:

  • The API-key table: an organization-less key is refused only when the posture walls and is not union-scoped, i.e. under isolated, not under group (packages/core/src/security/api-key.ts:368-377). The dev's correction of the card stands.
  • Masking:
    • presets phone, id_card, bank_account, email, name plus { keepHead, keepTail } (spec/src/data/field.zod.ts:729-747);
    • masked for every non-system caller unless all of requiredPermissions are held (:1931);
    • writing the placeholder back is 400 VALIDATION_ERROR (plugin-security/src/errors.ts MaskedValueWriteError).
  • Deactivation: a deactivated set contributes nothing (resolve-authz-context.ts step 6b), and the last-administrator guard judges a deactivation (plugin-auth/last-admin-guard.ts, active in the standing keys).
  • Sign-up audience: SELF_REGISTRATION_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED, email_domain and selfRegistrationPermissionSet are all present; the audience key is patched in plugin-auth/src/auth-manager.ts:4310.
  • RLS: rls-predicate-unenforceable and rls-predicate-unknown-field are present.
  • Audit visibility: the view_all_audit_log capability reads "rows about deleted records, ended sessions and records the holder cannot open" (spec/src/security/capabilities.ts:94). The dev's correction of the card ("admins included") stands.
  • Not documented, correctly: readAudit exists only as an AuditPlugin constructor option (cli/src/commands/serve.ts:4317-4322 registers it with no options), and recordReaderVisibleObjects only as an approval-service option. Neither has an ObjectOS-reachable surface on 17.x.
  • Not verified by the seat: the Setup UI label "Setup → Authentication → Audience". The settings key exists; the label is the dev's reading of the auth namespace.

Locale siblings. All 14 deletions are the positions and permissions/index siblings, whose guest claim the English now reverses. That meets the criterion of ruling 5989567068. The 7 operate/observability siblings are kept: the English narrows who is served sign-out rows, but platform administrators still hold view_all_audit_log by default. Accepted as a judgment, not a reversal.

Out-of-scope findings. (a) The changelog's 17.1 bullets point at capabilities ObjectOS cannot reach. Carrier: #330, in flight and owning changelog.mdx; forwarded to that dev. The other three are Acceptance notes, not cards.

Landing. Non-governed and under the size line. The seat marks the PR ready. ❗ Merging to main publishes production, and this session's classifier refuses the seat's own merge, so the PR awaits a human merge.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat note · 2026-10-15T13:24Z: a one-sentence follow-up is being pushed to this PR — reference/security.mdx, the AI-tool count, which should read "per action exposed to AI". The seat re-checks the new head and updates 6192993343. Merging the current head is safe, but it would leave that sentence for a separate PR.


Generated by Claude Code

This branch has not been deployed

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

Labels

None yet

Projects

None yet

2 participants