-
b20201f: fix(service-automation):
runAs:'user'runs data ops with the triggering user's real permission sets + positions, not a bare member fallback (#3356, follow-up to #1888)Since #1888 the automation engine honours
flow.runAs(systemelevates), but therunAs:'user'credential propagation was hollow. A record-change-triggeredrunAs:'user'flow ran its data nodes (update_record, …) with a zero-grant principal — only themember/everyonebaseline — even when the triggering user was fully authorized. Two faces by object config: aprivateobject 403'd the in-flow write (not permitted for positions [org_member, everyone]— the user's permission sets were invisible); apublic_read_writeobject let the write through but silently stripped readonly/FLS-gated fields. The root cause: the ObjectQL record-change hook session carries only auserId— never the writer's positions/permission sets — and nothing in between resolved them, so the comment promising "enforces RLS exactly as the user who made the change" never held.The fix resolves the triggering user's actual authorization at run setup, from the same tables a direct REST request resolves through:
@objectstack/corefactors the userId-driven core ofresolveAuthzContextinto a new exportedresolveUserAuthzGrants(ql, userId, opts)— the single place that readssys_member/sys_user_position/sys_*_permission_setand derives positions, permission-set names,platform_admin, and posture. The HTTP resolver now delegates to it (behaviour byte-identical; the full contract suite still passes), so a non-HTTP surface that already knows the user id builds the SAME envelope instead of re-implementing the reads.@objectstack/service-automationgainsAutomationEngine.setUserGrantsResolver, wired by the plugin toresolveUserAuthzGrantsover the objectql/data engine. For arunAs:'user'run whose trigger left the authz envelope unresolved (nopermissions), the engine now resolves the user's positions + permission sets once at run setup and threads them into every data node's ObjectQL context — so the run enforces RLS/FLS exactly as that user. Contexts that already carrypermissionsare left untouched (a REST trigger, and notably an ADR-0090 agent ceiling acting on-behalf-of a user — always non-empty — so a deliberately narrowed identity is never re-broadened).runAs:'system'is unchanged, and a resolver error fails safe (warns, keeps the bare user — never elevates).@objectstack/trigger-record-changestops forwarding the misleading half-populatedpositions(empty in practice, and neverpermissions) from the hook session; it forwardsuserId+ tenant only and lets the engine resolve the full grants authoritatively.
When no ObjectQL engine is present (bare engine / tests) the resolver is unwired and run identity is unchanged from before.
- Updated dependencies [9e45b63]
- @objectstack/spec@16.1.0
-
dd9f223: feat(analytics): scope a datetime date-bucket drill to the reference-tz midnight instants (#1752 follow-up)
Closes the one gap left by the initial #1752 change: a
datetimedate dimension bucketed under a non-UTC reference timezone previously fell back to a superset drill (its bucket boundary is that tz's midnight instant, whichYYYY-MM-DDcalendar bounds can't express).@objectstack/coreaddszonedDateStartToUtcMs(ymd, tz)— the UTC instant at which a calendar day begins in a reference timezone (the inverse ofcalendarPartsInTz). DST-safe: the offset is read from the platform tz database viaIntl, with a two-pass resolution for the rare offset-boundary case; an unset/'UTC'/invalid zone returns plain UTC midnight.@objectstack/service-analyticsnow emitsdrillRangesbounds per the field's temporal type (ADR-0053): adatetimefield → ISO instant bounds at the reference tz's midnight (works under any tz, incl. DST); adatefield →YYYY-MM-DDcalendar bounds (tz-naive, exact under any tz). An unknown field type is still emitted only under UTC and omitted (superset) under a non-UTC tz.
No objectui change is needed — the client already forwards whatever bound values the server sends into the drill filter and the
filter[field][gte|lt]URL. -
290e2f0: feat(analytics): emit a half-open date-range drill scope for granularity-bucketed date dimensions (#1752)
A report/dashboard cell grouped by a
dateGranularitydate dimension ("2026-Q2") covers a SPAN of records, so drilling it needs a range (>= start AND < nextStart), which the equality drill contract (drillRawRows) can't express — date dims were therefore excluded from drill metadata and a drill landed on an unscoped superset.@objectstack/coreaddsbucketKeyToCalendarRange(key, granularity), the inverse ofbucketDateValue: it turns a canonical bucket key into its half-open[start, end)calendar span (YYYY-MM-DD,endexclusive). Pure, timezone-naive calendar arithmetic; returnsnullfor unbucketable / out-of-range keys so the caller falls back to an unscoped (superset) drill rather than emit a wrong bound.@objectstack/service-analyticsemits adrillRangessidecar (aligned torowsby index — the range companion todrillRawRows) fordate+dateGranularitydimensions, computed from the canonical bucket key in the pre-label-resolution snapshot pass. Adatetimefield under a non-UTC reference timezone is omitted (host drills a superset) until instant-boundary support lands; a tz-naivedatefield is exact under any timezone (ADR-0053).
Consumed by objectui's report drill-through to scope the drilled record list to the clicked time bucket.
-
e057f42: fix: harden the bulk-write path — retries, idempotency, contracts, and summary visibility (#3147–#3152)
Six reliability fixes to the batched seed/import +
engine.insert(array)path introduced by the #2678 bulk-write rework:- #3151
bulkWritevalidates thatwriteBatchreturns one record per input row (a short/long/non-array return is degraded per-row, not backfilled as phantom success);engine.insert(array)likewise rejects a short driverbulkCreatereturn instead of padding afterInsert withundefined. - #3150 wraps the two remaining un-retried write points (seed
writeRecord/resolveDeferredUpdates, import's no-createManyDatafallback) inwithTransientRetry;defaultIsTransientErrorshort-circuits definitive logical errors to non-transient. - #3148 import
resolveRefflushes pending creates on a same-object miss so a later row can reference an earlier same-file CREATE, and no longer negatively caches a miss. - #3149 threads an
attemptcounter throughbulkWrite; seed rechecks byexternalIdand import bymatchFieldsbefore re-writing, so a commit-then-lost-response retry cannot duplicate a batch. - #3147
recomputeSummariesretries transient failures and, on exhaustion, surfacesSummaryRecomputeError(ERR_SUMMARY_RECOMPUTE) instead of a silent warn; seed/import recover it to a warning without re-writing. - #3152 autonumbers are assigned after validation, so a batch that dies in validation consumes no sequence value (no number-range gaps).
- #3151
-
5f05de2:
createLogger({ file })now actually writes the file under ESM.openFileStreamloadedfswith a lazyrequire()to keep the browser-safe logger entry out of thefsbundle graph; esbuild rewrites that to its__requireshim in the ESM output, which throwsDynamic require of "fs" is not supported, and a barecatch {}swallowed it. Since the workspace istype: module, every Node ESM consumer —os serve,os dev— silently got no file logging at all, while the CJS build kept working. The builtin now loads viaprocess.getBuiltinModule(opaque to bundlers, works in both module systems, with arequirefallback for Node < 20.16), and afiledestination that cannot be opened reports itself on stderr instead of disappearing.Turning the destination back on also fixed three faults that were unreachable while it never opened:
child()opened a second stream per child and orphaned it, destroying a child logger closed the stream its parent and siblings were still writing to, and an async open failure (e.g. an unwritable path) hit an'error'event with no listener and took the process down. -
021ba4c: fix(core): ObjectLogger honors NO_COLOR and TTY detection before emitting ANSI colors
The kernel/plugin logger (
ctx.logger, wired byos serve/os dev) colorized itspretty-format level tags unconditionally, soNO_COLOR=1runs and piped/CI output still carried ANSI escapes (e.g.\x1b[31m…ERROR\x1b[0m), breaking plain-text log scanners (see scripts/publish-smoke.sh, which had to strip ANSI before grepping).Per the no-color.org convention, color is now emitted only when the destination stream (stdout, or stderr for error/fatal) is an interactive TTY and
NO_COLORis unset or empty — any non-emptyNO_COLORvalue disables color. Interactive terminals keep the existing colorized output. The optional file destination now always receives plain text. -
Updated dependencies [f972574]
-
Updated dependencies [6289ec3]
-
Updated dependencies [22013aa]
-
Updated dependencies [3ad3dd5]
-
Updated dependencies [8efa395]
-
Updated dependencies [3a18b60]
-
Updated dependencies [a8aa34c]
-
Updated dependencies [a3823b2]
-
Updated dependencies [43a3efb]
-
Updated dependencies [524696a]
-
Updated dependencies [bfa3c3f]
-
Updated dependencies [5e3301d]
-
Updated dependencies [46e876c]
-
Updated dependencies [158aa14]
-
Updated dependencies [62a2117]
-
Updated dependencies [d2723e2]
-
Updated dependencies [fefcd54]
-
Updated dependencies [beaf2de]
-
Updated dependencies [369eb6e]
-
Updated dependencies [06ff734]
-
Updated dependencies [b659111]
-
Updated dependencies [5754a23]
-
Updated dependencies [6c270a6]
-
Updated dependencies [668dd17]
-
Updated dependencies [8abf133]
-
Updated dependencies [e0859b1]
-
Updated dependencies [04ecd4e]
-
Updated dependencies [4d5a892]
-
Updated dependencies [16cebeb]
-
Updated dependencies [86d30af]
-
Updated dependencies [8923843]
-
Updated dependencies [a2795f6]
-
Updated dependencies [f16b492]
-
Updated dependencies [4b6fde8]
-
Updated dependencies [2018df9]
-
Updated dependencies [fc5a3a2]
-
Updated dependencies [8ff9210]
- @objectstack/spec@16.0.0
- Updated dependencies [6289ec3]
- Updated dependencies [8efa395]
- Updated dependencies [bfa3c3f]
- Updated dependencies [62a2117]
- Updated dependencies [06ff734]
- @objectstack/spec@16.0.0-rc.1
-
dd9f223: feat(analytics): scope a datetime date-bucket drill to the reference-tz midnight instants (#1752 follow-up)
Closes the one gap left by the initial #1752 change: a
datetimedate dimension bucketed under a non-UTC reference timezone previously fell back to a superset drill (its bucket boundary is that tz's midnight instant, whichYYYY-MM-DDcalendar bounds can't express).@objectstack/coreaddszonedDateStartToUtcMs(ymd, tz)— the UTC instant at which a calendar day begins in a reference timezone (the inverse ofcalendarPartsInTz). DST-safe: the offset is read from the platform tz database viaIntl, with a two-pass resolution for the rare offset-boundary case; an unset/'UTC'/invalid zone returns plain UTC midnight.@objectstack/service-analyticsnow emitsdrillRangesbounds per the field's temporal type (ADR-0053): adatetimefield → ISO instant bounds at the reference tz's midnight (works under any tz, incl. DST); adatefield →YYYY-MM-DDcalendar bounds (tz-naive, exact under any tz). An unknown field type is still emitted only under UTC and omitted (superset) under a non-UTC tz.
No objectui change is needed — the client already forwards whatever bound values the server sends into the drill filter and the
filter[field][gte|lt]URL. -
290e2f0: feat(analytics): emit a half-open date-range drill scope for granularity-bucketed date dimensions (#1752)
A report/dashboard cell grouped by a
dateGranularitydate dimension ("2026-Q2") covers a SPAN of records, so drilling it needs a range (>= start AND < nextStart), which the equality drill contract (drillRawRows) can't express — date dims were therefore excluded from drill metadata and a drill landed on an unscoped superset.@objectstack/coreaddsbucketKeyToCalendarRange(key, granularity), the inverse ofbucketDateValue: it turns a canonical bucket key into its half-open[start, end)calendar span (YYYY-MM-DD,endexclusive). Pure, timezone-naive calendar arithmetic; returnsnullfor unbucketable / out-of-range keys so the caller falls back to an unscoped (superset) drill rather than emit a wrong bound.@objectstack/service-analyticsemits adrillRangessidecar (aligned torowsby index — the range companion todrillRawRows) fordate+dateGranularitydimensions, computed from the canonical bucket key in the pre-label-resolution snapshot pass. Adatetimefield under a non-UTC reference timezone is omitted (host drills a superset) until instant-boundary support lands; a tz-naivedatefield is exact under any timezone (ADR-0053).
Consumed by objectui's report drill-through to scope the drilled record list to the clicked time bucket.
-
e057f42: fix: harden the bulk-write path — retries, idempotency, contracts, and summary visibility (#3147–#3152)
Six reliability fixes to the batched seed/import +
engine.insert(array)path introduced by the #2678 bulk-write rework:- #3151
bulkWritevalidates thatwriteBatchreturns one record per input row (a short/long/non-array return is degraded per-row, not backfilled as phantom success);engine.insert(array)likewise rejects a short driverbulkCreatereturn instead of padding afterInsert withundefined. - #3150 wraps the two remaining un-retried write points (seed
writeRecord/resolveDeferredUpdates, import's no-createManyDatafallback) inwithTransientRetry;defaultIsTransientErrorshort-circuits definitive logical errors to non-transient. - #3148 import
resolveRefflushes pending creates on a same-object miss so a later row can reference an earlier same-file CREATE, and no longer negatively caches a miss. - #3149 threads an
attemptcounter throughbulkWrite; seed rechecks byexternalIdand import bymatchFieldsbefore re-writing, so a commit-then-lost-response retry cannot duplicate a batch. - #3147
recomputeSummariesretries transient failures and, on exhaustion, surfacesSummaryRecomputeError(ERR_SUMMARY_RECOMPUTE) instead of a silent warn; seed/import recover it to a warning without re-writing. - #3152 autonumbers are assigned after validation, so a batch that dies in validation consumes no sequence value (no number-range gaps).
- #3151
-
5f05de2:
createLogger({ file })now actually writes the file under ESM.openFileStreamloadedfswith a lazyrequire()to keep the browser-safe logger entry out of thefsbundle graph; esbuild rewrites that to its__requireshim in the ESM output, which throwsDynamic require of "fs" is not supported, and a barecatch {}swallowed it. Since the workspace istype: module, every Node ESM consumer —os serve,os dev— silently got no file logging at all, while the CJS build kept working. The builtin now loads viaprocess.getBuiltinModule(opaque to bundlers, works in both module systems, with arequirefallback for Node < 20.16), and afiledestination that cannot be opened reports itself on stderr instead of disappearing.Turning the destination back on also fixed three faults that were unreachable while it never opened:
child()opened a second stream per child and orphaned it, destroying a child logger closed the stream its parent and siblings were still writing to, and an async open failure (e.g. an unwritable path) hit an'error'event with no listener and took the process down. -
021ba4c: fix(core): ObjectLogger honors NO_COLOR and TTY detection before emitting ANSI colors
The kernel/plugin logger (
ctx.logger, wired byos serve/os dev) colorized itspretty-format level tags unconditionally, soNO_COLOR=1runs and piped/CI output still carried ANSI escapes (e.g.\x1b[31m…ERROR\x1b[0m), breaking plain-text log scanners (see scripts/publish-smoke.sh, which had to strip ANSI before grepping).Per the no-color.org convention, color is now emitted only when the destination stream (stdout, or stderr for error/fatal) is an interactive TTY and
NO_COLORis unset or empty — any non-emptyNO_COLORvalue disables color. Interactive terminals keep the existing colorized output. The optional file destination now always receives plain text. -
Updated dependencies [f972574]
-
Updated dependencies [22013aa]
-
Updated dependencies [3ad3dd5]
-
Updated dependencies [3a18b60]
-
Updated dependencies [a8aa34c]
-
Updated dependencies [a3823b2]
-
Updated dependencies [43a3efb]
-
Updated dependencies [524696a]
-
Updated dependencies [5e3301d]
-
Updated dependencies [46e876c]
-
Updated dependencies [158aa14]
-
Updated dependencies [d2723e2]
-
Updated dependencies [fefcd54]
-
Updated dependencies [beaf2de]
-
Updated dependencies [369eb6e]
-
Updated dependencies [b659111]
-
Updated dependencies [5754a23]
-
Updated dependencies [6c270a6]
-
Updated dependencies [668dd17]
-
Updated dependencies [8abf133]
-
Updated dependencies [e0859b1]
-
Updated dependencies [04ecd4e]
-
Updated dependencies [4d5a892]
-
Updated dependencies [16cebeb]
-
Updated dependencies [86d30af]
-
Updated dependencies [8923843]
-
Updated dependencies [a2795f6]
-
Updated dependencies [f16b492]
-
Updated dependencies [4b6fde8]
-
Updated dependencies [2018df9]
-
Updated dependencies [fc5a3a2]
- @objectstack/spec@16.0.0-rc.0
- @objectstack/spec@15.1.1
-
f531a26: refactor(security): converge the anonymous-deny decision into one shared function + a source-enumerating ratchet (#2567 Phase 2)
Phase 1 gated every HTTP surface (REST
/data, dispatcher/graphql+/meta, raw-hono/data) against the secure-by-defaultrequireAuthposture, but each seam hand-rolled the same!userId && !isSystem → 401check. Phase 2 removes that duplication and pins the surfaces so a new ungated entry point fails CI.- New
shouldDenyAnonymousin@objectstack/core(security/anonymous-deny.ts) — the single anonymous-deny decision + shared 401 body/constants, mirroring theauth-gate.tspattern (pure function so the seams can never drift). All five seams — RESTenforceAuth, dispatcherhandleGraphQL/handleMetadata/handleAI, honodenyAnonymous— now delegate to it. Pure refactor: no runtime behavior change (verified by the unchanged Phase-1 handler + e2e proofs). Identity resolution and the dynamic exemptions (public-form grants, share-link tokens) are untouched — they run upstream and only ever hand the seam an already-resolved context. - A
discover()ratchet on the authz-conformance matrix — it statically enumerates the data/meta/graphql HTTP entry points from source (curated per-file probes, control-plane routes excluded) and asserts each is classified by a matrixcoverskey. A new/data//meta//graphqlroute (or a removed/stalecovers) now fails CI as UNCLASSIFIED / STALE, not in review. A companion negative test proves the ratchet bites.
A design trap is guarded:
isAuthGateAllowlisted(undefined)returnstrue, so a body-routed seam (GraphQL, which has no request path) must pass no path — the shared function's non-empty-path guard denies anonymous unconditionally there, never falling through to the control-plane allowlist. - New
-
f531a26: feat(kernel): add
kernel:bootstrappedlifecycle anchor — the phase that fires after everykernel:readyhandler has settled but beforekernel:listening(HTTP socket open).kernel:readyhandlers run sequentially in plugin-registration order, so a handler that consumes data produced by a later-starting plugin (e.g. the security bootstrap seedssys_position; the app plugin's seed loader inserts records) would race the very rows it needs.kernel:bootstrappedis the correct anchor for reconcile/backfill work: every producer's ready handler has finished by the time it fires. BothObjectKernelandLiteKerneltrigger it. The sharing-rule boot backfill moves fromkernel:listeningtokernel:bootstrapped(semantics-only; behaviour unchanged).
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [3fe9df1]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [4109153]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [627f225]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- Updated dependencies [f531a26]
- @objectstack/spec@15.1.0
-
13749ec: ADR-0095 D2/D3: the authorization kernel now resolves an explicit posture ladder — a monotonic principal tier
PLATFORM_ADMIN > TENANT_ADMIN > MEMBER > EXTERNAL— once, inresolveAuthzContext, and carries it onResolvedAuthzContext.posture.- D2 — the ladder. New
@objectstack/core/securitymoduleposture-ladder.tsreuses the specAuthzPostureenum and pins the rung → row-visibility injection-rule mapping (exactly one rule per rung) plus its two ADR-required invariants as unit-tested properties: strict nesting (rung n's visible set ⊇ rung n−1's) and theEXTERNALdeny-by-default semantics (explicitly shared rows only — OWD baselines and sharing rules never widen it).EXTERNALis defined and test-locked now but never resolved: no external principal type exists yet (portal/ADR-0093), so the resolver's floor isMEMBER. - D3 — capability-derived, single track. The rung derives from held
capability grants, never a better-auth role:
PLATFORM_ADMINfrom the unscopedadmin_full_accessgrant (the sameviewAllRecords/modifyAllRecordsevidence the superuser bypass trusts),TENANT_ADMINfrom theorganization_admingrant. The better-authrole='admin'remains only a provisioning source of those grants (auto-org-admin-grant.ts,mapMembershipRole); no enforcement path reads the raw role, closing the #2836 dual-track adjudication class by construction. - New spec export
ORGANIZATION_ADMIN(the org-admin capability-grant name), alongside the existingADMIN_FULL_ACCESS.
Behavior-preserving. Enforcement is unchanged — the per-object Layer 0 exemption and per-side superuser bypass still gate access exactly as before;
postureis an additive, derived, explainable field. Theauthz-matrix-gateunit snapshot and the dogfood authz-conformance matrix stay green. No migration required. - D2 — the ladder. New
- Updated dependencies [28b7c28]
- Updated dependencies [13749ec]
- Updated dependencies [e62c233]
- Updated dependencies [ed61c9b]
- Updated dependencies [31d04d4]
- @objectstack/spec@15.0.0
- Updated dependencies [16b4bf6]
- Updated dependencies [16b4bf6]
- Updated dependencies [10e8983]
- Updated dependencies [607aaf4]
- Updated dependencies [bb71321]
- @objectstack/spec@14.8.0
- Updated dependencies [d6a72eb]
- @objectstack/spec@14.7.0
- Updated dependencies [609cb13]
- Updated dependencies [ce6d151]
- @objectstack/spec@14.6.0
- Updated dependencies [526805e]
- Updated dependencies [d79ca07]
- Updated dependencies [33ebd34]
- Updated dependencies [c044f08]
- Updated dependencies [01274eb]
- @objectstack/spec@14.5.0
-
82e745e: ADR-0091 L1 — grant validity windows: effective-dated assignments, resolution-time filtering, explain expired state, authoring lint.
- plugin-security (objects):
sys_user_positionandsys_user_permission_setgain the D1 lifecycle columns —valid_from,valid_until(half-open[from, until), UTC; null = unbounded, existing rows unchanged),reason,delegated_from,last_certified_at,certified_by. - core: new shared predicate
isGrantActive/isGrantExpired(@objectstack/core), andresolveAuthzContextnow filters BOTH grant tables through it (D2, fail-closed — an expired unscopedadmin_full_accessgrant no longer derivesplatform_admin). Present-but-unparseable bounds fail closed. - plugin-security (explain):
buildContextForUserapplies the same filter and returnsexpiredGrants; the principal layer reports the dedicated "held until … — expired" contributor state so "why did access disappear" is self-answering. SpecExplainLayerSchemacontributors gain an optionalstate: 'active' | 'expired'. - plugin-sharing:
PositionGraphService.expandPositionUsersfilters expired holders — sharing-rule recipients stop including them at resolution time. - lint (D7): two new error rules over seed data —
security-grant-expired-at-authoring(avalid_untilin the past, or unparseable, is a grant that can never resolve) andsecurity-delegation-missing-reason(adelegated_fromrow withoutreasonbreaks the D3 dual audit). Also re-exported the missingSECURITY_MASTER_DETAIL_UNGRANTEDconstant.
No background job is involved anywhere — per ADR-0049, an expired grant simply stops resolving, in every edition.
- plugin-security (objects):
- Updated dependencies [7953832]
- Updated dependencies [82e745e]
- Updated dependencies [f3035bd]
- Updated dependencies [82c0d94]
- Updated dependencies [7449476]
- @objectstack/spec@14.4.0
- Updated dependencies [2a71f48]
- Updated dependencies [02f6af4]
- Updated dependencies [c1064f1]
- @objectstack/spec@14.3.0
- Updated dependencies [ac8f029]
- Updated dependencies [4ab9958]
- @objectstack/spec@14.2.0
- Updated dependencies [5a8465f]
- Updated dependencies [7f8620b]
- Updated dependencies [82ba3a6]
- @objectstack/spec@14.1.0
- Updated dependencies [0a8e685]
- Updated dependencies [afa8115]
- Updated dependencies [80f12ca]
- Updated dependencies [e2fa074]
- Updated dependencies [23c8668]
- Updated dependencies [29f017d]
- Updated dependencies [216fa9a]
- Updated dependencies [6c22b12]
- @objectstack/spec@14.0.0
-
6d83431: ADR-0090 P1 breaking wave — permission model v2 concept convergence.
Pre-launch one-step renames and secure defaults (no compatibility aliases, per ADR-0090 D3/D4 superseding ADR-0057 D5/D7's alias discipline):
sys_role→sys_position,sys_user_role→sys_user_position(fieldrole→position),sys_role_permission_set→sys_position_permission_set(fieldrole_id→position_id);RoleSchema/defineRole→PositionSchema/definePositionwith noparent(positions are flat; hierarchy lives on the business-unit tree).ExecutionContext.roles[]→positions[]; the EvalUser/CEL contractcurrent_user.roles→current_user.positions(formula validators updated); stack propertyroles:→positions:; metadata kindsrole/profile→position(profile kind removed).isProfileremoved fromPermissionSetSchema(ADR-0090 D2);isDefaultnarrows to an install-time suggestion;appDefaultProfileName→appDefaultPermissionSetName(isDefault-only).- OWD enum drops legacy aliases
read/read_write/full; new optionalexternalSharingModel(external dial,privatedefault) lands as P1 spec shape (ADR-0090 D11). - Secure default (D1): a custom object with an owner field and NO
sharingModelnow resolvesprivate(was: fully public). System objects keep their explicit posture. Unrecognised stored values fail closed. - ExecutionContext gains the P1 principal-taxonomy shape (D10):
principalKind/audience/onBehalfOf(optional, semantics phase in later). - Sharing recipients:
role→position(expanded viasys_user_position∪ the better-auth membership transition source);role_and_subordinatesremoved —unit_and_subordinatesnow expands the business-unit subtree (finishes ADR-0057 D5's re-homing).
-
01917c2: ADR-0090 P2 — audience anchors:
everyone/guestbuiltin positions.EVERYONE_POSITION/GUEST_POSITIONconstants in@objectstack/spec; both anchors seeded (system-managed) alongside the builtin identity names.- Every authenticated principal implicitly holds
everyoneinctx.positions, so sets bound to it resolve as ordinary position-bound grants — ADDITIVE. The fallback CLIFF is abolished: the configured baseline (fallbackPermissionSet, defaultmember_default) now applies in addition to explicit grants instead of only when the user had none, and is also seeded as aneveryonebinding (same table/audit/explain path as admin-authored defaults). - Sessionless HTTP principals resolve as
principalKind: 'guest'holding exactly['guest']; internal bare contexts are untouched. - Audience-anchor binding gate:
sys_position_permission_setwrites that would bind a high-privilege set (VAMA, delete/purge/transfer, system permissions,'*'wildcard) toeveryone/guestare rejected at the data layer, unconditionally (describeHighPrivilegeBitspredicate is exported and shared with the seed-time validation).
- Updated dependencies [6d83431]
- Updated dependencies [01917c2]
- Updated dependencies [b271691]
- Updated dependencies [a5a1e41]
- Updated dependencies [466adf6]
- Updated dependencies [5be00c3]
- Updated dependencies [466adf6]
- Updated dependencies [2bee609]
- Updated dependencies [fc7e7f7]
- @objectstack/spec@13.0.0
-
21420d9: Seed loader and data-import now route bulk writes through the engine's array-form
insert()(one round-trip per batch, with parent-deduplicated summary recompute) instead of oneinsert()/createData()call per record, and both retry transient driver errors instead of silently dropping the row (#2678).A new shared helper,
bulkWrite(@objectstack/core), batches rows through a caller-supplied batch-write function, retries a whole-batch transient failure (network blip / timeout) with exponential backoff, and degrades to per-row writes (each itself retried) when a batch fails for a non-transient reason — so one bad row can't drop the other N-1.withTransientRetrywraps a single write (e.g. an update) with the same retry behavior.SeedLoaderService.loadDataset()(@objectstack/metadata-protocol) buffers insert-mode records and flushes them in batches of 200 via the engine's arrayinsert(). Datasets with a self-referencing field (e.g.employee.manager_id -> employee) keep the historical per-record write path, since a later record may need an earlier one's freshly-assigned id.runImport()(@objectstack/rest) buffers create-resolved rows and flushes them viaprotocol.createManyData()when the protocol supports it, falling back to the original per-rowcreateData()call otherwise.Protocol.createManyData(@objectstack/metadata-protocol) now forwardscontexttoengine.insert()likecreateDataalready did, so tenant-scoped bulk creates work correctly.
Previously, a 1000-row seed or import into an object with a rollup summary issued 1000+ round-trips and up to 1000 summary recomputes; a single transient network error on any one row silently dropped it with no retry (the 2026-07-06 HotCRM first-boot incident). A
bulkCreate-capable driver now sees roughlyceil(N/batch)writes, and a transient error is retried before a row is ever reported as failed.Fix (
@objectstack/driver-sql):SqlDriver.bulkCreate()never generated a client-side id for a row missing one, unlikecreate()— a latent gap that this change is the first to exercise at scale (a bulk-inserted row without a driver-native id default silently landed withid: NULL).bulkCreate()now mirrorscreate()'s id/_idnormalization per row.
- Updated dependencies [6cebf22]
- @objectstack/spec@12.6.0
- Updated dependencies [8b3d363]
- @objectstack/spec@12.5.0
- Updated dependencies [60dc3ba]
- @objectstack/spec@12.4.0
- Updated dependencies [e7eceec]
- @objectstack/spec@12.3.0
-
4f5b791: Wire three more Studio-authored metadata surfaces at runtime (#2605 — the "declared but never wired" family, following the #2596 hooks template).
Authored actions now execute (#2605 item 1).
engine.executeAction's map was only ever populated from the app bundle at boot, so a publishedactionrow (standalone or embedded in an authored object'sactions[]) was stored and listed but never executable — before OR after a restart. Now:AppPlugininstalls a QuickJS-sandboxed default action runner at boot (engine.setDefaultActionRunner), the action-path twin of the #2596 hook body runner. Opt out withOS_DISABLE_AUTHORED_ACTIONS=1.ObjectQLPluginre-registers runtime-authored actions from theirsys_metadatarows underpackageId: 'metadata-service'atkernel:ready, onmetadata:reloaded, and onaction/objectprotocol mutations — saves, publishes, edits, and deletes take effect live. Package-artifact actions are excluded (AppPlugin owns those; re-registering would clobber their handlers).
Authored translations reach the i18n runtime (#2591).
translationmetadata items (single-localeAppTranslationBundlepayloads; locale from_meta.locale, a top-levellocale, or a BCP-47-shaped item name) now load into the i18n service as a separate authored layer that overlays static bundles. Both adapters carry the layer — service-i18n'sFileI18nAdapterAND the kernel's in-memory fallback (createMemoryI18n), which is what dev and standalone stacks actually run. The shared sync (wireAuthoredTranslationSync, exported from@objectstack/core, wired by the runtime's AppPlugin and by I18nServicePlugin with single-owner semantics) runs atkernel:ready, onmetadata:reloaded, and ontranslationprotocol mutations, with clear-then-reload semantics so deleted items/keys stop resolving instead of lingering in the deep-merged map.Sharing rules created at runtime bind without a restart (#2592).
bindRuleHookswas boot-only, so the first rule authored at runtime for an object with no boot-time rule silently never evaluated (rule authoring is a data insert —metadata:reloadednever fires). The sharing plugin now binds afterInsert/afterUpdate/afterDelete triggers onsys_sharing_rulethat unbind + re-bind the rule-hook package from a freshlistRules(), serialized so overlapping writes can't leave a stale snapshot bound, and fail-safe so a rebind failure never fails the rule write. -
Updated dependencies [fce8ff4]
-
Updated dependencies [3962023]
-
Updated dependencies [2bb193d]
-
Updated dependencies [0426d27]
-
Updated dependencies [da807f7]
- @objectstack/spec@12.2.0
- Updated dependencies [93e6d02]
- @objectstack/spec@12.1.0
- Updated dependencies [a8df396]
- Updated dependencies [e695fe0]
- Updated dependencies [7c09621]
- Updated dependencies [7709db4]
- Updated dependencies [2082109]
- Updated dependencies [7c09621]
- Updated dependencies [9860de4]
- Updated dependencies [069c205]
- @objectstack/spec@12.0.0
- Updated dependencies [6a9397e]
- Updated dependencies [c0efe5d]
- @objectstack/spec@11.10.0
- Updated dependencies [d3595d9]
- @objectstack/spec@11.9.0
- @objectstack/spec@11.8.0
- Updated dependencies [5178906]
- @objectstack/spec@11.7.0
- @objectstack/spec@11.6.0
- Updated dependencies [6ee4f04]
- Updated dependencies [c1e3a65]
- @objectstack/spec@11.5.0
- Updated dependencies [5821c51]
- Updated dependencies [a0fce3f]
- @objectstack/spec@11.4.0
- Updated dependencies [58e8e31]
- Updated dependencies [b4a5df0]
- @objectstack/spec@11.3.0
- Updated dependencies [d0f4b13]
- Updated dependencies [302bdab]
- @objectstack/spec@11.2.0
-
ce0b4f6: Auth: password expiry — the session-validation gate (ADR-0069 D1, P1)
Builds the authentication-policy session gate ADR-0069 needs and uses it for password expiry. When
password_expiry_days(newauthsetting, 0 = off) is exceeded, an authenticated user is blocked from protected REST resources with403 PASSWORD_EXPIREDuntil they change their password — while auth + remediation paths stay reachable.- core: new pure
evaluateAuthGate/isAuthGateAllowlistedhelper (@objectstack/core/security) — single source of truth for the allow-list (auth endpoints, change-password, health, UI-bootstrap reads). - plugin-auth:
customSessioncomputes the gate posture once and attachesuser.authGate;computeAuthGatereadssys_user.password_changed_atvs the configured window;password_changed_atis stamped on sign-up / change / reset;isAuthGateActive()keeps the gate zero-overhead when off. - platform-objects: new
sys_user.password_changed_atcolumn. - rest:
resolveExecCtxcarriesauthGate;enforceAuthblocks gated sessions (independent ofrequireAuth) using the core allow-list. - service-settings: new
password_expiry_daysfield.
Default-off / additive (no upgrade behavior change); a null
password_changed_atnever expires (existing users). Per ADR-0049 the setting ships with its enforcement; timestamps written asDate(ADR-0074).This gate is the shared seam for enforced MFA (ADR-0069 D3), which lands next as a small addition (a second
authGatebranch). The dispatcher/MCP path is a follow-up (tracked in #2375); the REST surface the Console uses is fully gated here. - core: new pure
-
3e593a7: Remove the deprecated
DriverInterfacetype alias — useIDataDriver(11.0).DriverInterfacewas a@deprecatedalias ofIDataDriver(the authoritative driver contract). It is removed from@objectstack/spec/contractsand@objectstack/core;objectql's engine now types drivers asIDataDriverdirectly (a type-identical change, since the alias wasIDataDriver).Driver authors: replace
DriverInterfacewithIDataDriver(same shape).Note: this is unrelated to the live
IDataEngineinterface (engine-layer contract, not deprecated) and to the separate zod-derivedDriverInterface/DriverInterfaceSchemain@objectstack/spec/data(the runtime driver schema), both of which are unchanged.
-
9ccfcd6: perf(core): authenticated requests issued ~16 sequential queries — duplicate authz + repeated localization — now request-scoped memoized
An authenticated REST request resolves its execution context (identity + RBAC/RLS + localization) many times in a single handler — the data operation itself, app-nav RBAC filtering, dashboard widget gating, the ADR-0069 auth gate. Each
resolveExecCtxpass is the fullresolveAuthzContextaggregation plus the localization read (~16 sequential queries), and nothing memoized it, so a request that resolves twice paid for duplicate authz and repeated localization.@objectstack/rest—resolveExecCtxis now memoized per request, keyed by the request object (aWeakMap, so the entry is collected with the request — no TTL, no cross-request leak) and the inputenvironmentId. The in-flight Promise is cached so concurrent callers share one resolution. The heavy path moved tocomputeExecCtx. Anonymous (undefined) resolutions are cached too.@objectstack/core— within a singleresolveAuthzContextpass,sys_useris now read at most once (the email fallback and theai_seatsynthesis shared a duplicate query on the API-key path);resolveLocalizationContext's direct-read fallback batchestimezone/locale/currencyinto onesys_settingquery ($inonkey) instead of three sequential reads.
No authorization-behavior change — the same roles/permissions/RLS context is resolved, just without the redundant reads. The
sys_memberreads (per-user roles vs. all-org-members) are intentionally left distinct (different filters/limits).Tests: query-counting regressions assert
sys_userreads once and localization reads once; new rest-server tests pin the per-request/per-environment memo contract. -
Updated dependencies [ecf193f]
-
Updated dependencies [51bec81]
-
Updated dependencies [3e593a7]
-
Updated dependencies [63d5403]
- @objectstack/spec@11.1.0
-
c715d25: chore(license): unify the framework repo to a single Apache-2.0 license
The repo was left in a half-finished, self-contradictory source-available transition: 44 package
LICENSEfiles carried restrictive dual-license text (a Licensor of "ObjectStack AI LLC", a four-year conversion date, and an anti-competitive-hosting grant) while those same packages'package.jsonalready declared"license": "Apache-2.0"— and that license text pointed atLICENSING.mdfor the authoritative list of restricted packages, which listed none. The root also carried a redundantLICENSE.apacheleft over from that transition.The framework is deliberately permissive Apache-2.0 to maximize adoption; value capture lives in the separate closed-source cloud repo, not here. This change makes that unambiguous: every package
LICENSEnow contains the canonical Apache 2.0 text (copied from the rootLICENSE), the redundant rootLICENSE.apacheis removed, andLICENSING.mdstates the entire repository is Apache-2.0 with no dual-license language. No restrictive-license residue remains anywhere outsidenode_modules.This is a metadata-only change (license text and
package.jsonalready agreed); the patch bump republishes the affected packages with the correctedLICENSE. -
aa33b02: fix(security): single-source the request authorization resolver — REST no longer drops sys_user_position
The REST server and the runtime dispatcher each carried their own copy of the request → ExecutionContext identity/role resolver, and they drifted on a security path. The REST copy silently omitted
sys_user_position(so custom roles granted via the ADR-0057 D4 platform-RBAC path did not apply over REST),sys_position_permission_set, theowner→org_ownermembership normalization, the platform-admin derivation, and theai_seatsynthesis — fail-closed (legitimate access denied), not an escalation.Both entry points now delegate to a single shared resolver,
resolveAuthzContextin@objectstack/core/security(joining the API-key verifier that already lived there). A contract test locks every authorization source and a lint gate (check:authz-resolver) prevents a future duplicate resolver or a dropped delegation. -
Updated dependencies [ab5718a]
-
Updated dependencies [4845c12]
-
Updated dependencies [c1a754a]
-
Updated dependencies [6fbe91f]
-
Updated dependencies [715d667]
-
Updated dependencies [5eef4cf]
-
Updated dependencies [72759e1]
-
Updated dependencies [6c4fbd9]
-
Updated dependencies [ef3ed67]
-
Updated dependencies [cd51229]
-
Updated dependencies [7697a0e]
-
Updated dependencies [e7e04f1]
-
Updated dependencies [cfd5ac4]
-
Updated dependencies [2be5c1f]
-
Updated dependencies [ad143ce]
-
Updated dependencies [5c4a8c8]
-
Updated dependencies [3afaeed]
-
Updated dependencies [8801c02]
-
Updated dependencies [3d04e06]
-
Updated dependencies [4a84c98]
-
Updated dependencies [d980f0d]
-
Updated dependencies [a658523]
-
Updated dependencies [82ff91c]
-
Updated dependencies [638f472]
- @objectstack/spec@11.0.0
- @objectstack/spec@10.3.0
- Updated dependencies [b496498]
- @objectstack/spec@10.2.0
- Updated dependencies [49da36e]
- Updated dependencies [ac79f16]
- @objectstack/spec@10.1.0
-
d5f6d29: fix(runtime): surface code-defined datasources at
GET /api/v1/datasourcesandGET /api/v1/meta/datasourceon the standalone / host-config boot path (ADR-0015 §18, follow-up to #2111).A datasource declared in
defineStack({ datasources: [...] })(e.g. the showcase'sshowcase_external) is stampedorigin: 'code'and registered byAppPluginviametadata.registerInMemory('datasource', …)— gated ontypeof metadata.registerInMemory === 'function'. On the standalone / host-config path (os dev/servefor a config whosepluginsare already instantiated —isHostConfigtrue — so noMetadataPluginloads) themetadataservice is an in-memory fallback that implementedregister/list/getbut notregisterInMemory. The guard was therefore false, AppPlugin silently skipped the registration, and the datasource was absent from both REST surfaces (and Setup → Integrations → Datasources) even though the boot banner counted it and its federated objects were queryable.Both in-memory
metadatafallbacks (@objectstack/core'screateMemoryMetadataand@objectstack/plugin-dev's dev stub) now implementregisterInMemory(synchronous, no persistence — identical toregisterfor these in-memory stores, matchingMetadataManager's signature). The read paths (metadata.list, datasource-adminlistDatasources, andprotocol.getMetaItemswhich mergesmetadata.list) were already correct; this restores the write-side registration they depend on. It also makes stack-declared security metadata (roles/permissions/sharingRules/policies, registered through the same guard) listable on this path. -
Updated dependencies [d7ff626]
-
Updated dependencies [2a1b16b]
-
Updated dependencies [e16f2a8]
-
Updated dependencies [e411a82]
-
Updated dependencies [a581385]
-
Updated dependencies [220ce5b]
-
Updated dependencies [3efe334]
-
Updated dependencies [feead7e]
-
Updated dependencies [6ca20b3]
-
Updated dependencies [5f875fe]
-
Updated dependencies [b469950]
- @objectstack/spec@10.0.0
- 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
- Updated dependencies [db02bd5]
- Updated dependencies [641675d]
- Updated dependencies [94e9040]
- Updated dependencies [1f88fd9]
- Updated dependencies [1f88fd9]
- @objectstack/spec@9.10.0
- @objectstack/spec@9.9.1
-
601cc11: feat(analytics): timezone-aware date bucketing (ADR-0053 Phase 2)
Analytics day/week/month/quarter/year buckets now resolve on a reference timezone's calendar days, so a row near a tz day-boundary lands in the bucket a user in that zone would expect — identically on SQLite and Postgres.
Per ADR-0053 decision D2, bucketing is done in-memory, uniformly for non-UTC zones rather than emitting dialect-specific
date_trunc … AT TIME ZONE(SQLite has no tz database and MySQL needs tz tables loaded, so splitting by dialect would shift bucket boundaries for the same data).engine.aggregate({ timezone })therefore forces the in-memory aggregation path when a non-UTC reference tz is set — the date-rangewherestill goes to the driver, so only matching rows are fetched. UTC / unset keeps the native driver fast path unchanged.- New shared
calendarPartsInTz/calendarPartsInTzOrUtcutil in@objectstack/core(DST-safe viaIntl.DateTimeFormat, never hand-rolled offset math; falls back to UTC for an unset/'UTC'/invalid zone). EngineAggregateOptionsand the analyticsexecuteAggregatebridge /ObjectQLStrategythread the reference timezone (sourced from the dataset selection /ExecutionContext) through toapplyInMemoryAggregation→bucketDateValue, and the draft-preview evaluator'sbucketDate.formatDateBucket(dimension labels) stays UTC-only by design: it re-labels values that were already bucketed upstream, so re-applying a timezone there would shift a correct bucket by a day.
- New shared
- 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
- Updated dependencies [97c55b3]
- Updated dependencies [1b1f490]
- @objectstack/spec@9.8.0
- @objectstack/spec@9.7.0
- Updated dependencies [d1e930a]
- Updated dependencies [71578f2]
- Updated dependencies [5e3a301]
- Updated dependencies [5db2742]
- @objectstack/spec@9.6.0
- Updated dependencies [ee72aae]
- @objectstack/spec@9.5.1
- Updated dependencies [d08551c]
- Updated dependencies [707aeed]
- Updated dependencies [7a103d4]
- Updated dependencies [4b01250]
- @objectstack/spec@9.5.0
- Updated dependencies [060467a]
- Updated dependencies [0856476]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- Updated dependencies [b678d8c]
- @objectstack/spec@9.4.0
- Updated dependencies [1ada658]
- Updated dependencies [3219191]
- Updated dependencies [290f631]
- Updated dependencies [50b7b47]
- Updated dependencies [f15d6f6]
- Updated dependencies [f8684ea]
- Updated dependencies [b4765be]
- @objectstack/spec@9.3.0
- Updated dependencies [2f57b75]
- Updated dependencies [2f57b75]
- @objectstack/spec@9.2.0
- Updated dependencies [b9062c9]
- @objectstack/spec@9.1.0
- Updated dependencies [1817845]
- @objectstack/spec@9.0.1
- Updated dependencies [4c3f693]
- Updated dependencies [0bf39f1]
- Updated dependencies [f533f42]
- Updated dependencies [1c83ee8]
- @objectstack/spec@9.0.0
- @objectstack/spec@8.0.1
-
c262301: fix(rest): REST data API honors sys_api_key — one shared verifier with MCP (closes #1633)
Staging e2e found the MCP surface authenticated a
sys_api_keybut the REST data API (@objectstack/rest) returned 401 for the same key — itsresolveExecCtxonly checked the better-auth session, never the API key.Converged both surfaces onto ONE verifier so they can't drift:
@objectstack/core/securitynow owns the sharedsys_api_keyprimitives (hashApiKey,generateApiKey,extractApiKey,parseScopes,isExpired) plus a newresolveApiKeyPrincipal(ql, headers, nowMs?)that hashes the inbound key, looks it up by the indexed at-rest hash, and rejects unknown / revoked / expired / owner-less keys (fail-closed).coreis the natural home: bothrestandruntimedepend on it, it depends on neither (no cycle), and it's server-side (already usesnode:crypto).@objectstack/runtime—security/api-key.tsre-exports the primitives from core (stable import surface) andresolveExecutionContextnow delegates its API-key branch toresolveApiKeyPrincipal.@objectstack/rest—resolveExecCtxresolves the data engine once and triesresolveApiKeyPrincipal(x-api-key /Authorization: ApiKey) BEFORE the session, so/api/v1/data+/api/v1/metanow authenticate an API key under the key's permissions + RLS, exactly like the dispatcher/MCP path.
Tests: core
api-key.test.ts(primitives + verifier: valid / revoked / expired / unknown / owner-less / plaintext-not-matched / fail-closed-ql). runtime + rest suites green.
- Updated dependencies [a46c017]
- Updated dependencies [b990b89]
- Updated dependencies [99111ec]
- Updated dependencies [d5a8161]
- Updated dependencies [5cf1f1b]
- Updated dependencies [9ef89d4]
- Updated dependencies [3306d2f]
- Updated dependencies [bc44195]
- Updated dependencies [9e2e229]
- @objectstack/spec@8.0.0
- @objectstack/spec@7.9.0
- Updated dependencies [06f2bbb]
- Updated dependencies [36719db]
- Updated dependencies [424ab26]
- @objectstack/spec@7.8.0
- Updated dependencies [b391955]
- Updated dependencies [f06b64e]
- Updated dependencies [023bf93]
- @objectstack/spec@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 [55866f5]
- Updated dependencies [60f9c45]
- @objectstack/spec@7.6.0
- @objectstack/spec@7.5.0
- @objectstack/spec@7.4.1
- Updated dependencies [23c7107]
- Updated dependencies [c72daad]
- Updated dependencies [f115182]
- Updated dependencies [2faf9f2]
- Updated dependencies [2faf9f2]
- Updated dependencies [2faf9f2]
- Updated dependencies [58b450b]
- Updated dependencies [82eb6cf]
- Updated dependencies [13d8653]
- Updated dependencies [ff3d006]
- Updated dependencies [5e831de]
- @objectstack/spec@7.4.0
-
5e7c554: Rename kernel plugin-sandbox permission schemas to remove a naming footgun (issue #1383).
@objectstack/spec/kernelexportedPermissionSchema/PermissionSetSchema(and thePermission/PermissionSettypes) for the plugin-sandbox security model. Their names collided with the metadata-protocol permission set exported from@objectstack/spec/security(PermissionSetSchema), making it very easy to validate thepermission/profilemetadata type against the wrong schema and reject every legal payload.The kernel symbols are now prefixed with
Pluginto reflect their specialized semantics:Old ( @objectstack/spec/kernel)New PermissionSchemaPluginPermissionSchemaPermissionSetSchemaPluginPermissionSetSchemaPermission(type)PluginPermissionPermissionSet(type)PluginPermissionSetThe metadata
permission/profiletypes are unchanged — keep usingPermissionSetSchemafrom@objectstack/spec/security. -
Updated dependencies [5e7c554]
- @objectstack/spec@7.3.0
- @objectstack/spec@7.2.1
- @objectstack/spec@7.2.0
- Updated dependencies [47a92f4]
- @objectstack/spec@7.1.0
- Updated dependencies [74470ad]
- Updated dependencies [d29617e]
- Updated dependencies [dc72172]
- @objectstack/spec@7.0.0
- @objectstack/spec@6.9.0
- @objectstack/spec@6.8.1
- Updated dependencies [6e88f77]
- Updated dependencies [c8b9f57]
- @objectstack/spec@6.8.0
- @objectstack/spec@6.7.1
- Updated dependencies [430067b]
- Updated dependencies [4f9e9d4]
- @objectstack/spec@6.7.0
- Updated dependencies [a49cfc2]
- @objectstack/spec@6.6.0
- @objectstack/spec@6.5.1
- @objectstack/spec@6.5.0
- Updated dependencies [f8651cc]
- Updated dependencies [f8651cc]
- Updated dependencies [0bf6f9a]
- @objectstack/spec@6.4.0
- @objectstack/spec@6.3.0
- Updated dependencies [b4c74a9]
- @objectstack/spec@6.2.0
- @objectstack/spec@6.1.1
- Updated dependencies [93c0589]
- @objectstack/spec@6.1.0
- Updated dependencies [629a716]
- Updated dependencies [dbc4f7d]
- Updated dependencies [944f187]
- @objectstack/spec@6.0.0
- Updated dependencies [bab2b20]
- Updated dependencies [fa011d8]
- Updated dependencies [b806f58]
- @objectstack/spec@5.2.0
- Updated dependencies [75f4ee6]
- Updated dependencies [823d559]
- @objectstack/spec@5.1.0
- Updated dependencies [2f9073a]
- @objectstack/spec@5.0.0
- Updated dependencies [2869891]
- @objectstack/spec@4.2.0
- @objectstack/spec@4.1.1
- Updated dependencies [2108c30]
- Updated dependencies [23db640]
- @objectstack/spec@4.1.0
- 15e0df6: chore: unify all package versions to a single patch release
- Updated dependencies [15e0df6]
- @objectstack/spec@4.0.5
- Updated dependencies [326b66b]
- @objectstack/spec@4.0.4
- @objectstack/spec@4.0.3
- Updated dependencies [5f659e9]
- @objectstack/spec@4.0.2
-
e0b0a78: Deprecate DataEngineQueryOptions in favor of QueryAST-aligned EngineQueryOptions.
Engine, Protocol, and Client now use standard QueryAST parameter names:
filter→whereselect→fieldssort→orderByskip→offsetpopulate→expandtop→limit
The old DataEngine* schemas and types are preserved with
@deprecatedmarkers for backward compatibility.
- Updated dependencies [f08ffc3]
- Updated dependencies [e0b0a78]
- @objectstack/spec@4.0.0
- @objectstack/spec@3.3.1
- @objectstack/spec@3.3.0
- @objectstack/spec@3.2.9
- @objectstack/spec@3.2.8
- @objectstack/spec@3.2.7
- @objectstack/spec@3.2.6
- @objectstack/spec@3.2.5
- @objectstack/spec@3.2.4
- @objectstack/spec@3.2.3
- Updated dependencies [46defbb]
- @objectstack/spec@3.2.2
- Updated dependencies [850b546]
- @objectstack/spec@3.2.1
- Updated dependencies [5901c29]
- @objectstack/spec@3.2.0
- Updated dependencies [953d667]
- @objectstack/spec@3.1.1
- Updated dependencies [0088830]
- @objectstack/spec@3.1.0
- Updated dependencies [92d9d99]
- @objectstack/spec@3.0.11
- Updated dependencies [d1e5d31]
- @objectstack/spec@3.0.10
- Updated dependencies [15e0df6]
- @objectstack/spec@3.0.9
- Updated dependencies [5a968a2]
- @objectstack/spec@3.0.8
- Updated dependencies [0119bd7]
- Updated dependencies [5426bdf]
- @objectstack/spec@3.0.7
- Updated dependencies [5df254c]
- @objectstack/spec@3.0.6
- Updated dependencies [23a4a68]
- @objectstack/spec@3.0.5
- Updated dependencies [d738987]
- @objectstack/spec@3.0.4
- c7267f6: Patch release for maintenance updates and improvements.
- Updated dependencies [c7267f6]
- @objectstack/spec@3.0.3
- Updated dependencies [28985f5]
- @objectstack/spec@3.0.2
- Updated dependencies [389725a]
- @objectstack/spec@3.0.1
- Release v3.0.0 — unified version bump for all ObjectStack packages.
- Updated dependencies
- @objectstack/spec@3.0.0
- Updated dependencies
- @objectstack/spec@2.0.7
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.6
- Updated dependencies
- @objectstack/spec@2.0.5
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.4
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.3
- Updated dependencies [1db8559]
- @objectstack/spec@2.0.2
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@2.0.1
- Updated dependencies [38e5dd5]
- Updated dependencies [38e5dd5]
- @objectstack/spec@2.0.0
- chore: add Vercel deployment configs, simplify console runtime configuration
- Updated dependencies
- @objectstack/spec@1.0.12
- @objectstack/spec@1.0.11
- 10f52e1: fix: silence unhandled promise rejections when checking for async services in kernel
- @objectstack/spec@1.0.10
- @objectstack/spec@1.0.9
- @objectstack/spec@1.0.8
- @objectstack/spec@1.0.7
- Updated dependencies [a7f7b9d]
- @objectstack/spec@1.0.6
- b1d24bd: refactor: migrate build system from tsc to tsup for faster builds
- Replaced
tscwithtsup(using esbuild) across all packages - Added shared
tsup.config.tsin workspace root - Added
tsupas workspace dev dependency - significantly improved build performance
- Replaced
- Updated dependencies [b1d24bd]
- @objectstack/spec@1.0.5
- @objectstack/spec@1.0.4
- fb2eabd: fix: resolve "process is not defined" runtime error in browser environments by adding safe environment detection and polyfills
- @objectstack/spec@1.0.3
-
a0a6c85: Infrastructure and development tooling improvements
- Add changeset configuration for automated version management
- Add comprehensive GitHub Actions workflows (CI, CodeQL, linting, releases)
- Add development configuration files (.cursorrules, .github/prompts)
- Add documentation files (ARCHITECTURE.md, CONTRIBUTING.md, workflows docs)
- Update test script configuration in package.json
- Add @objectstack/cli to devDependencies for better development experience
-
109fc5b: Unified patch release to align all package versions.
-
Updated dependencies [a0a6c85]
-
Updated dependencies [109fc5b]
- @objectstack/spec@1.0.2
- @objectstack/spec@1.0.1
- Major version release for ObjectStack Protocol v1.0.
- Stabilized Protocol Definitions
- Enhanced Runtime Plugin Support
- Fixed Type Compliance across Monorepo
- Updated dependencies
- @objectstack/spec@1.0.0
- Updated dependencies
- @objectstack/spec@0.9.2
- Patch release for maintenance and stability improvements. All packages updated with unified versioning.
- Updated dependencies
- @objectstack/spec@0.9.1
- Updated dependencies [555e6a7]
- @objectstack/spec@0.8.2
- @objectstack/spec@0.8.1
-
This release includes a major upgrade to the core validation engine (Zod v4) and aligns all protocol definitions with stricter type safety.
- Updated dependencies
- @objectstack/spec@1.0.0
- fb41cc0: Patch release: Updated documentation and JSON schemas
- Updated dependencies [fb41cc0]
- @objectstack/spec@0.7.2
- Updated dependencies
- @objectstack/spec@0.7.1
- Patch release for maintenance and stability improvements
- Updated dependencies
- @objectstack/spec@0.6.1
-
b2df5f7: Unified version bump to 0.5.0
- Standardized all package versions to 0.5.0 across the monorepo
- Fixed driver-memory package.json paths for proper module resolution
- Ensured all packages are in sync for the 0.5.0 release
- Updated dependencies [b2df5f7]
- @objectstack/spec@0.6.0