Skip to content

Commit 5a53515

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-21018-generate-picklist
2 parents b5ecf9b + 70dae53 commit 5a53515

26 files changed

Lines changed: 1543 additions & 74 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/runtime': patch
4+
'@objectstack/metadata-protocol': patch
5+
---
6+
7+
feat(spec): discovery reports which optional `/auth` route families are mounted, starting with the better-auth admin family (`authFamilies.admin`) (#21046)
8+
9+
Clause-②: yes
10+
11+
**New key.** `DiscoverySchema` declares an optional `authFamilies` block, `{ admin: boolean }`. `admin` says whether the better-auth admin family (`{routes.auth}/admin/*`: `list-users`, `set-role`, `update-user`, `ban-user`, …) is mounted on this deployment. On a deployment that does not enable the admin plugin those routes answer a plain `404`, the same as a mistyped path, so a caller checks `authFamilies.admin` before building a URL into the family. `@objectstack/spec/api` also exports the block's schema (`AuthFamiliesSchema`, type `AuthFamilies`) and its reader, `readAuthFamilies(authService)`.
12+
13+
**Same answer as `/auth/config`.** The value is the auth service's own `getPublicConfig().features.admin`, the object `GET /api/v1/auth/config` serves. Both discovery producers read it through `readAuthFamilies`: `getDiscovery()` in `@objectstack/metadata-protocol` (served by `@objectstack/rest` at `GET /api/v1/discovery`) and `getDiscoveryInfo()` in `@objectstack/runtime` (served at `GET /.well-known/objectstack`). Neither re-derives whether the admin plugin is on, so on one boot the two documents and `/auth/config` agree. On a stock boot `authFamilies.admin` is `false`. With the admin plugin on (`plugins.admin: true`, or SCIM, which forces it on) it is `true`.
14+
15+
**When the key is absent.** A producer that cannot read the answer emits no `authFamilies`, rather than a guessed `false`. That happens when no `auth` service is registered (then `routes.auth` is absent too), when the registered service has no `getPublicConfig()`, or when that call throws (`/auth/config` answers `500 AUTH_CONFIG_ERROR` in that state). Treat an absent block as "not known to be mounted".
16+
17+
**What did not change.** No existing key, route or status moved. The unmounted admin routes still answer a plain `404`.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
'@objectstack/cli': patch
3+
'@objectstack/runtime': minor
4+
---
5+
6+
fix(cli): `os migrate plan` / `apply` no longer run the app's `onEnable` or a host plugin's post-declaration hooks during their boot
7+
8+
Clause-②: yes (widening)
9+
10+
The two schema commands boot the host's stack to read what it declares. That boot ran the
11+
config's `onEnable`, and every `kernel:bootstrapped` / `kernel:listening` hook a host plugin
12+
registered from `init()`. A hook that reads a table the plan does not declare then failed on
13+
every plan. On `examples/app-crm`, whose `onEnable` binds positions to permission sets, each
14+
plan printed six `[sql-driver] DATABASE_ERROR` lines and six `position binding lookup failed`
15+
warnings, on a database `apply` had just migrated as well as on an absent one.
16+
17+
The boot now composes host code for its declarations only:
18+
19+
- `AppPlugin` takes a new `skipOnEnable` option. When it is set, `start()` does not run the
20+
bundle's `onEnable`, logs that it withheld it, and reports it through `onEnableWithheld`. The
21+
migrate commands set it on the app they compose from `objectstack.config.ts`.
22+
- A host plugin's `init()` gets a context that does not register `kernel:bootstrapped` or
23+
`kernel:listening` hooks. The kernel contract defines those phases as work after registration
24+
ends: reconcile/backfill, and opening listeners. `kernel:ready` hooks still run, and the
25+
write guard still refuses their row writes. `kernel:shutdown` hooks and data hooks register
26+
as before.
27+
- The plan's notes, and the `--json` payload's `composition.notes`, carry one line naming what
28+
was not run.
29+
30+
The plan itself is unchanged: the same tables, the same pending DDL, the same drift. `apply`
31+
still flushes the DDL the operator confirms and still runs the coverage pass. The platform's own
32+
plugins are untouched, so the value-shape gate announcement still prints.
33+
34+
`@objectstack/runtime` widens its public surface, additively: `AppPlugin`, exported from the
35+
package root, gains the optional constructor option `skipOnEnable` (default `false`) and the
36+
read-only getter `onEnableWithheld`. A composition that does not pass the option gets exactly
37+
the behaviour it had, `onEnable` included.

‎content/docs/references/api/discovery.mdx‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Discovery schema — API Protocol reference
33
navTitle: Discovery
4-
description: "Discovery schemas of the ObjectStack API Protocol: ApiRoutes, CapabilityDescriptor and 9 more — each property with its type, default and a TypeScript example."
4+
description: "Discovery schemas of the ObjectStack API Protocol: ApiRoutes, AuthFamilies and 10 more — each property with its type, default and a TypeScript example."
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -13,8 +13,8 @@ description: "Discovery schemas of the ObjectStack API Protocol: ApiRoutes, Capa
1313
## TypeScript Usage
1414

1515
```typescript
16-
import { ApiRoutesSchema, CapabilityDescriptorSchema, DiscoverySchema, DiscoveryEnvironmentSchema, EnvironmentTypeSchema, RouteHealthEntrySchema, RouteHealthReportSchema, ServiceInfoSchema, ServiceSelfInfoSchema, ServiceStatus, WellKnownCapabilitiesSchema } from '@objectstack/spec/api';
17-
import type { ApiRoutes, CapabilityDescriptor, DiscoveryEnvironment, EnvironmentType, RouteHealthEntry, RouteHealthReport, ServiceInfo, ServiceSelfInfo, ServiceStatus, WellKnownCapabilities } from '@objectstack/spec/api';
16+
import { ApiRoutesSchema, AuthFamiliesSchema, CapabilityDescriptorSchema, DiscoverySchema, DiscoveryEnvironmentSchema, EnvironmentTypeSchema, RouteHealthEntrySchema, RouteHealthReportSchema, ServiceInfoSchema, ServiceSelfInfoSchema, ServiceStatus, WellKnownCapabilitiesSchema } from '@objectstack/spec/api';
17+
import type { ApiRoutes, AuthFamilies, CapabilityDescriptor, DiscoveryEnvironment, EnvironmentType, RouteHealthEntry, RouteHealthReport, ServiceInfo, ServiceSelfInfo, ServiceStatus, WellKnownCapabilities } from '@objectstack/spec/api';
1818

1919
// Validate data
2020
const result = ApiRoutesSchema.parse(data);
@@ -47,6 +47,19 @@ const result = ApiRoutesSchema.parse(data);
4747
| **mcp** | `string` | optional | e.g. /api/v1/mcp — always the unscoped base; absent when MCP is disabled or unserveable |
4848

4949

50+
---
51+
52+
## AuthFamilies
53+
54+
Which optional better-auth route families are mounted under routes.auth
55+
56+
### Properties
57+
58+
| Property | Type | Required | Description |
59+
| :--- | :--- | :--- | :--- |
60+
| **admin** | `boolean` | ✅ | Whether the better-auth admin family (`{routes.auth}`/admin/*: list-users, set-role, update-user, ban-user, …) is mounted. Same value as features.admin on GET `{routes.auth}`/config, read from the same source; false means those routes answer a plain 404 on this deployment. |
61+
62+
5063
---
5164

5265
## CapabilityDescriptor
@@ -77,6 +90,7 @@ const result = ApiRoutesSchema.parse(data);
7790
| **capabilities** | `{ comments: object; automation: object; cron: object; search: object; … }` | ✅ | Hierarchical capability descriptors — the full WellKnownCapabilities vocabulary, every key present |
7891
| **schemaDiscovery** | `{ openapi?: string; jsonSchema?: string }` | optional | Schema discovery endpoints for API toolchain integration |
7992
| **scoping** | `{ enabled: boolean; resolution: Enum<'required' \| 'optional' \| 'auto'>; scoped: boolean; environmentId?: string }` | optional | Environment-scoping posture, added by the REST discovery endpoint |
93+
| **authFamilies** | `{ admin: boolean }` | optional | Which optional better-auth route families are mounted under routes.auth — the same source as GET `{routes.auth}`/config features; absent when no auth service answers |
8094
| **metadata** | `Record<string, any>` | optional | Custom metadata key-value pairs for extensibility |
8195

8296
### Nested Shape: `Discovery.routes`
@@ -148,6 +162,12 @@ const result = ApiRoutesSchema.parse(data);
148162
| **scoped** | `boolean` | ✅ | Whether THIS response was served from the environment-scoped mount |
149163
| **environmentId** | `string` | optional | The resolved environment id — present only on a scoped mount |
150164

165+
### Nested Shape: `Discovery.authFamilies`
166+
167+
| Property | Type | Required | Description |
168+
| :--- | :--- | :--- | :--- |
169+
| **admin** | `boolean` | ✅ | Whether the better-auth admin family (`{routes.auth}`/admin/*: list-users, set-role, update-user, ban-user, …) is mounted. Same value as features.admin on GET `{routes.auth}`/config, read from the same source; false means those routes answer a plain 404 on this deployment. |
170+
151171

152172
---
153173

‎content/docs/references/api/protocol.mdx‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,7 @@ Enable package response
999999
| **capabilities** | `{ comments: object; automation: object; cron: object; search: object; … }` | optional | Hierarchical capability descriptors — the full WellKnownCapabilities vocabulary, every key present |
10001000
| **schemaDiscovery** | `{ openapi?: string; jsonSchema?: string }` | optional | Schema discovery endpoints for API toolchain integration |
10011001
| **scoping** | `{ enabled: boolean; resolution: Enum<'required' \| 'optional' \| 'auto'>; scoped: boolean; environmentId?: string }` | optional | Environment-scoping posture, added by the REST discovery endpoint |
1002+
| **authFamilies** | `{ admin: boolean }` | optional | Which optional better-auth route families are mounted under routes.auth — the same source as GET `{routes.auth}`/config features; absent when no auth service answers |
10021003
| **metadata** | `Record<string, any>` | optional | Custom metadata key-value pairs for extensibility |
10031004
| **apiName** | `string` | optional | API name (deprecated — use `name`; removed in protocol 18) |
10041005

@@ -1071,6 +1072,12 @@ Enable package response
10711072
| **scoped** | `boolean` | ✅ | Whether THIS response was served from the environment-scoped mount |
10721073
| **environmentId** | `string` | optional | The resolved environment id — present only on a scoped mount |
10731074

1075+
### Nested Shape: `GetDiscoveryResponse.authFamilies`
1076+
1077+
| Property | Type | Required | Description |
1078+
| :--- | :--- | :--- | :--- |
1079+
| **admin** | `boolean` | ✅ | Whether the better-auth admin family (`{routes.auth}`/admin/*: list-users, set-role, update-user, ban-user, …) is mounted. Same value as features.admin on GET `{routes.auth}`/config, read from the same source; false means those routes answer a plain 404 on this deployment. |
1080+
10741081

10751082
---
10761083

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Protocol reference — every schema by module
33
navTitle: Protocol Reference
4-
description: Every schema published by @objectstack/spec — 1521 schemas across 14 protocol modules
4+
description: Every schema published by @objectstack/spec — 1522 schemas across 14 protocol modules
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -21,7 +21,7 @@ counts are sums of the rows they head. Regenerate with
2121
| Module | Pages | Schemas | Description |
2222
| :--- | ---: | ---: | :--- |
2323
| [AI Protocol](/docs/references/ai) | 12 | 68 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. |
24-
| [API Protocol](/docs/references/api) | 32 | 429 | REST contracts, endpoints, routing, realtime, batch, discovery. |
24+
| [API Protocol](/docs/references/api) | 32 | 430 | REST contracts, endpoints, routing, realtime, batch, discovery. |
2525
| [Automation Protocol](/docs/references/automation) | 14 | 75 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
2626
| [Data Protocol](/docs/references/data) | 30 | 178 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
@@ -34,7 +34,7 @@ counts are sums of the rows they head. Regenerate with
3434
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3535
| [System Protocol](/docs/references/system) | 34 | 275 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3636
| [UI Protocol](/docs/references/ui) | 16 | 166 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
37-
| **Total** | **197** | **1521** | 14 protocol modules |
37+
| **Total** | **197** | **1522** | 14 protocol modules |
3838

3939
---
4040

@@ -63,7 +63,7 @@ Agents, tools, skills, RAG and knowledge sources, model registry, conversations.
6363

6464
## API Protocol
6565

66-
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **32 pages, 429 schemas**
66+
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **32 pages, 430 schemas**
6767

6868
REST contracts, endpoints, routing, realtime, batch, discovery.
6969

@@ -75,7 +75,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
7575
| [`automation-api.zod.ts`](/docs/references/api/automation-api) | `AutomationApiErrorCode`, `AutomationFlowPathParams`, `AutomationRunPathParams`, `CreateFlowRequest`, `CreateFlowResponse`, `DeleteFlowRequest`, `DeleteFlowResponse`, `GetFlowRequest`, `GetFlowResponse`, `GetRunRequest`, `GetRunResponse`, `ListRunsRequest`, `ListRunsResponse`, `ResumeFailureDetails`, `ToggleFlowRequest`, `ToggleFlowResponse`, `TriggerFlowRequest`, `TriggerFlowResponse`, `UpdateFlowRequest`, `UpdateFlowResponse` |
7676
| [`batch.zod.ts`](/docs/references/api/batch) | `BatchConfig`, `BatchOperationResult`, `BatchOperationType`, `BatchOptions`, `BatchRecord`, `BatchUpdateRequest`, `BatchUpdateResponse`, `CrossObjectBatchDroppedFields`, `CrossObjectBatchOperation`, `CrossObjectBatchRequest`, `CrossObjectBatchResponse`, `DeleteManyRequest`, `UpdateManyRecord`, `UpdateManyRequest` |
7777
| [`contract.zod.ts`](/docs/references/api/contract) | `ApiError`, `BaseResponse`, `BatchLoadingStrategy`, `BulkRequest`, `BulkResponse`, `CreateRequest`, `DataLoaderConfig`, `DeleteResponse`, `ExportRequest`, `IdRequest`, `ListRecordResponse`, `ModificationResult`, `QueryOptimizationConfig`, `RecordData`, `SingleRecordResponse`, `UpdateRequest` |
78-
| [`discovery.zod.ts`](/docs/references/api/discovery) | `ApiRoutes`, `CapabilityDescriptor`, `Discovery`, `DiscoveryEnvironment`, `EnvironmentType`, `RouteHealthEntry`, `RouteHealthReport`, `ServiceInfo`, `ServiceSelfInfo`, `ServiceStatus`, `WellKnownCapabilities` |
78+
| [`discovery.zod.ts`](/docs/references/api/discovery) | `ApiRoutes`, `AuthFamilies`, `CapabilityDescriptor`, `Discovery`, `DiscoveryEnvironment`, `EnvironmentType`, `RouteHealthEntry`, `RouteHealthReport`, `ServiceInfo`, `ServiceSelfInfo`, `ServiceStatus`, `WellKnownCapabilities` |
7979
| [`dispatcher.zod.ts`](/docs/references/api/dispatcher) | `DispatcherConfig`, `DispatcherErrorCode`, `DispatcherErrorResponse`, `DispatcherRoute` |
8080
| [`documentation.zod.ts`](/docs/references/api/documentation) | `ApiChangelogEntry`, `ApiDocumentationConfig`, `ApiTestCollection`, `ApiTestRequest`, `ApiTestingUiConfig`, `ApiTestingUiType`, `CodeGenerationTemplate`, `GeneratedApiDocumentation`, `OpenApiSecurityScheme`, `OpenApiServer`, `OpenApiSpec` |
8181
| [`endpoint.zod.ts`](/docs/references/api/endpoint) | `ApiEndpoint`, `ApiMapping` |

‎docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate.
1919

2020
| Dir | Sites |
2121
|---|---|
22-
| `api/` | 432 |
22+
| `api/` | 433 |

0 commit comments

Comments
 (0)