Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/console-c6cfdf1288b6-backfill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"@objectstack/console": minor
---

Console (objectui) backfill for `2cb8d78e24ad...c6cfdf1288b6` — the one refresh in
the v17 window that landed with no changeset.

`scripts/bump-objectui.sh` emits a `@objectstack/console` changeset on every bump
precisely so a SHA move leaves a trace (see `docs/releases-maintenance.md`). One
bump in this window did not, so 25 commits — including two breaking ones — were
absent from the release history and from the curated v17 page. This entry records
them after the fact; it declares no new SHA move (`.objectui-sha` already points
past this range at `4a4829d0ef39`).

Frontend changes in this range:

- feat(react)!: trim dead device/preference delegates from useClientNotifications (objectstack#3612 companion) (#2862)
- feat(types)!: drop the ObjectStack/ObjectOS/ObjectQL/ObjectUI Capabilities re-exports (#2860)
- feat: gate detail/form edit & delete on the server's effective operation set (framework#3546) (#2832)
- feat(app-shell): approver values become record lookups (framework#3508) (#2834)
- feat(console): group tenancy posture affordances — org switcher as write context + org attribution (ADR-0105 Phase 1) (#2858)
- feat(console): i18n the system-settings hub (objectui#2851 P2) (#2859)
- fix(dashboard,charts): resolve `{current_user_id}` in widget filters (framework#3574) (#2857)
- fix(grid): validate email format in the import preview (objectstack#3566) (#2840)
- fix(fields): consistent image-field rendering + click-to-zoom (#2836) (#2837)
- fix(app-shell): stop the flow-node repeater from committing during render (#2838) (#2839)

Plus 15 dependency bumps, three of them major for the Console's own build:
`maplibre-gl` 5→6, `chalk` 5→6, `jsdom` 29→30 (dev).

objectui range: `2cb8d78e24ad...c6cfdf1288b6`
19 changes: 19 additions & 0 deletions .changeset/spec-changes-manifest-catchup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@objectstack/spec": patch
---

fix(spec): regenerate the ADR-0087 change manifest the RC version bump left stale

The v17 RC bump moved `PROTOCOL_VERSION` to `17.0.0` but did not re-run
`gen:spec-changes` / `gen:upgrade-guide`, so the published `spec-changes.json`
still declared `protocolVersion: 16.0.0` with no protocol-17 entries — while the
protocol-17 conversions (`execute`→`target`, `conditionalRequired`→`requiredWhen`,
`knowledge.topics`→`sources`, `agent.tools` removal, sharing `full`→`edit`) were
already registered and applied at load. Anything projecting the manifest (the
generated upgrade guide, the `spec_changes` MCP tool) reported a 16-era chain on
a 17 protocol.

`check:spec-changes` caught it, but only on the first PR to touch a
generated-artifacts path after the bump — this regenerates both projections and
turns the gate green again. `docs/protocol-upgrade-guide.md` now carries the
Protocol 16 → 17 section.
52 changes: 52 additions & 0 deletions .changeset/v17-page-console-gap-and-nav.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
---

Docs-only: close two gaps in the v17 release page and make the Releases section
reachable from the sidebar again. Releases nothing.

**The Console section was missing 25 objectui commits.** The v17 frontend window
is `cf2d56e32a11` (what 16.1.0 pinned) → `4a4829d0ef39` = 128 commits, but the
pending `console-*.md` changesets only covered 103 of them: the range
`2cb8d78e24ad...c6cfdf1288b6` landed with no changeset, which is exactly the
"a SHA bump leaves no trace" failure `scripts/bump-objectui.sh` exists to
prevent. Two of the missing commits are breaking, and both are the frontend half
of a backend change the page already documented — so the page described one side
of each and omitted the other:

- `useClientNotifications` loses its dead device/preference delegates
(objectui#2862), the companion to the SDK removal in #3612
- `@object-ui/types` drops the Capabilities re-exports (objectui#2860), the
companion to the retired capabilities-descriptor cluster

Both now appear beside their backend halves and in the upgrade checklist, along
with the other eight substantive commits (effective-operation-set gating on
detail/form, approver record lookups, `{current_user_id}` in widget filters, org
switcher as write context, system-settings i18n, image click-to-zoom, import
email validation, flow-node repeater render fix) and the three major build-dep
bumps. `.changeset/console-c6cfdf1288b6-backfill.md` records the range itself so
the history no longer has a hole.

**`releases` returns to the root sidebar.** #3423 took References and Releases
out of the tree as a decluttering pass, on the stated basis that "URLs still
resolve and the docs home page links both". That trade holds for References (22
generated pages) but reads poorly for Releases during a release window, when
finding the upgrade notes is the reason someone opened the docs. References stays
out.

**First v17 docs sweep** (`docs/v17-docs-sweep.md` — playbook + append-only run
log, designed for re-runs while the RC train keeps accumulating changesets).
Run 1 fixed the drift v17 left in hand-written prose: `services-checklist`
(17→16 services, graphql rows, the dissolved `ObjectStackProtocol`),
authorization/permissions pages still describing `/graphql` in the
anonymous-deny matrix and owner/`group` sharing shapes as "declared but not
enforced" (they no longer parse), `http-protocol` claiming `enable.trash`
exists, "REST/GraphQL" in objectql diagrams/prose, and the `objectstack-api`
skill description (mirrors regenerated via `gen:skill-docs`).

**`check-release-notes` now checks what it claims.** Its success line said every
release page is "navigable" while it only ever read the section's own
`meta.json` — it could not see that the section had been unlisted from the root
nav, so it stayed green for the entire period the whole section was absent from
the sidebar. It now also requires the section to be reachable via the root nav
**or** a `/docs/releases` link on the docs home page: either satisfies it, so
#3423's trade remains available, but losing both now fails the build.
6 changes: 3 additions & 3 deletions content/docs/ai/skills-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ ObjectStack ships **9 domain-specific skills**. Each is self-contained — an AI
| 4 | [UI](#ui) | `ui` | `skills/objectstack-ui/` | Author ObjectStack UI metadata — Views (list/form/kanban/calendar/gantt), Apps (navigation), Pages (structured plus the HTML and React source-authoring tiers, ADR-0080/0081), Dashboards, Reports, Charts, Actions, and package Docs (`src/docs/*.md`). |
| 5 | [Automation](#automation) | `automation` | `skills/objectstack-automation/` | Design ObjectStack automation — Flows (visual logic), Triggers, Approvals, state machines, scheduled jobs, and webhooks. |
| 6 | [AI](#ai) | `ai` | `skills/objectstack-ai/` | Design ObjectStack AI skills, tools, knowledge sources, conversations, model registry entries, and MCP integrations. |
| 7 | [API](#api) | `api` | `skills/objectstack-api/` | Design the server-side API surface that an ObjectStack runtime exposes — REST/GraphQL endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts. |
| 7 | [API](#api) | `api` | `skills/objectstack-api/` | Design the server-side API surface that an ObjectStack runtime exposes — REST endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts. |
| 8 | [i18n](#i18n) | `i18n` | `skills/objectstack-i18n/` | Author ObjectStack translation bundles — object/field labels, view text, app navigation strings, automation messages — and configure locale fallback, coverage reporting, and the per-locale source layout. |
| 9 | [Formula](#formula) | `expression` | `skills/objectstack-formula/` | Author CEL expressions used across ObjectStack — formula fields, field conditional rules (`visibleWhen`, `readonlyWhen`, `requiredWhen`), validation / sharing / visibility predicates, flow conditions, and dynamic seed values. |

Expand Down Expand Up @@ -145,9 +145,9 @@ Do not use for general LLM prompting questions unrelated to ObjectStack metadata

**Domain** `api` · **Path** `skills/objectstack-api/`

Design the server-side API surface that an ObjectStack runtime exposes — REST/GraphQL endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts.
Design the server-side API surface that an ObjectStack runtime exposes — REST endpoints, auth providers, realtime channels, error envelopes, batch/versioning contracts.

Use when the user is adding `*.endpoint.ts`, configuring auth providers, defining custom routes, or extending the REST/GraphQL generator.
Use when the user is adding `*.endpoint.ts`, configuring auth providers, defining custom routes, or extending the REST generator.

Do not use for: consuming an ObjectStack API from a client (that is just standard HTTP — no skill needed); the auto-generated CRUD endpoints (those follow from objectstack-data); request-side query syntax (see objectstack-query). CEL expressions in route guards or auth predicates: load objectstack-formula alongside.

Expand Down
15 changes: 6 additions & 9 deletions content/docs/kernel/services-checklist.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ for the protocol-method inventory; treat the per-service status columns as
historical until this page is regenerated.
</Callout>

The ObjectStack protocol defines **17 kernel services** registered via the `CoreServiceName` enum. Each service maps to a set of protocol methods (57 total), governed by the `ObjectStackProtocol` interface.
The ObjectStack protocol defines **16 kernel services** registered via the `CoreServiceName` enum (the never-implemented `graphql` entry was removed in v17). Each service maps to a set of protocol methods governed by its per-domain contract (`DataProtocol`, `MetadataProtocol`, ...) — the transitional `ObjectStackProtocol` composition alias was dissolved in v17 (ADR-0076 D9); capability availability comes from the runtime discovery `services` registry.

**Key architecture principle**: The kernel itself only provides **data** and **metadata** (framework). Everything else — including **auth** and **automation** — is delivered by **plugins**. The current `@objectstack/objectql` package is an example kernel implementation to get the basic API running; production kernels will be rebuilt as new plugins.

Expand Down Expand Up @@ -42,7 +42,7 @@ The ObjectStack protocol defines **17 kernel services** registered via the `Core
├─────────────────────────────────────────────────────────┤
│ Plugin Layer │
│ All other services: auth, automation, workflow, ui, │
│ realtime, notification, ai, i18n, graphql, search, │
│ realtime, notification, ai, i18n, search,
│ file-storage, cache, queue, job │
│ │
│ Discovery API reports: enabled/unavailable/degraded │
Expand Down Expand Up @@ -72,7 +72,6 @@ The ObjectStack protocol defines **17 kernel services** registered via the `Core
| 14 | **cache** | `core` | — | ✅ Built-in (in-memory fallback) | `@objectstack/service-cache` |
| 15 | **queue** | `core` | — | ✅ Built-in (in-memory fallback) | `@objectstack/service-queue` |
| 16 | **job** | `core` | — | ✅ Built-in (in-memory fallback) | `@objectstack/service-job` |
| 17 | **graphql** | `optional` | — | ❌ Plugin Required | TBD plugin |

<Callout type="warn">
**Criticality Levels**
Expand Down Expand Up @@ -282,7 +281,7 @@ The i18n service is a **core built-in service** with automatic in-memory fallbac
// Production (overrides built-in fallback)
kernel.use(new I18nServicePlugin({ defaultLocale: 'en', localesDir: './i18n' }));

// Development (automatic — DevPlugin registers i18n stub for all 17 core services)
// Development (automatic — DevPlugin registers i18n stub for all 16 core services)
kernel.use(new DevPlugin());

// No plugin required — kernel auto-injects in-memory fallback if none registered
Expand Down Expand Up @@ -331,9 +330,9 @@ AppPlugin will:

---

## 12–17. Infrastructure Services
## 12–16. Infrastructure Services

`cache`, `queue`, and `job` are `core` services: like `i18n`, the kernel auto-injects an in-memory fallback when no plugin registers them (see `CORE_FALLBACK_FACTORIES` in `packages/core/src/fallbacks/`). The `optional` services (`file-storage`, `search`, `graphql`) stay disabled until a plugin provides them.
`cache`, `queue`, and `job` are `core` services: like `i18n`, the kernel auto-injects an in-memory fallback when no plugin registers them (see `CORE_FALLBACK_FACTORIES` in `packages/core/src/fallbacks/`). The `optional` services (`file-storage`, `search`) stay disabled until a plugin provides them.

| Service | Description |
|:--------|:------------|
Expand All @@ -342,7 +341,6 @@ AppPlugin will:
| **cache** | General-purpose cache. In-memory fallback; Redis via `@objectstack/service-cache`. |
| **queue** | Message queue. In-memory fallback; BullMQ / Redis via `@objectstack/service-queue`. |
| **job** | Scheduled task execution. In-memory fallback; cron-based, concurrency control. |
| **graphql** | Auto-generate GraphQL schema from Object metadata. |

---

Expand Down Expand Up @@ -373,8 +371,7 @@ AppPlugin will:
|:-------|:------------|
| **plugin-realtime** | WebSocket / SSE real-time push |
| **plugin-notification** | Notification engine |
| **plugin-ai** | NLQ, chat, suggestions, insights |
| **plugin-graphql** | GraphQL API endpoint |
| **plugin-ai** | Chat, completion, models, conversations (`service-ai`, Cloud/EE) |
| **plugin-search** | Full-text search |
| **plugin-job** | Scheduled tasks |
| **plugin-queue** | Message queue |
Expand Down
3 changes: 2 additions & 1 deletion content/docs/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"deployment",
"plugins",
"kernel",
"protocol"
"protocol",
"releases"
]
}
4 changes: 2 additions & 2 deletions content/docs/permissions/authorization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ site — the file you read when behavior surprises you.

| # | Gate | What it decides | Enforcement site | Failure direction |
|---|---|---|---|---|
| 1 | **Anonymous deny** | No identity → HTTP 401. **Uniform across every HTTP surface that reaches object data** (#2567): REST `/data`, the metadata endpoints (`/meta`), the dispatcher GraphQL endpoint (`/graphql`), and the raw-hono standard `/data` routes — one shared decision, so a caller denied on `/data` can't read the same rows through a sibling door. **Default-on** (ADR-0056 D2): public serving requires an explicit `api.requireAuth: false` opt-out, which logs a boot warning. Control plane (`/auth`, `/health`, `/discovery`) is exempt; share-links validate their token then read as SYSTEM. | `packages/core/src/security/anonymous-deny.ts` `shouldDenyAnonymous` — called by `rest-server.ts` `enforceAuth`, the dispatcher `handleGraphQL`/`handleMetadata`/`handleAI`, and `plugin-hono-server` `denyAnonymous` (default in `packages/spec/src/api/rest-server.zod.ts`); a source-enumerating ratchet in `authz-conformance.test.ts` fails CI if a new surface ships ungated | fail-closed |
| 1 | **Anonymous deny** | No identity → HTTP 401. **Uniform across every HTTP surface that reaches object data** (#2567): REST `/data`, the metadata endpoints (`/meta`), and the raw-hono standard `/data` routes (the dispatcher GraphQL endpoint left the matrix when the GraphQL surface was removed in v17 — `/graphql` now 404s) — one shared decision, so a caller denied on `/data` can't read the same rows through a sibling door. **Default-on** (ADR-0056 D2): public serving requires an explicit `api.requireAuth: false` opt-out, which logs a boot warning. Control plane (`/auth`, `/health`, `/discovery`) is exempt; share-links validate their token then read as SYSTEM. | `packages/core/src/security/anonymous-deny.ts` `shouldDenyAnonymous` — called by `rest-server.ts` `enforceAuth`, the dispatcher `handleMetadata`/`handleAI`, and `plugin-hono-server` `denyAnonymous` (default in `packages/spec/src/api/rest-server.zod.ts`); a source-enumerating ratchet in `authz-conformance.test.ts` fails CI if a new surface ships ungated | fail-closed |
| 2 | **Public-form grant** | An anonymous form submission carries a declaration-derived `publicFormGrant` authorizing ONLY create + read-back on the form's declared target object — never anything else (ADR-0056 Option A). No guest-portal configuration needed (anonymous principals hold the `guest` position). | `packages/plugins/plugin-security/src/security-plugin.ts` (ObjectQL middleware) | scope-limited allow |
| 3 | **Object CRUD** | `allowRead/Create/Edit/Delete` (+ the destructive lifecycle class `allowTransfer/Restore/Purge`, gated ahead of the M2 operations — #1883) resolved across the caller's permission sets. | `packages/plugins/plugin-security/src/permission-evaluator.ts` `checkObjectPermission` | fail-closed 403 |
| 4 | **OWD / sharing** | Org-wide default (`private` / `public_read` / `public_read_write` / `controlled_by_parent`; **unset or unknown ⇒ `private`, fail-closed** — ADR-0090 D1) plus the external dial (`externalSharingModel`, ADR-0090 D11), manual record shares, criteria sharing rules (owner-type rules are declared but seed-skipped — [not enforced](/docs/permissions/sharing-rules#owner-based-sharing-rules)), business-unit hierarchy widening (ADR-0057 D5: scope-depth hierarchy lives on `sys_business_unit`, not positions). | `packages/plugins/plugin-sharing/src/sharing-service.ts` + `sharing-rule-service.ts` | fail-closed to owner-only |
| 4 | **OWD / sharing** | Org-wide default (`private` / `public_read` / `public_read_write` / `controlled_by_parent`; **unset or unknown ⇒ `private`, fail-closed** — ADR-0090 D1) plus the external dial (`externalSharingModel`, ADR-0090 D11), manual record shares, criteria sharing rules (owner-type rules were removed from the authoring surface in v17 rather than left declared-but-skipped — [Sharing Rules](/docs/permissions/sharing-rules#recipient-types)), business-unit hierarchy widening (ADR-0057 D5: scope-depth hierarchy lives on `sys_business_unit`, not positions). | `packages/plugins/plugin-sharing/src/sharing-service.ts` + `sharing-rule-service.ts` | fail-closed to owner-only |
| 5 | **Row-level security** | CEL predicates (`using` read filter, `check` write post-image) compiled into the query. If **no** applicable policy compiles, the result is a deny-all sentinel (fail-closed); an uncompilable policy alongside compilable ones is excluded from the OR-union **with a logged warning** — exclusion can only narrow access, never widen it. Tenant isolation is a wildcard RLS rule AND-ed on top. | `packages/plugins/plugin-security/src/rls-compiler.ts` + `security-plugin.ts` | fail-closed |
| 6 | **Field-level security** | Read mask (strip non-readable fields) + write deny per `fields` rules. Caller queries that **filter, sort, group, or aggregate by** a non-readable field are rejected outright (HTTP 403, `field_predicate_denied`) — masking only the output would leave row presence as a value oracle. RLS-injected predicates are exempt (they run after the guard and may reference hidden fields like `owner_id`). | `packages/plugins/plugin-security/src/field-masker.ts` + `predicate-guard.ts` | fail-closed on predicates; see posture note below |

Expand Down
2 changes: 1 addition & 1 deletion content/docs/permissions/field-level-security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fields: {
The client-side ObjectForm / inline grid hides non-editable fields from
the UI — but that is a **UX layer only**. The SecurityPlugin middleware
enforces field-level write rules on the server, regardless of how the
request arrived (REST, GraphQL, raw ObjectQL call).
request arrived (REST, SDK, raw ObjectQL call).

**On read** — `find` / `findOne` results have non-readable fields
stripped from every record before the response leaves the engine.
Expand Down
Loading
Loading