Skip to content

Latest commit

 

History

History
1236 lines (894 loc) · 41.6 KB

File metadata and controls

1236 lines (894 loc) · 41.6 KB

@objectstack/plugin-security

10.3.0

Patch Changes

  • @objectstack/spec@10.3.0
  • @objectstack/core@10.3.0
  • @objectstack/formula@10.3.0
  • @objectstack/platform-objects@10.3.0

10.2.0

Patch Changes

  • Updated dependencies [b496498]
    • @objectstack/spec@10.2.0
    • @objectstack/core@10.2.0
    • @objectstack/formula@10.2.0
    • @objectstack/platform-objects@10.2.0

10.1.0

Patch Changes

  • Updated dependencies [49da36e]
  • Updated dependencies [ac79f16]
    • @objectstack/spec@10.1.0
    • @objectstack/core@10.1.0
    • @objectstack/formula@10.1.0
    • @objectstack/platform-objects@10.1.0

10.0.0

Minor Changes

  • e16f2a8: BREAKING: the system object sys_department is renamed to sys_business_unit — object + member table (sys_department_membersys_business_unit_member), fields, and i18n — with no compatibility alias. Any deployment holding sys_department rows, or metadata that references the object by name (lookups, list views, queries, sharing/approval scopes), must migrate to sys_business_unit. A renamed shipped system object is a breaking change to the platform's public data surface, so this lands as a major. Verified per ADR-0059's pre-publish hotcrm gate: no published downstream consumer references the old name.

    ADR-0057 — ERP authorization core. Adds permission-grant access DEPTH (own/own_and_reports/unit/unit_and_below/org), renames sys_departmentsys_business_unit (no aliases — see BREAKING above), introduces the platform-owned sys_user_role assignment, and seeds stack-declared roles/sharingRules into sys_role/sys_sharing_rule at boot (closes #2077). Hierarchy-relative scopes are delegated to a pluggable IHierarchyScopeResolver (open edition fails closed to owner-only; defineStack errors without requires: ['hierarchy-security']). Also fixes a latent over-grant where engine.find({ filter }) was ignored (driver reads where) — normalized filterwhere in the engine.

  • cfd86ce: ADR-0058 — expression & predicate surface unification. Adds the canonical CEL→FilterCondition pushdown compiler in @objectstack/formula (compileCelToFilter, isPushdownableCel, lowerCelAst) plus an in-memory matchesFilterCondition backend (one AST, three backends). plugin-security (RLS using, via a SQL bridge) and plugin-sharing (celToFilter) cut over to it, retiring the bespoke regex/field-equality front-ends. Compound sharing conditions now compile and enforce end-to-end (closes #1887). The RLS check clause is now enforced on the write post-image (insert/by-id update), fail-closed. Non-pushdownable predicates (arithmetic, functions, subqueries, cross-object) are an authoring compile error, never silently dropped (ADR-0049/0055).

  • 6ca20b3: ADR-0058 D1 follow-through — RLS predicates are now canonical CEL. Migrated every seeded RLS using/check (default permission sets, showcase, and the RLS.ownerPolicy/tenantPolicy/allowAllPolicy helper factories) from the legacy SQL-ish form (=, IN (...)) to pure CEL (==, in), so authors and AI learn ONE expression language. The sqlPredicateToCel bridge is retained as a DEPRECATED transitional shim: a stored SQL-style predicate still compiles (no silent deny on legacy data) but emits a deprecation warn; canonical CEL passes through as a no-op. No runtime behavior change — CEL and the old SQL form compile to the identical FilterCondition.

Patch Changes

  • Updated dependencies [d7ff626]
  • Updated dependencies [2a1b16b]
  • Updated dependencies [2256e93]
  • Updated dependencies [7108ff3]
  • Updated dependencies [30c0313]
  • Updated dependencies [e16f2a8]
  • Updated dependencies [cfd86ce]
  • Updated dependencies [e411a82]
  • Updated dependencies [ae271d0]
  • Updated dependencies [61ed5c7]
  • Updated dependencies [a581385]
  • Updated dependencies [d5f6d29]
  • Updated dependencies [220ce5b]
  • Updated dependencies [3efe334]
  • Updated dependencies [0df063e]
  • Updated dependencies [ce13bb8]
  • Updated dependencies [feead7e]
  • Updated dependencies [6ca20b3]
  • Updated dependencies [5f875fe]
  • Updated dependencies [b469950]
  • Updated dependencies [47d978a]
  • Updated dependencies [48a307a]
  • Updated dependencies [25fc0e4]
    • @objectstack/spec@10.0.0
    • @objectstack/platform-objects@10.0.0
    • @objectstack/formula@10.0.0
    • @objectstack/core@10.0.0

9.11.0

Minor Changes

  • fa8964d: feat(security): RLS predicates that won't compile are surfaced, not silently dropped (ADR-0056 D4)

    The RLS compiler previously dropped any using/check it could not parse (e.g. ==, AND/OR, ranges) in silence — if it was the only policy, the object lost protection with no signal (the class of bug that left a showcase owner predicate inert for two PRs). Now the compiler WARNS (via the security plugin's logger) when an unsupported-shape predicate is dropped, distinguishing it from the intentional "context variable absent" fail-closed skip. Also exports isSupportedRlsExpression(expr) so an authoring-time gate (objectstack compile) can reject a predicate the runtime would never enforce. No change to compiled filters for valid predicates; fail-closed semantics preserved.

  • 6595b53: feat(security): app-declarable default profile (isDefault, ADR-0056 D7)

    An app can now declare its default access posture for authenticated users who have no explicit grants, via isDefault: true on a permission set — instead of always inheriting the built-in member_default. The SecurityPlugin resolves the fallback from the isDefault profile when no explicit fallbackPermissionSet is configured (falling back to member_default when none is declared — non-breaking). This is the foundation for SSO/JIT provisioning (mapping IdP claims → a declared default profile). Proven by the showcase-default-profile dogfood test: a sign-up governed by a custom default that grants only showcase_announcement can read it but is denied showcase_private_note (which the member_default wildcard would have allowed).

  • 751f5cf: feat(security): declaration-derived public-form authorization (ADR-0056, Option A)

    Public form submissions are now authorized by the declaration, not by a deployment-configured guest_portal profile. The form-submit route derives a narrow publicFormGrant: { object } from the matched form's target object; the SecurityPlugin honors it as a least-privilege capability — create + the immediate read-back on THAT object only, with no userId, and crucially NOT the anonymous fall-open. This makes public forms work under secure-by-default (requireAuth) without a hand-configured guest_portal, scoped to exactly the declared object (the field allow-list is still enforced at the route; guest_portal/anonymous are kept on the context for back-compat with guest-detection hooks). It is the prerequisite that unblocks the eventual requireAuth default flip, and generalizes the platform principle "public access = declared + runtime-derived scoped grant" (the same shape share-links already use). Proven by form-self-auth dogfood (create on target allowed; cross-object + update/delete denied). plugin-security 108, rest 121, full dogfood 98 — no regression.

  • 5a5a9fe: feat(security): public-form demo (Option A) + app-declared default profile wiring (ADR-0056 D7)

    Wires ADR-0056's app-declarable default profile through the CLI so it actually takes effect under pnpm dev. @objectstack/plugin-security exports a new appDefaultProfileName(permissions) helper that extracts the first isProfile && isDefault profile name from a stack; @objectstack/cli (serve.ts) passes it as the SecurityPlugin fallbackPermissionSet (undefined → built-in member_default preserved, so apps that declare no default are unaffected).

    The showcase gains a working web-to-lead public form (showcase_inquiry + an allowAnonymous FormView authorized by the declaration-derived publicFormGrant, no guest_portal profile) and an app-declared default profile (showcase_member_default), each covered by a dogfood proof over the real HTTP stack.

  • 4c213c2: Master-detail "controlled by parent" permissions (ADR-0055).

    A detail object can now declare sharingModel: 'controlled_by_parent': its read/write access is derived from its master record, with no authored RLS.

    • @objectstack/spec: controlled_by_parent added to the authorable object.sharingModel enum.
    • @objectstack/plugin-security: reads inject masterFK IN (accessible master ids) (resolved from the master's own RLS, reusing the existing filter machinery — zero RLS-compiler changes); by-id writes (insert/update/delete) to a detail now require edit access to its master, closing the #1994-class by-id hole for derived access.
    • @objectstack/verify: related-record topological synthesisderiveCrudCases no longer skips objects with required relations; it builds the object dependency graph, orders it topologically, and threads real target ids, so relationship-dense objects (and the master-detail RLS proof) are verifiable. Honest blocked verdicts remain for required-reference cycles and external/missing targets.

    v1 limits (per ADR-0055): the accessible-master id set is unbounded (large-tenant scale is a documented future limit), and master-detail chains are single-level (not transitively traversed).

  • 2afb612: feat(security): resolve current_user.email in RLS owner policies

    RLS using predicates can now reference current_user.email — a unique, human-readable, seedable owner anchor (owner = current_user.email). Previously the RLS compiler resolved only current_user.id / organization_id / roles / org_user_ids, so any owner-by-name/email predicate silently compiled to the deny sentinel (fail-closed → the user saw nothing). Email is sourced for free from the auth session (with a bounded sys_user fallback for the API-key path) and threaded onto the ExecutionContext in both identity resolvers — the REST data path (rest-server) and the dispatcher path (resolve-execution-context).

    Display name is deliberately not exposed to RLS: names collide, and a collision on an ownership predicate is an access-control leak. Only unique identifiers (id, email) are resolvable.

    This makes owner-scoped row-level security work with seed data (no per-user ids needed) and, combined with controlled_by_parent (ADR-0055), lets a master's owner scoping flow to its detail records. The example-showcase demonstrates it: showcase_invoice carries an owner email + an owner RLS policy, its lines are controlled-by-parent, and invoices/lines are seeded per owner. It also fixes the showcase's previously inert owner predicates (they used == and current_user.name, neither of which the compiler accepts) to = current_user.email.

Patch Changes

  • Updated dependencies [e7f6539]
  • Updated dependencies [2365d07]
  • Updated dependencies [6595b53]
  • Updated dependencies [fa8964d]
  • Updated dependencies [36138c7]
  • Updated dependencies [a8e4f3b]
  • Updated dependencies [4c213c2]
  • Updated dependencies [2afb612]
    • @objectstack/spec@9.11.0
    • @objectstack/core@9.11.0
    • @objectstack/platform-objects@9.11.0

9.10.0

Minor Changes

  • 1f88fd9: Converge the RLS contract with the reference compiler, and wire §7.3.1 dynamic membership.

    • spec (docs): narrow rls.zod.ts to the four expression forms the compiler actually implements — field = current_user.<prop>, field = 'literal', field IN (current_user.<array>), and 1 = 1. Removed the over-promised surface (subqueries, AND/OR/NOT, LIKE/ILIKE, regex, ANY/ALL, NOT IN, IS NULL, NOW()/CURRENT_DATE) from the operator list, context-variable list, and @example policies, and documented the fail-closed behaviour explicitly.
    • spec (schema): ExecutionContext gains rlsMembership?: Record<string, string[]> — a bag of pre-resolved dynamic-membership id arrays (team members, territory accounts, shared records) that the runtime stages so RLS can scope via field IN (current_user.<key>) without subquery support. Generalizes the previously hard-coded org_user_ids.
    • plugin-security: RLSCompiler.compileFilter merges rlsMembership keys into the user context (arrays only, never clobbering the named id/organization_id/roles/org_user_ids fields), so §7.3.1 hierarchy- and sharing-based policies compile. compileExpression now recognizes 1 = 1 as always-true (empty filter), making RLS.allowAllPolicy grant access instead of silently failing closed. Missing/empty membership sets still fail closed.
  • e2b5324: feat(ownership): auto-provision a canonical owner_id and hand seeded records to the first admin

    Ownership is now correct-by-default instead of opt-in — closing the gap where seeded demo data ended up owned by nobody a human can log in as (so "My" views, owner reports and owner notifications were empty out of the box) and where author-written objects silently shipped with no working ownership at all.

    • applySystemFields (objectql) now auto-injects a canonical, reassignable owner_id lookup (→ sys_user) on user-authored business objects, alongside the existing tenant/audit fields. Unlike the audit *_by lookups it is NOT readonly — ownership transfers. Withheld for managedBy / sys_* tables and for objects that opt out via ownership: 'org' | 'none' (Dataverse-style). The safe default direction: forgetting the opt-out leaves a harmless spare column, whereas the old opt-IN model let authors ship objects with broken ownership. Once present, the existing machinery engages automatically (insert auto-stamp, owner-scoped RLS, owner-keyed views/reports).

    • claimSeedOwnership (plugin-security), invoked from bootstrapPlatformAdmin right after the first human is promoted to platform admin, transfers ownership of seeded rows (owner_id NULL or usr_system) to that admin. The ownership twin of org-scoping's claimOrphanOrgRows. Idempotent; skips managedBy / sys_*. Authors write plain seed records (no owner_id) and the platform — not the author — performs the handoff, so there is nothing to remember or mistype.

    • usr_system is never minted (runtime + objectql). The seed loader binds os.user to a NULL identity, so celos.user.id``resolves to NULL at seed time (the owning admin does not exist yet) and the row seeds NULL-owned — then the handoff above fills it. The runtime'sensureSeedIdentity(the only code that inserted a`usr_system`row) is removed.`SystemUserId.SYSTEM`survives only as a reserved id so legacy DBs' exclusion guards / ownership handoff still recognize a pre-existing row.`os.org`is unaffected (derived from`organizationId`).

    Also hardens bootstrapPlatformAdmin against a latent dts typecheck error (defensive read of the untyped description on seed permission sets).

Patch Changes

  • Updated dependencies [db02bd5]
  • Updated dependencies [641675d]
  • Updated dependencies [94e9040]
  • Updated dependencies [4331adb]
  • Updated dependencies [1f88fd9]
  • Updated dependencies [1f88fd9]
    • @objectstack/spec@9.10.0
    • @objectstack/platform-objects@9.10.0
    • @objectstack/core@9.10.0

9.9.1

Patch Changes

  • @objectstack/spec@9.9.1
  • @objectstack/core@9.9.1
  • @objectstack/platform-objects@9.9.1

9.9.0

Minor Changes

  • 92d75ca: fix(security): enforce row-level security on by-id writes — close the member-can-edit-others'-records hole (#1985).

    A single-id update/delete goes straight to driver.update(object, id, …) / driver.delete(object, id) and builds no query ast, so the RLS where filter the middleware injects on the read path was never applied to by-id writes. Combined with member_default granting *: { edit, delete } (scoped, by design, via the owner_only_writes/deletes RLS), this meant the owner predicate was silently bypassed: any authenticated member could modify or delete another user's records (verified end-to-end — a member PATCH'd an admin's record and the change persisted).

    Two coordinated changes:

    • Enforce a pre-image authorization check. Before a single-id update/delete, the security middleware computes the write-operation RLS filter and re-reads the target row with { id } AND <writeFilter>; if the row isn't visible (someone else's, or RLS-hidden) it throws PermissionDeniedError (403). Reuses the existing RLS/tenant machinery, is recursion-safe (a find doesn't trigger the check), and is skipped when no RLS policy applies (e.g. admin sets, modifyAllRecords) so admins and unguarded objects are unchanged.
    • Repoint owner scoping to a column that exists. owner_only_writes/owner_only_deletes keyed on owner_id, which author-defined objects almost never declare — so the policy referenced a missing column and computeRlsFilter dropped it (the no-op that made the bypass invisible). Now keyed on created_by, the ownership column the engine stamps on every object.

    Result: a member may edit/delete the records they created, not others'; admins (and any set with modifyAllRecords or no RLS) are unrestricted. Objects that opt out of audit fields (systemFields.audit: false) have no created_by and now fail closed for member writes (grant modifyAllRecords or a per-object policy to allow). Objects modeling transferable ownership should override with a per-object owner policy.

    Verified live on app-crm (2 users): member→others' record PATCH/DELETE = 403 (unmutated); member→own = 200; admin→any = 200. Note: cross-tenant write isolation additionally depends on an organization being assigned at sign-up (tracked separately in #1985).

Patch Changes

  • Updated dependencies [84249a4]
  • Updated dependencies [11af299]
  • Updated dependencies [d5774b5]
  • Updated dependencies [134043a]
  • Updated dependencies [90108e0]
  • Updated dependencies [9afeb2d]
  • Updated dependencies [6bec07e]
  • Updated dependencies [601cc11]
  • Updated dependencies [575448d]
    • @objectstack/spec@9.9.0
    • @objectstack/core@9.9.0
    • @objectstack/platform-objects@9.9.0

9.8.0

Patch Changes

  • Updated dependencies [97c55b3]
  • Updated dependencies [1b1f490]
    • @objectstack/spec@9.8.0
    • @objectstack/core@9.8.0
    • @objectstack/platform-objects@9.8.0

9.7.0

Patch Changes

  • @objectstack/spec@9.7.0
  • @objectstack/core@9.7.0
  • @objectstack/platform-objects@9.7.0

9.6.0

Patch Changes

  • Updated dependencies [d1e930a]
  • Updated dependencies [71578f2]
  • Updated dependencies [5e3a301]
  • Updated dependencies [5db2742]
    • @objectstack/spec@9.6.0
    • @objectstack/core@9.6.0
    • @objectstack/platform-objects@9.6.0

9.5.1

Patch Changes

  • Updated dependencies [ee72aae]
    • @objectstack/spec@9.5.1
    • @objectstack/core@9.5.1
    • @objectstack/platform-objects@9.5.1

9.5.0

Patch Changes

  • Updated dependencies [d08551c]
  • Updated dependencies [5be7102]
  • Updated dependencies [707aeed]
  • Updated dependencies [7a103d4]
  • Updated dependencies [4b01250]
    • @objectstack/spec@9.5.0
    • @objectstack/platform-objects@9.5.0
    • @objectstack/core@9.5.0

9.4.0

Patch Changes

  • Updated dependencies [060467a]
  • Updated dependencies [0856476]
  • Updated dependencies [b678d8c]
  • Updated dependencies [b678d8c]
  • Updated dependencies [b678d8c]
    • @objectstack/spec@9.4.0
    • @objectstack/core@9.4.0
    • @objectstack/platform-objects@9.4.0

9.3.0

Patch Changes

  • Updated dependencies [1ada658]
  • Updated dependencies [3219191]
  • Updated dependencies [290f631]
  • Updated dependencies [50b7b47]
  • Updated dependencies [f15d6f6]
  • Updated dependencies [f8684ea]
  • Updated dependencies [c802327]
  • Updated dependencies [b4765be]
    • @objectstack/spec@9.3.0
    • @objectstack/platform-objects@9.3.0
    • @objectstack/core@9.3.0

9.2.0

Patch Changes

  • Updated dependencies [2f57b75]
  • Updated dependencies [2f57b75]
    • @objectstack/spec@9.2.0
    • @objectstack/core@9.2.0
    • @objectstack/platform-objects@9.2.0

9.1.0

Patch Changes

  • Updated dependencies [b9062c9]
    • @objectstack/spec@9.1.0
    • @objectstack/core@9.1.0
    • @objectstack/platform-objects@9.1.0

9.0.1

Patch Changes

  • Updated dependencies [1817845]
    • @objectstack/spec@9.0.1
    • @objectstack/core@9.0.1
    • @objectstack/platform-objects@9.0.1

9.0.0

Patch Changes

  • Updated dependencies [4c3f693]
  • Updated dependencies [0bf39f1]
  • Updated dependencies [f533f42]
  • Updated dependencies [1c83ee8]
    • @objectstack/spec@9.0.0
    • @objectstack/core@9.0.0
    • @objectstack/platform-objects@9.0.0

8.0.1

Patch Changes

  • @objectstack/spec@8.0.1
  • @objectstack/core@8.0.1
  • @objectstack/platform-objects@8.0.1

8.0.0

Patch Changes

  • 1e8b680: fix(security): close four P0 launch-readiness findings

    • plugin-auth (P0-1): generateSecret() now throws (fails boot) when no OS_AUTH_SECRET is set and NODE_ENV==='production', instead of silently falling back to a predictable dev-secret-<timestamp> (session forgery). The dev/test fallback is unchanged.
    • plugin-security (P0-2): the permission-resolution catch now fails closed — it logs at ERROR and throws PermissionDeniedError rather than return next(). A degraded metadata service can no longer let every authenticated request bypass RBAC/RLS. System operations still bypass as before.
    • driver-sql (P0-3): the contains / $contains operator now escapes LIKE metacharacters (% / _ / \) in the user value and binds an explicit ESCAPE '\', so a value of % matches literally instead of every row (filter bypass). Correct across SQLite/MySQL/Postgres.
    • driver-mongodb (P0-4): the field-operator translator now rejects unknown $-operators instead of passing them through, blocking $where / $function / $expr (server-side JS execution / query-intent bypass). All legitimate ObjectQL operators remain allowlisted.

    +12 regression tests across the four packages.

  • Updated dependencies [a46c017]

  • Updated dependencies [b990b89]

  • Updated dependencies [99111ec]

  • Updated dependencies [d5a8161]

  • Updated dependencies [5cf1f1b]

  • Updated dependencies [9ef89d4]

  • Updated dependencies [3306d2f]

  • Updated dependencies [c262301]

  • Updated dependencies [bc44195]

  • Updated dependencies [9e2e229]

    • @objectstack/spec@8.0.0
    • @objectstack/core@8.0.0
    • @objectstack/platform-objects@8.0.0

7.9.0

Patch Changes

  • @objectstack/spec@7.9.0
  • @objectstack/core@7.9.0
  • @objectstack/platform-objects@7.9.0

7.8.0

Patch Changes

  • Updated dependencies [06f2bbb]
  • Updated dependencies [36719db]
  • Updated dependencies [424ab26]
    • @objectstack/spec@7.8.0
    • @objectstack/core@7.8.0
    • @objectstack/platform-objects@7.8.0

7.7.0

Patch Changes

  • Updated dependencies [b391955]
  • Updated dependencies [f06b64e]
  • Updated dependencies [023bf93]
  • Updated dependencies [764c747]
    • @objectstack/spec@7.7.0
    • @objectstack/platform-objects@7.7.0
    • @objectstack/core@7.7.0

7.6.0

Patch Changes

  • Updated dependencies [955d4c8]
  • Updated dependencies [c4a4cbd]
  • Updated dependencies [b046ec2]
  • Updated dependencies [2170ad9]
  • Updated dependencies [02d6359]
  • Updated dependencies [7648242]
  • Updated dependencies [8fa1e7f]
  • Updated dependencies [7ae6abc]
  • Updated dependencies [55866f5]
  • Updated dependencies [60f9c45]
    • @objectstack/spec@7.6.0
    • @objectstack/platform-objects@7.6.0
    • @objectstack/core@7.6.0

7.5.0

Patch Changes

  • @objectstack/spec@7.5.0
  • @objectstack/core@7.5.0
  • @objectstack/platform-objects@7.5.0

7.4.1

Patch Changes

  • @objectstack/spec@7.4.1
  • @objectstack/core@7.4.1
  • @objectstack/platform-objects@7.4.1

7.4.0

Minor Changes

  • e478e0c: ADR-0029 K2 — security domain ownership (RBAC + sharing) + Setup nav contributions.

    Moves the security objects out of the @objectstack/platform-objects monolith into the two capability plugins that already register and operate them, split by concern (the two are orthogonal — sharing objects never reference RBAC objects):

    • @objectstack/plugin-security (RBAC) gains sys_role, sys_permission_set, sys_user_permission_set, sys_role_permission_set, and the defaultPermissionSets seed (which its bootstrap-platform-admin already consumes). The RBAC + default-permission-set tests move with them.
    • @objectstack/plugin-sharing gains sys_record_share, sys_sharing_rule, sys_share_link.
    • @objectstack/platform-objects no longer defines/exports any security objects; the /security subpath is now an empty barrel. Runtime is unchanged (both plugins already registered these objects at runtime).

    D7 navigation — the Setup app's group_access_control is now assembled from three sources: plugin-security contributes Roles / Permission Sets (priority 100), plugin-sharing contributes Sharing Rules / Record Shares (priority 200), and platform-objects keeps only API Keys (sys_api_key, an identity object, priority 300) — preserving the original menu order.

    i18n (D8) — the objects are removed from the platform-objects i18n extract config; existing generated bundles keep working at runtime (object-name keyed). Migrating the i18n extraction to the owning plugins remains the tracked follow-up.

Patch Changes

  • 4404572: ADR-0029 D8 — migrate i18n ownership for the moved domains to their plugins.

    The object translations for the domains decomposed in K2.a/K2.b/K2 previously lived in the @objectstack/platform-objects generated bundles even though the objects now live in their capability plugins. This moves each domain's i18n extraction + bundles to the owning plugin, preserving every hand-translated string (zh-CN / ja-JP / es-ES):

    • Each plugin gains a build-time scripts/i18n-extract.config.ts and a src/translations/ bundle ({locale}.objects.generated.ts + an index.ts barrel), generated with os i18n extract and self-baselined so re-runs preserve translations.
    • Each plugin loads its bundle at runtime on kernel:ready via i18n.loadTranslations (the i18n service is optional — load is best-effort).
      • plugin-webhookssys_webhook, sys_webhook_delivery
      • plugin-approvalssys_approval_request, sys_approval_action
      • plugin-securitysys_role, sys_permission_set, sys_user_permission_set, sys_role_permission_set
      • plugin-sharingsys_record_share, sys_sharing_rule, sys_share_link
    • @objectstack/platform-objects translation bundles are regenerated to drop those objects' keys (its extract config already excluded them); all other objects' translations and the metadata-form bundles are preserved.

    Net runtime effect is unchanged (same translations load, now contributed by the package that owns each object) — closing the D8 follow-up tracked since K2.a.

  • 08fbbb4: Fix: the first-boot platform-admin promotion no longer gets stolen by the usr_system seed identity, and the dev seed admin uses fixed, well-known credentials.

    @objectstack/plugin-securitybootstrapPlatformAdmin skips the system user

    5e831dea3 (#1392) added ensureSeedIdentity to the runtime SeedLoader, which upserts a non-loginable system identity (usr_system, role system, system@objectstack.local) to own seeded records — created before the first human sign-up. Because bootstrapPlatformAdmin promoted the earliest-created sys_user, on any app that ships seed data usr_system won the promotion and the real admin login stayed at role: user. Login succeeded but Setup and Studio (gated by setup.access / studio.access on admin_full_access) were invisible — a silent, confusing regression.

    bootstrap-platform-admin.ts now filters out the system account (id === SystemUserId.SYSTEM || role === 'system') when picking the first user to promote, and the "an admin already exists" short-circuit ignores any admin_full_access grant held by usr_system — so a database where it was wrongly promoted self-heals on the next boot.

    @objectstack/clios dev seeds admin@objectos.ai / admin123

    The --admin-email / --admin-password defaults changed from admin@dev.local / admin12345 to the fixed, well-known admin@objectos.ai / admin123, so tooling and docs never have to guess the seeded credentials. Override with --admin-email / --admin-password.

  • Updated dependencies [23c7107]

  • Updated dependencies [c72daad]

  • Updated dependencies [4404572]

  • Updated dependencies [eea3f1b]

  • Updated dependencies [e478e0c]

  • Updated dependencies [4cc2ced]

  • Updated dependencies [13632b1]

  • Updated dependencies [f115182]

  • Updated dependencies [2faf9f2]

  • Updated dependencies [2faf9f2]

  • Updated dependencies [2faf9f2]

  • Updated dependencies [58b450b]

  • Updated dependencies [82eb6cf]

  • Updated dependencies [c381977]

  • Updated dependencies [13d8653]

  • Updated dependencies [ff3d006]

  • Updated dependencies [5e831de]

    • @objectstack/spec@7.4.0
    • @objectstack/platform-objects@7.4.0
    • @objectstack/core@7.4.0

7.3.0

Patch Changes

  • Updated dependencies [5e7c554]
    • @objectstack/spec@7.3.0
    • @objectstack/core@7.3.0
    • @objectstack/platform-objects@7.3.0

7.2.1

Patch Changes

  • @objectstack/spec@7.2.1
  • @objectstack/core@7.2.1
  • @objectstack/platform-objects@7.2.1

7.2.0

Patch Changes

  • @objectstack/spec@7.2.0
  • @objectstack/core@7.2.0
  • @objectstack/platform-objects@7.2.0

7.1.0

Patch Changes

  • Updated dependencies [6228609]
  • Updated dependencies [47a92f4]
    • @objectstack/platform-objects@7.1.0
    • @objectstack/spec@7.1.0
    • @objectstack/core@7.1.0

7.0.0

Major Changes

  • 3a630b6: Split organization-scoping from @objectstack/plugin-security into a new @objectstack/plugin-org-scoping package.

    Per ADR-0002, "tenant" in ObjectStack means physical isolation (one Environment = one database, handled by @objectstack/driver-turso's multi-tenant router). The row-level organization_id scoping that previously lived inside SecurityPlugin is a different concept — logical scoping inside a single DB — and now ships as its own plugin.

    Breaking changes — @objectstack/plugin-security

    • Removed the multiTenant constructor option. SecurityPlugin no longer touches organization_id on insert and no longer registers the sys_organization post-create seed pipeline.
    • Wildcard current_user.organization_id RLS policies in the default permission sets are now stripped UNLESS the new org-scoping service is registered (i.e. unless OrgScopingPlugin is also installed).
    • Removed export cloneTenantSeedData (now exposed as cloneOrgSeedData from @objectstack/plugin-org-scoping).
    • bootstrapPlatformAdmin() no longer accepts a multiTenant flag and no longer auto-creates a default organization — that behavior moved to ensureDefaultOrganization() in the new plugin.

    Migration

    Single-tenant deployments — no action required.

    Multi-tenant deployments (previously new SecurityPlugin({ multiTenant: true })):

    + import { OrgScopingPlugin } from '@objectstack/plugin-org-scoping';
      import { SecurityPlugin } from '@objectstack/plugin-security';
    
    + await kernel.use(new OrgScopingPlugin());     // MUST be BEFORE SecurityPlugin
    - await kernel.use(new SecurityPlugin({ multiTenant: true }));
    + await kernel.use(new SecurityPlugin());

    The runtime's OS_MULTI_TENANT env switch — read by @objectstack/runtime/cloud/ArtifactKernelFactory, @objectstack/plugin-dev, and the objectstack CLI's serve / dev / start commands — automatically registers OrgScopingPlugin when set to true, so projects driven by the CLI need no code changes.

Patch Changes

  • Updated dependencies [74470ad]
  • Updated dependencies [d29617e]
  • Updated dependencies [dc72172]
  • Updated dependencies [d29617e]
  • Updated dependencies [010757b]
  • Updated dependencies [257954d]
    • @objectstack/spec@7.0.0
    • @objectstack/platform-objects@7.0.0
    • @objectstack/core@7.0.0

6.9.0

Patch Changes

  • @objectstack/spec@6.9.0
  • @objectstack/core@6.9.0
  • @objectstack/platform-objects@6.9.0

6.8.1

Patch Changes

  • @objectstack/spec@6.8.1
  • @objectstack/core@6.8.1
  • @objectstack/platform-objects@6.8.1

6.8.0

Patch Changes

  • Updated dependencies [6e88f77]
  • Updated dependencies [c8b9f57]
  • Updated dependencies [45d27c5]
    • @objectstack/spec@6.8.0
    • @objectstack/platform-objects@6.8.0
    • @objectstack/core@6.8.0

6.7.1

Patch Changes

  • @objectstack/spec@6.7.1
  • @objectstack/core@6.7.1
  • @objectstack/platform-objects@6.7.1

6.7.0

Patch Changes

  • Updated dependencies [430067b]
  • Updated dependencies [4f9e9d4]
  • Updated dependencies [4f9e9d4]
    • @objectstack/spec@6.7.0
    • @objectstack/platform-objects@6.7.0
    • @objectstack/core@6.7.0

6.6.0

Patch Changes

  • Updated dependencies [a49cfc2]
    • @objectstack/spec@6.6.0
    • @objectstack/core@6.6.0
    • @objectstack/platform-objects@6.6.0

6.5.1

Patch Changes

  • @objectstack/spec@6.5.1
  • @objectstack/core@6.5.1
  • @objectstack/platform-objects@6.5.1

6.5.0

Patch Changes

  • @objectstack/spec@6.5.0
  • @objectstack/core@6.5.0
  • @objectstack/platform-objects@6.5.0

6.4.0

Patch Changes

  • Updated dependencies [f8651cc]
  • Updated dependencies [f8651cc]
  • Updated dependencies [0bf6f9a]
    • @objectstack/spec@6.4.0
    • @objectstack/core@6.4.0
    • @objectstack/platform-objects@6.4.0

6.3.0

Patch Changes

  • @objectstack/spec@6.3.0
  • @objectstack/core@6.3.0
  • @objectstack/platform-objects@6.3.0

6.2.0

Patch Changes

  • Updated dependencies [b4c74a9]
    • @objectstack/spec@6.2.0
    • @objectstack/core@6.2.0
    • @objectstack/platform-objects@6.2.0

6.1.1

Patch Changes

  • @objectstack/spec@6.1.1
  • @objectstack/core@6.1.1
  • @objectstack/platform-objects@6.1.1

6.1.0

Patch Changes

  • Updated dependencies [93c0589]
    • @objectstack/spec@6.1.0
    • @objectstack/core@6.1.0
    • @objectstack/platform-objects@6.1.0

6.0.0

Patch Changes

  • Updated dependencies [629a716]
  • Updated dependencies [dbc4f7d]
  • Updated dependencies [944f187]
    • @objectstack/spec@6.0.0
    • @objectstack/platform-objects@6.0.0
    • @objectstack/core@6.0.0

5.2.0

Patch Changes

  • b806f58: Scope sys_user visibility to fellow organization members.

    The default RLS policy on sys_user was id = current_user.id, which meant @-mention pickers, owner/assignee lookups, reviewer selectors and the user roster all returned just the current user. The RLS compiler doesn't support subqueries, so a id IN (SELECT user_id FROM sys_member ...) policy isn't expressible.

    This change:

    1. Pre-resolves org_user_ids (the IDs of all users in the active org) into ExecutionContext in all three REST entry-point resolvers (@objectstack/rest, @objectstack/runtime, @objectstack/plugin-hono-server).
    2. Adds the field to ExecutionContextSchema so it survives Zod parsing.
    3. Adds an org_user_ids field to the RLS compiler's user context.
    4. Adds a new sys_user_org_members policy (id IN (current_user.org_user_ids)) to both member_default and viewer_readonly permission sets, alongside the existing sys_user_self policy. The RLS compiler OR-combines them, so users see themselves AND their org collaborators.

    Capped at 1000 members per request. Large enterprises should plug in a directory cache or split per workspace.

  • Updated dependencies [bab2b20]

  • Updated dependencies [fa011d8]

  • Updated dependencies [f0f7c27]

  • Updated dependencies [b806f58]

    • @objectstack/platform-objects@5.2.0
    • @objectstack/spec@5.2.0
    • @objectstack/core@5.2.0

5.1.0

Patch Changes

  • Updated dependencies [75f4ee6]
  • Updated dependencies [823d559]
    • @objectstack/spec@5.1.0
    • @objectstack/platform-objects@5.1.0
    • @objectstack/core@5.1.0

5.0.0

Patch Changes

  • Updated dependencies [888a5c1]
  • Updated dependencies [2f9073a]
    • @objectstack/platform-objects@5.0.0
    • @objectstack/spec@5.0.0
    • @objectstack/core@5.0.0

4.2.0

Patch Changes

  • Updated dependencies [2869891]
    • @objectstack/spec@4.2.0
    • @objectstack/core@4.2.0
    • @objectstack/platform-objects@4.2.0

4.1.1

Patch Changes

  • @objectstack/spec@4.1.1
  • @objectstack/core@4.1.1
  • @objectstack/platform-objects@4.1.1

4.1.0

Minor Changes

  • d3b455f: Add server-side Field-Level Security write enforcement. Client-side ObjectForm / inline-grid already hides non-editable fields, but the SecurityPlugin middleware previously only enforced FLS on read (maskResults on find/findOne). Insert and update operations could target any field — a hand-crafted POST bypassed FLS entirely.

    The middleware now runs FieldMasker.detectForbiddenWrites on every insert / update payload (single record or bulk array) and throws PermissionDeniedError (HTTP 403) when the payload references a field the caller is not permitted to edit. The offending field list is exposed via details.forbiddenFields for actionable client error UI.

    Allow-list semantics: only fields explicitly enumerated in a permission set's fields map are constrained. System operations (ExecutionContext.isSystem) continue to bypass the check.

    Why throw vs. silently stripping: silent strip hides the boundary from honest clients (partial-save confusion) AND gives probing clients no signal that the field exists. Throwing makes the boundary observable in both directions.

    Also exposes FieldMasker.detectForbiddenWrites(data, fieldPermissions) as a standalone helper for callers that want to do the check out-of-band (e.g., adapters that strip-then-warn instead of fail-closed).

Patch Changes

  • Updated dependencies [2108c30]
  • Updated dependencies [23db640]
    • @objectstack/spec@4.1.0
    • @objectstack/core@4.1.0
    • @objectstack/platform-objects@4.1.0

4.0.5

Patch Changes

  • 15e0df6: chore: unify all package versions to a single patch release
  • Updated dependencies [15e0df6]
    • @objectstack/spec@4.0.5
    • @objectstack/core@4.0.5
    • @objectstack/platform-objects@4.0.5

4.0.4

Patch Changes

  • Updated dependencies [326b66b]
    • @objectstack/spec@4.0.4
    • @objectstack/core@4.0.4

4.0.3

Patch Changes

  • @objectstack/spec@4.0.3
  • @objectstack/core@4.0.3

4.0.2

Patch Changes

  • Updated dependencies [5f659e9]
    • @objectstack/spec@4.0.2
    • @objectstack/core@4.0.2

4.0.0

Patch Changes

  • Updated dependencies [f08ffc3]
  • Updated dependencies [e0b0a78]
    • @objectstack/spec@4.0.0
    • @objectstack/core@4.0.0

3.3.1

Patch Changes

  • @objectstack/spec@3.3.1
  • @objectstack/core@3.3.1

3.3.0

Patch Changes

  • @objectstack/spec@3.3.0
  • @objectstack/core@3.3.0

3.2.9

Patch Changes

  • @objectstack/spec@3.2.9
  • @objectstack/core@3.2.9

3.2.8

Patch Changes

  • @objectstack/spec@3.2.8
  • @objectstack/core@3.2.8

3.2.7

Patch Changes

  • @objectstack/spec@3.2.7
  • @objectstack/core@3.2.7

3.2.6

Patch Changes

  • @objectstack/spec@3.2.6
  • @objectstack/core@3.2.6

3.2.5

Patch Changes

  • @objectstack/spec@3.2.5
  • @objectstack/core@3.2.5

3.2.4

Patch Changes

  • @objectstack/spec@3.2.4
  • @objectstack/core@3.2.4

3.2.3

Patch Changes

  • @objectstack/spec@3.2.3
  • @objectstack/core@3.2.3

3.2.2

Patch Changes

  • Updated dependencies [46defbb]
    • @objectstack/spec@3.2.2
    • @objectstack/core@3.2.2

3.2.1

Patch Changes

  • Updated dependencies [850b546]
    • @objectstack/spec@3.2.1
    • @objectstack/core@3.2.1

3.2.0

Patch Changes

  • Updated dependencies [5901c29]
    • @objectstack/spec@3.2.0
    • @objectstack/core@3.2.0

3.1.1

Patch Changes

  • Updated dependencies [953d667]
    • @objectstack/spec@3.1.1
    • @objectstack/core@3.1.1

3.1.0

Patch Changes

  • Updated dependencies [0088830]
    • @objectstack/spec@3.1.0
    • @objectstack/core@3.1.0

3.0.11

Patch Changes

  • Updated dependencies [92d9d99]
    • @objectstack/spec@3.0.11
    • @objectstack/core@3.0.11

3.0.10

Patch Changes

  • Updated dependencies [d1e5d31]
    • @objectstack/spec@3.0.10
    • @objectstack/core@3.0.10

3.0.9

Patch Changes

  • Updated dependencies [15e0df6]
    • @objectstack/spec@3.0.9
    • @objectstack/core@3.0.9

3.0.8

Patch Changes

  • Updated dependencies [5a968a2]
    • @objectstack/spec@3.0.8
    • @objectstack/core@3.0.8

3.0.7

Patch Changes

  • Updated dependencies [0119bd7]
  • Updated dependencies [5426bdf]
    • @objectstack/spec@3.0.7
    • @objectstack/core@3.0.7

3.0.6

Patch Changes

  • Updated dependencies [5df254c]
    • @objectstack/spec@3.0.6
    • @objectstack/core@3.0.6

3.0.5

Patch Changes

  • Updated dependencies [23a4a68]
    • @objectstack/spec@3.0.5
    • @objectstack/core@3.0.5

3.0.4

Patch Changes

  • Updated dependencies [d738987]
    • @objectstack/spec@3.0.4
    • @objectstack/core@3.0.4

3.0.3

Patch Changes

  • c7267f6: Patch release for maintenance updates and improvements.
  • Updated dependencies [c7267f6]
    • @objectstack/spec@3.0.3
    • @objectstack/core@3.0.3

3.0.2

Patch Changes

  • Updated dependencies [28985f5]
    • @objectstack/spec@3.0.2
    • @objectstack/core@3.0.2

3.0.1

Patch Changes

  • Updated dependencies [389725a]
    • @objectstack/spec@3.0.1
    • @objectstack/core@3.0.1

3.0.0

Major Changes

  • Release v3.0.0 — unified version bump for all ObjectStack packages.

Patch Changes

  • Updated dependencies
    • @objectstack/spec@3.0.0
    • @objectstack/core@3.0.0

2.0.7

Patch Changes

  • Updated dependencies
    • @objectstack/spec@2.0.7
    • @objectstack/core@2.0.7

2.0.6

Patch Changes

  • Patch release for maintenance and stability improvements
  • Updated dependencies
    • @objectstack/spec@2.0.6
    • @objectstack/core@2.0.6

2.0.5

Patch Changes

  • Unify all package versions with a patch release
  • Updated dependencies
    • @objectstack/spec@2.0.5
    • @objectstack/core@2.0.5