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
refactor(spec)!: v17 close-out of the #3896 line — release notes, tool inert-key removal, ADR-0113 (Proposed) (#3998)
Three parts, closing the #3896 security-audit line for the v17 window.
1. v17 release notes carry the whole security-hardening arc: a Highlights bullet (empty-criteria sharing + disabled RLS both fail closed now), two breaking-change sections with migration guidance, dead-cluster rows for DynamicLoadingConfig and rls.priority, Spec/kernel bullets for the 44-entry security-subset re-verification and the check:empty-state gate, and three upgrade-checklist items. implementation-status updated to match.
2. The four inert tool authoring keys are removed (major): category/permissions/active/builtIn parsed and did nothing — permissions promised an invocation gate nothing enforced, and active:false read as "withdrawn" while the tool kept reaching the LLM set and /execute kept running it (the rls.enabled shape). Full retirement kit per #3715: strict-rejection prescriptions (TOOL_RETIRED_KEY_GUIDANCE, 6 new tests), D2 conversion + D3 chain step (tool-inert-authoring-keys-removed), ToolCategorySchema removed with the key it typed, Studio tool form drops the retired inputs, the published AI skill's Tool section rewritten (it was teaching the removed keys, os:check'd), ledger entries deleted, api-surface + authorable-surface + json-schema.manifest + form-translation bundles updated deliberately, and the CLI authorWarn expectation retired with its ledger entry.
3. ADR-0113 merges as PROPOSED (not implemented): required as a write-time contract vs the column constraint as its own explicitly-authored axis — the tri-binding cited at its exact sites (record-validator; sql-driver.ts:4901; schema-drift.ts:249), criteria_json as first consumer. Q1 (spelling) / Q2 (new-field default) / Q3 (requiredWhen) remain open for adjudication.
spec: 6895 tests green; all gates green; 198 prose examples type-check; dogfood green. Union-merged over #4010's step-17 rationale (chain-replay gate verified the composition).
|**parameters**|`Record<string, any>`| ✅ | JSON Schema for tool parameters |
41
44
|**outputSchema**|`Record<string, any>`| optional |[EXPERIMENTAL — not enforced] JSON Schema for tool output. Keys are folded into the tool description only; outputs are not validated (liveness #1878/#1893). |
42
45
|**objectName**|`string`| optional | Target object name (snake_case) |
Copy file name to clipboardExpand all lines: content/docs/releases/implementation-status.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -287,7 +287,7 @@ The `auth` service in `CoreServiceName` covers both **authentication** (identity
287
287
- Auth route (`/auth/*`) only appears in Discovery when the auth plugin is registered
288
288
- Fine-grained authorization (RLS, sharing, territory) is internal to the auth plugin
289
289
- **Phase-1 RBAC enforcement is live end-to-end**: REST → ObjectQL → SecurityPlugin middleware now receives a populated `ExecutionContext` (userId, tenantId, positions, permissions). Tenant isolation is enforced as a Layer 0 tenant wall (`plugin-security/tenant-layer.ts`, ADR-0095 D1) that AND-composes `organization_id == current_user.organization_id` ahead of and independently of business RLS — the earlier wildcard `tenant_isolation` RLS policy on `member_default` was retired (an OR-merged business policy could widen it). The default `member_default` set still ships per-object overrides `sys_organization_self` (`id == current_user.organization_id`) and `sys_user_self` (`id == current_user.id`) for the global tables that lack an `organization_id` column. The earlier `tenantField` indirection (RLS expressions written against an abstract `tenant_id` column then rewritten to the configured physical column at compile time) was removed — the placeholder, the column name, and `RLSUserContext.organization_id` are now the same name end-to-end. The legacy `objectql.registerTenantMiddleware` (hardcoded `where.tenant_id` injection that pre-dated SecurityPlugin) has been removed; SecurityPlugin is the sole authority for tenant isolation. Analytics now uses the same reusable read scope via `security.getReadFilter`, so dataset-bound dashboards/reports do not bypass RLS. Verified cross-organization isolation on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_position_permission_set`. **Anonymous traffic is denied by default** (the ADR-0056 D2 default-deny flip landed: `requireAuth` defaults to `true`); an explicit `requireAuth: false` opt-out logs a boot-time warning, and public forms do not depend on any fall-open — they carry a declaration-derived `publicFormGrant` (ADR-0056 Option A).
290
-
-**OWD / sharing-model enforcement is live and proven end-to-end (ADR-0056)**: `private`, `public_read`, `public_read_write`, and `controlled_by_parent` are enforced through `plugin-sharing` + `plugin-security` and verified by dogfood proofs over the real HTTP stack. `object.sharingModel` accepts the canonical OWD vocabulary only (`private` / `public_read` / `public_read_write` / `controlled_by_parent`) — the legacy `read` / `read_write` / `full` aliases were removed from the enum (ADR-0090 D4), and an unset `sharingModel` on a custom object resolves to `private` (ADR-0090 D1). RLS owner policies resolve `current_user.email` in addition to `id` / `organization_id` / `positions` (#2054). Permission sets may declare `isDefault: true` as the install-time suggestion to bind the set to the built-in `everyone` position (ADR-0090 D5, superseding the ADR-0056 D7 fallback-profile mechanism).
290
+
- **OWD / sharing-model enforcement is live and proven end-to-end (ADR-0056)**: `private`, `public_read`, `public_read_write`, and `controlled_by_parent` are enforced through `plugin-sharing` + `plugin-security` and verified by dogfood proofs over the real HTTP stack. `object.sharingModel` accepts the canonical OWD vocabulary only (`private` / `public_read` / `public_read_write` / `controlled_by_parent`) — the legacy `read` / `read_write` / `full` aliases were removed from the enum (ADR-0090 D4), and an unset `sharingModel` on a custom object resolves to `private` (ADR-0090 D1). RLS owner policies resolve `current_user.email` in addition to `id` / `organization_id` / `positions` (#2054). Permission sets may declare `isDefault: true` as the install-time suggestion to bind the set to the built-in `everyone` position (ADR-0090 D5, superseding the ADR-0056 D7 fallback-profile mechanism). **A sharing rule must state its criteria** (#3896): all three write paths reject a match-all shape, a stored criteria-less rule matches nothing, and its materialised grants are revoked on the next reconcile.
291
291
292
292
---
293
293
@@ -419,7 +419,7 @@ describe that separate runtime.
-[x] Authorization Plugin — `@objectstack/plugin-security` enforces CRUD/FLS/RLS in the ObjectQL middleware chain; REST → ObjectQL now propagates `ExecutionContext` end-to-end (Phase-1)
422
-
-[x] Row-Level Security — tenant isolation is a Layer 0 tenant wall (`plugin-security/tenant-layer.ts`, ADR-0095 D1) that AND-composes `organization_id == current_user.organization_id` independently of business RLS (the earlier wildcard `tenant_isolation` policy on `member_default` was retired); `member_default` still applies per-object overrides `sys_organization_self` / `sys_user_self`. The earlier `tenantField` rewrite indirection was removed: RLS column, placeholder, and `RLSUserContext.organization_id` use the same canonical name end-to-end
422
+
-[x] Row-Level Security — tenant isolation is a Layer 0 tenant wall (`plugin-security/tenant-layer.ts`, ADR-0095 D1) that AND-composes `organization_id == current_user.organization_id` independently of business RLS (the earlier wildcard `tenant_isolation` policy on `member_default` was retired); `member_default` still applies per-object overrides `sys_organization_self` / `sys_user_self`. The earlier `tenantField` rewrite indirection was removed: RLS column, placeholder, and `RLSUserContext.organization_id` use the same canonical name end-to-end. Policy `enabled: false` is enforced at the compiler choke point — a disabled policy no longer contributes its OR-branch grant (#3980) — and the void `priority` key is removed (#3990)
423
423
-[x] Analytics RLS bridge — `@objectstack/service-analytics` auto-bridges to `security.getReadFilter(object, context)` and fails closed when read-scope resolution cannot be safely applied
424
424
-[x] Multi-tenancy — verified cross-organization isolation on `pnpm dev:crm` (Alice@OrgAlpha vs. Bob@OrgBeta only see their own records across `sys_organization`, `sys_member`, `sys_user`, and `sys_*_permission_set` link tables)
425
425
-[x] Legacy `objectql.registerTenantMiddleware` removed — SecurityPlugin is now the sole tenant-isolation authority
0 commit comments