- @objectstack/spec@10.3.0
- @objectstack/core@10.3.0
- @objectstack/formula@10.3.0
- @objectstack/platform-objects@10.3.0
- Updated dependencies [b496498]
- @objectstack/spec@10.2.0
- @objectstack/core@10.2.0
- @objectstack/formula@10.2.0
- @objectstack/platform-objects@10.2.0
- 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
-
e16f2a8: BREAKING: the system object
sys_departmentis renamed tosys_business_unit— object + member table (sys_department_member→sys_business_unit_member), fields, and i18n — with no compatibility alias. Any deployment holdingsys_departmentrows, or metadata that references the object by name (lookups, list views, queries, sharing/approval scopes), must migrate tosys_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), renamessys_department→sys_business_unit(no aliases — see BREAKING above), introduces the platform-ownedsys_user_roleassignment, and seeds stack-declaredroles/sharingRulesintosys_role/sys_sharing_ruleat boot (closes #2077). Hierarchy-relative scopes are delegated to a pluggableIHierarchyScopeResolver(open edition fails closed to owner-only;defineStackerrors withoutrequires: ['hierarchy-security']). Also fixes a latent over-grant whereengine.find({ filter })was ignored (driver readswhere) — normalizedfilter→wherein 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-memorymatchesFilterConditionbackend (one AST, three backends).plugin-security(RLSusing, via a SQL bridge) andplugin-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 RLScheckclause 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 theRLS.ownerPolicy/tenantPolicy/allowAllPolicyhelper factories) from the legacy SQL-ish form (=,IN (...)) to pure CEL (==,in), so authors and AI learn ONE expression language. ThesqlPredicateToCelbridge 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.
- 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
-
fa8964d: feat(security): RLS predicates that won't compile are surfaced, not silently dropped (ADR-0056 D4)
The RLS compiler previously dropped any
using/checkit 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 exportsisSupportedRlsExpression(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: trueon a permission set — instead of always inheriting the built-inmember_default. The SecurityPlugin resolves the fallback from theisDefaultprofile when no explicitfallbackPermissionSetis configured (falling back tomember_defaultwhen none is declared — non-breaking). This is the foundation for SSO/JIT provisioning (mapping IdP claims → a declared default profile). Proven by theshowcase-default-profiledogfood test: a sign-up governed by a custom default that grants onlyshowcase_announcementcan read it but is deniedshowcase_private_note(which themember_defaultwildcard 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_portalprofile. The form-submit route derives a narrowpublicFormGrant: { 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-configuredguest_portal, scoped to exactly the declared object (the field allow-list is still enforced at the route;guest_portal/anonymousare kept on the context for back-compat with guest-detection hooks). It is the prerequisite that unblocks the eventualrequireAuthdefault flip, and generalizes the platform principle "public access = declared + runtime-derived scoped grant" (the same shape share-links already use). Proven byform-self-authdogfood (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-securityexports a newappDefaultProfileName(permissions)helper that extracts the firstisProfile && isDefaultprofile name from a stack;@objectstack/cli(serve.ts) passes it as the SecurityPluginfallbackPermissionSet(undefined → built-inmember_defaultpreserved, so apps that declare no default are unaffected).The showcase gains a working web-to-lead public form (
showcase_inquiry+ anallowAnonymousFormView authorized by the declaration-derivedpublicFormGrant, noguest_portalprofile) 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_parentadded to the authorableobject.sharingModelenum.@objectstack/plugin-security: reads injectmasterFK 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 synthesis —deriveCrudCasesno 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. Honestblockedverdicts 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.emailin RLS owner policiesRLS
usingpredicates can now referencecurrent_user.email— a unique, human-readable, seedable owner anchor (owner = current_user.email). Previously the RLS compiler resolved onlycurrent_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 boundedsys_userfallback for the API-key path) and threaded onto theExecutionContextin both identity resolvers — the REST data path (rest-server) and the dispatcher path (resolve-execution-context).Display
nameis 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_invoicecarries anowneremail + 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==andcurrent_user.name, neither of which the compiler accepts) to= current_user.email.
- 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
-
1f88fd9: Converge the RLS contract with the reference compiler, and wire §7.3.1 dynamic membership.
- spec (docs): narrow
rls.zod.tsto the four expression forms the compiler actually implements —field = current_user.<prop>,field = 'literal',field IN (current_user.<array>), and1 = 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@examplepolicies, and documented the fail-closed behaviour explicitly. - spec (schema):
ExecutionContextgainsrlsMembership?: 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 viafield IN (current_user.<key>)without subquery support. Generalizes the previously hard-codedorg_user_ids. - plugin-security:
RLSCompiler.compileFiltermergesrlsMembershipkeys into the user context (arrays only, never clobbering the namedid/organization_id/roles/org_user_idsfields), so §7.3.1 hierarchy- and sharing-based policies compile.compileExpressionnow recognizes1 = 1as always-true (empty filter), makingRLS.allowAllPolicygrant access instead of silently failing closed. Missing/empty membership sets still fail closed.
- spec (docs): narrow
-
e2b5324: feat(ownership): auto-provision a canonical
owner_idand hand seeded records to the first adminOwnership 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, reassignableowner_idlookup (→sys_user) on user-authored business objects, alongside the existing tenant/audit fields. Unlike the audit*_bylookups it is NOT readonly — ownership transfers. Withheld formanagedBy/sys_*tables and for objects that opt out viaownership: '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 frombootstrapPlatformAdminright after the first human is promoted to platform admin, transfers ownership of seeded rows (owner_idNULL orusr_system) to that admin. The ownership twin of org-scoping'sclaimOrphanOrgRows. Idempotent; skipsmanagedBy/sys_*. Authors write plain seed records (noowner_id) and the platform — not the author — performs the handoff, so there is nothing to remember or mistype. -
usr_systemis never minted (runtime + objectql). The seed loader bindsos.userto a NULL identity, socelos.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
bootstrapPlatformAdminagainst a latent dts typecheck error (defensive read of the untypeddescriptionon seed permission sets). -
- 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
- @objectstack/spec@9.9.1
- @objectstack/core@9.9.1
- @objectstack/platform-objects@9.9.1
-
92d75ca: fix(security): enforce row-level security on by-id writes — close the member-can-edit-others'-records hole (#1985).
A single-id
update/deletegoes straight todriver.update(object, id, …)/driver.delete(object, id)and builds no queryast, so the RLSwherefilter the middleware injects on the read path was never applied to by-id writes. Combined withmember_defaultgranting*: { edit, delete }(scoped, by design, via theowner_only_writes/deletesRLS), 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 throwsPermissionDeniedError(403). Reuses the existing RLS/tenant machinery, is recursion-safe (afinddoesn'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_deleteskeyed onowner_id, which author-defined objects almost never declare — so the policy referenced a missing column andcomputeRlsFilterdropped it (the no-op that made the bypass invisible). Now keyed oncreated_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
modifyAllRecordsor no RLS) are unrestricted. Objects that opt out of audit fields (systemFields.audit: false) have nocreated_byand now fail closed for member writes (grantmodifyAllRecordsor 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).
- Enforce a pre-image authorization check. Before a single-id
- 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
- Updated dependencies [97c55b3]
- Updated dependencies [1b1f490]
- @objectstack/spec@9.8.0
- @objectstack/core@9.8.0
- @objectstack/platform-objects@9.8.0
- @objectstack/spec@9.7.0
- @objectstack/core@9.7.0
- @objectstack/platform-objects@9.7.0
- 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
- Updated dependencies [ee72aae]
- @objectstack/spec@9.5.1
- @objectstack/core@9.5.1
- @objectstack/platform-objects@9.5.1
- 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
- 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
- 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
- Updated dependencies [2f57b75]
- Updated dependencies [2f57b75]
- @objectstack/spec@9.2.0
- @objectstack/core@9.2.0
- @objectstack/platform-objects@9.2.0
- Updated dependencies [b9062c9]
- @objectstack/spec@9.1.0
- @objectstack/core@9.1.0
- @objectstack/platform-objects@9.1.0
- Updated dependencies [1817845]
- @objectstack/spec@9.0.1
- @objectstack/core@9.0.1
- @objectstack/platform-objects@9.0.1
- 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
- @objectstack/spec@8.0.1
- @objectstack/core@8.0.1
- @objectstack/platform-objects@8.0.1
-
1e8b680: fix(security): close four P0 launch-readiness findings
- plugin-auth (P0-1):
generateSecret()now throws (fails boot) when noOS_AUTH_SECRETis set andNODE_ENV==='production', instead of silently falling back to a predictabledev-secret-<timestamp>(session forgery). The dev/test fallback is unchanged. - plugin-security (P0-2): the permission-resolution
catchnow fails closed — it logs at ERROR and throwsPermissionDeniedErrorrather thanreturn 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/$containsoperator now escapes LIKE metacharacters (%/_/\) in the user value and binds an explicitESCAPE '\', 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.
- plugin-auth (P0-1):
-
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
- @objectstack/spec@7.9.0
- @objectstack/core@7.9.0
- @objectstack/platform-objects@7.9.0
- 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
- 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
- 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
- @objectstack/spec@7.5.0
- @objectstack/core@7.5.0
- @objectstack/platform-objects@7.5.0
- @objectstack/spec@7.4.1
- @objectstack/core@7.4.1
- @objectstack/platform-objects@7.4.1
-
e478e0c: ADR-0029 K2 — security domain ownership (RBAC + sharing) + Setup nav contributions.
Moves the security objects out of the
@objectstack/platform-objectsmonolith 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) gainssys_role,sys_permission_set,sys_user_permission_set,sys_role_permission_set, and thedefaultPermissionSetsseed (which itsbootstrap-platform-adminalready consumes). The RBAC + default-permission-set tests move with them.@objectstack/plugin-sharinggainssys_record_share,sys_sharing_rule,sys_share_link.@objectstack/platform-objectsno longer defines/exports any security objects; the/securitysubpath is now an empty barrel. Runtime is unchanged (both plugins already registered these objects at runtime).
D7 navigation — the Setup app's
group_access_controlis now assembled from three sources:plugin-securitycontributes Roles / Permission Sets (priority 100),plugin-sharingcontributes Sharing Rules / Record Shares (priority 200), andplatform-objectskeeps 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-objectsi18n 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.
-
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-objectsgenerated 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.tsand asrc/translations/bundle ({locale}.objects.generated.ts+ anindex.tsbarrel), generated withos i18n extractand self-baselined so re-runs preserve translations. - Each plugin loads its bundle at runtime on
kernel:readyviai18n.loadTranslations(the i18n service is optional — load is best-effort).plugin-webhooks←sys_webhook,sys_webhook_deliveryplugin-approvals←sys_approval_request,sys_approval_actionplugin-security←sys_role,sys_permission_set,sys_user_permission_set,sys_role_permission_setplugin-sharing←sys_record_share,sys_sharing_rule,sys_share_link
@objectstack/platform-objectstranslation 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.
- Each plugin gains a build-time
-
08fbbb4: Fix: the first-boot platform-admin promotion no longer gets stolen by the
usr_systemseed identity, and the dev seed admin uses fixed, well-known credentials.@objectstack/plugin-security—bootstrapPlatformAdminskips the system user5e831dea3(#1392) addedensureSeedIdentityto the runtime SeedLoader, which upserts a non-loginable system identity (usr_system, rolesystem,system@objectstack.local) to own seeded records — created before the first human sign-up. BecausebootstrapPlatformAdminpromoted the earliest-createdsys_user, on any app that ships seed datausr_systemwon the promotion and the real admin login stayed atrole: user. Login succeeded but Setup and Studio (gated bysetup.access/studio.accessonadmin_full_access) were invisible — a silent, confusing regression.bootstrap-platform-admin.tsnow 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 anyadmin_full_accessgrant held byusr_system— so a database where it was wrongly promoted self-heals on the next boot.@objectstack/cli—os devseedsadmin@objectos.ai/admin123The
--admin-email/--admin-passworddefaults changed fromadmin@dev.local/admin12345to the fixed, well-knownadmin@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
- Updated dependencies [5e7c554]
- @objectstack/spec@7.3.0
- @objectstack/core@7.3.0
- @objectstack/platform-objects@7.3.0
- @objectstack/spec@7.2.1
- @objectstack/core@7.2.1
- @objectstack/platform-objects@7.2.1
- @objectstack/spec@7.2.0
- @objectstack/core@7.2.0
- @objectstack/platform-objects@7.2.0
- Updated dependencies [6228609]
- Updated dependencies [47a92f4]
- @objectstack/platform-objects@7.1.0
- @objectstack/spec@7.1.0
- @objectstack/core@7.1.0
-
3a630b6: Split organization-scoping from
@objectstack/plugin-securityinto a new@objectstack/plugin-org-scopingpackage.Per ADR-0002, "tenant" in ObjectStack means physical isolation (one Environment = one database, handled by
@objectstack/driver-turso's multi-tenant router). The row-levelorganization_idscoping that previously lived inside SecurityPlugin is a different concept — logical scoping inside a single DB — and now ships as its own plugin.- Removed the
multiTenantconstructor option. SecurityPlugin no longer touchesorganization_idon insert and no longer registers thesys_organizationpost-create seed pipeline. - Wildcard
current_user.organization_idRLS policies in the default permission sets are now stripped UNLESS the neworg-scopingservice is registered (i.e. unlessOrgScopingPluginis also installed). - Removed export
cloneTenantSeedData(now exposed ascloneOrgSeedDatafrom@objectstack/plugin-org-scoping). bootstrapPlatformAdmin()no longer accepts amultiTenantflag and no longer auto-creates a default organization — that behavior moved toensureDefaultOrganization()in the new plugin.
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_TENANTenv switch — read by@objectstack/runtime/cloud/ArtifactKernelFactory,@objectstack/plugin-dev, and theobjectstackCLI'sserve/dev/startcommands — automatically registersOrgScopingPluginwhen set totrue, so projects driven by the CLI need no code changes. - Removed the
- 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
- @objectstack/spec@6.9.0
- @objectstack/core@6.9.0
- @objectstack/platform-objects@6.9.0
- @objectstack/spec@6.8.1
- @objectstack/core@6.8.1
- @objectstack/platform-objects@6.8.1
- 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
- @objectstack/spec@6.7.1
- @objectstack/core@6.7.1
- @objectstack/platform-objects@6.7.1
- 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
- Updated dependencies [a49cfc2]
- @objectstack/spec@6.6.0
- @objectstack/core@6.6.0
- @objectstack/platform-objects@6.6.0
- @objectstack/spec@6.5.1
- @objectstack/core@6.5.1
- @objectstack/platform-objects@6.5.1
- @objectstack/spec@6.5.0
- @objectstack/core@6.5.0
- @objectstack/platform-objects@6.5.0
- 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
- @objectstack/spec@6.3.0
- @objectstack/core@6.3.0
- @objectstack/platform-objects@6.3.0
- Updated dependencies [b4c74a9]
- @objectstack/spec@6.2.0
- @objectstack/core@6.2.0
- @objectstack/platform-objects@6.2.0
- @objectstack/spec@6.1.1
- @objectstack/core@6.1.1
- @objectstack/platform-objects@6.1.1
- Updated dependencies [93c0589]
- @objectstack/spec@6.1.0
- @objectstack/core@6.1.0
- @objectstack/platform-objects@6.1.0
- 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
-
b806f58: Scope
sys_uservisibility to fellow organization members.The default RLS policy on
sys_userwasid = 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 aid IN (SELECT user_id FROM sys_member ...)policy isn't expressible.This change:
- Pre-resolves
org_user_ids(the IDs of all users in the active org) intoExecutionContextin all three REST entry-point resolvers (@objectstack/rest,@objectstack/runtime,@objectstack/plugin-hono-server). - Adds the field to
ExecutionContextSchemaso it survives Zod parsing. - Adds an
org_user_idsfield to the RLS compiler's user context. - Adds a new
sys_user_org_memberspolicy (id IN (current_user.org_user_ids)) to bothmember_defaultandviewer_readonlypermission sets, alongside the existingsys_user_selfpolicy. 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.
- Pre-resolves
-
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
- Updated dependencies [75f4ee6]
- Updated dependencies [823d559]
- @objectstack/spec@5.1.0
- @objectstack/platform-objects@5.1.0
- @objectstack/core@5.1.0
- Updated dependencies [888a5c1]
- Updated dependencies [2f9073a]
- @objectstack/platform-objects@5.0.0
- @objectstack/spec@5.0.0
- @objectstack/core@5.0.0
- Updated dependencies [2869891]
- @objectstack/spec@4.2.0
- @objectstack/core@4.2.0
- @objectstack/platform-objects@4.2.0
- @objectstack/spec@4.1.1
- @objectstack/core@4.1.1
- @objectstack/platform-objects@4.1.1
-
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 (
maskResultson find/findOne). Insert and update operations could target any field — a hand-crafted POST bypassed FLS entirely.The middleware now runs
FieldMasker.detectForbiddenWriteson every insert / update payload (single record or bulk array) and throwsPermissionDeniedError(HTTP 403) when the payload references a field the caller is not permitted to edit. The offending field list is exposed viadetails.forbiddenFieldsfor actionable client error UI.Allow-list semantics: only fields explicitly enumerated in a permission set's
fieldsmap 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).
- Updated dependencies [2108c30]
- Updated dependencies [23db640]
- @objectstack/spec@4.1.0
- @objectstack/core@4.1.0
- @objectstack/platform-objects@4.1.0
- 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
- Updated dependencies [326b66b]
- @objectstack/spec@4.0.4
- @objectstack/core@4.0.4
- @objectstack/spec@4.0.3
- @objectstack/core@4.0.3
- Updated dependencies [5f659e9]
- @objectstack/spec@4.0.2
- @objectstack/core@4.0.2
- Updated dependencies [f08ffc3]
- Updated dependencies [e0b0a78]
- @objectstack/spec@4.0.0
- @objectstack/core@4.0.0
- @objectstack/spec@3.3.1
- @objectstack/core@3.3.1
- @objectstack/spec@3.3.0
- @objectstack/core@3.3.0
- @objectstack/spec@3.2.9
- @objectstack/core@3.2.9
- @objectstack/spec@3.2.8
- @objectstack/core@3.2.8
- @objectstack/spec@3.2.7
- @objectstack/core@3.2.7
- @objectstack/spec@3.2.6
- @objectstack/core@3.2.6
- @objectstack/spec@3.2.5
- @objectstack/core@3.2.5
- @objectstack/spec@3.2.4
- @objectstack/core@3.2.4
- @objectstack/spec@3.2.3
- @objectstack/core@3.2.3
- Updated dependencies [46defbb]
- @objectstack/spec@3.2.2
- @objectstack/core@3.2.2
- Updated dependencies [850b546]
- @objectstack/spec@3.2.1
- @objectstack/core@3.2.1
- Updated dependencies [5901c29]
- @objectstack/spec@3.2.0
- @objectstack/core@3.2.0
- Updated dependencies [953d667]
- @objectstack/spec@3.1.1
- @objectstack/core@3.1.1
- Updated dependencies [0088830]
- @objectstack/spec@3.1.0
- @objectstack/core@3.1.0
- Updated dependencies [92d9d99]
- @objectstack/spec@3.0.11
- @objectstack/core@3.0.11
- Updated dependencies [d1e5d31]
- @objectstack/spec@3.0.10
- @objectstack/core@3.0.10
- Updated dependencies [15e0df6]
- @objectstack/spec@3.0.9
- @objectstack/core@3.0.9
- Updated dependencies [5a968a2]
- @objectstack/spec@3.0.8
- @objectstack/core@3.0.8
- Updated dependencies [0119bd7]
- Updated dependencies [5426bdf]
- @objectstack/spec@3.0.7
- @objectstack/core@3.0.7
- Updated dependencies [5df254c]
- @objectstack/spec@3.0.6
- @objectstack/core@3.0.6
- Updated dependencies [23a4a68]
- @objectstack/spec@3.0.5
- @objectstack/core@3.0.5
- Updated dependencies [d738987]
- @objectstack/spec@3.0.4
- @objectstack/core@3.0.4
- c7267f6: Patch release for maintenance updates and improvements.
- Updated dependencies [c7267f6]
- @objectstack/spec@3.0.3
- @objectstack/core@3.0.3
- Updated dependencies [28985f5]
- @objectstack/spec@3.0.2
- @objectstack/core@3.0.2
- Updated dependencies [389725a]
- @objectstack/spec@3.0.1
- @objectstack/core@3.0.1
- Release v3.0.0 — unified version bump for all ObjectStack packages.
- Updated dependencies
- @objectstack/spec@3.0.0
- @objectstack/core@3.0.0
- Updated dependencies
- @objectstack/spec@2.0.7
- @objectstack/core@2.0.7
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.6
- @objectstack/core@2.0.6
- Unify all package versions with a patch release
- Updated dependencies
- @objectstack/spec@2.0.5
- @objectstack/core@2.0.5