@objectstack/plugin-security 9.9.1 → 9.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -89,6 +89,8 @@ declare class SecurityPlugin implements Plugin {
89
89
  */
90
90
  private metadata;
91
91
  private ql;
92
+ /** ADR-0055: cache the resolved master-detail relation per controlled_by_parent object. */
93
+ private cbpRelCache;
92
94
  private dbLoader?;
93
95
  private logger;
94
96
  constructor(options?: SecurityPluginOptions);
@@ -143,6 +145,45 @@ declare class SecurityPlugin implements Plugin {
143
145
  * applies (caller adds no filter).
144
146
  */
145
147
  private computeRlsFilter;
148
+ /**
149
+ * Resolve a controlled_by_parent object's master-detail relation (the FK field
150
+ * key + the master object name), or null. Prefers a required `master_detail`
151
+ * field; falls back to any `master_detail`, then a required `lookup`. Cached.
152
+ */
153
+ private resolveCbpRelation;
154
+ /**
155
+ * ADR-0055 — master-detail "controlled by parent" READ derivation.
156
+ *
157
+ * For an object whose `sharingModel` is `controlled_by_parent`, access is
158
+ * derived from the master: return a filter `masterFK IN (<master ids this user
159
+ * can read>)`. The id set is resolved by running the MASTER's own read RLS
160
+ * (reused via `computeRlsFilter`) under a system context — no middleware
161
+ * re-entry, so no recursion. An empty set yields `{ masterFK: { $in: [] } }`,
162
+ * which matches no rows (fail closed). A misconfigured object (no
163
+ * master_detail/lookup to derive from) denies all reads (defense-in-depth;
164
+ * spec validation should prevent authoring it). Returns null when the object is
165
+ * not controlled_by_parent.
166
+ *
167
+ * v1 scope (ADR-0055): single level — the master's OWN controlled_by_parent is
168
+ * not traversed transitively; master accessibility is the master's RLS filter
169
+ * (sharing-service grants on the master are not folded in).
170
+ */
171
+ private computeControlledByParentFilter;
172
+ /**
173
+ * ADR-0055 — master-detail "controlled by parent" WRITE enforcement.
174
+ *
175
+ * A by-id write (insert/update/delete) to a controlled_by_parent detail
176
+ * requires EDIT access to its master: the caller must hold CRUD `update` on the
177
+ * master object AND the master row must be visible under the master's write RLS.
178
+ * This is the write-side companion to the read derivation — the RLS read filter
179
+ * never applies to a by-id write (the #1994 class), so without this a member
180
+ * could mutate a detail under a master they cannot edit. Throws on denial;
181
+ * no-op when the object is not controlled_by_parent.
182
+ *
183
+ * v1 scope: single-id writes. Bulk writes flow through the AST and are already
184
+ * scoped by the controlled-by-parent READ filter (to readable masters).
185
+ */
186
+ private assertControlledByParentWrite;
146
187
  /**
147
188
  * Collect all RLS policies from permission sets applicable to the given object/operation.
148
189
  */
@@ -230,9 +271,19 @@ interface RLSUserContext {
230
271
  * current user (incl. self). Pre-resolved by the runtime so RLS can
231
272
  * scope identity tables like `sys_user` via
232
273
  * `id IN (current_user.org_user_ids)` without needing subquery
233
- * support in the compiler.
274
+ * support in the compiler. This is the one well-known membership set;
275
+ * arbitrary §7.3.1 sets arrive via `ExecutionContext.rlsMembership`
276
+ * and are merged in under their own keys (see {@link RLSCompiler.compileFilter}).
234
277
  */
235
278
  org_user_ids?: string[];
279
+ /**
280
+ * The caller's unique, auth-enforced email. RLS expressions reference it as
281
+ * `current_user.email` for human-readable, *seedable* owner scoping
282
+ * (`owner = current_user.email`). Email is exposed because it is UNIQUE; the
283
+ * user's display `name` is deliberately NOT exposed — names collide, and a
284
+ * collision on an ownership predicate is an access-control leak.
285
+ */
286
+ email?: string;
236
287
  [key: string]: unknown;
237
288
  }
238
289
  /**
@@ -258,6 +309,11 @@ declare const RLS_DENY_FILTER: Record<string, unknown>;
258
309
  * Converts `using` / `check` expressions into ObjectQL-compatible filter conditions.
259
310
  */
260
311
  declare class RLSCompiler {
312
+ /** Optional logger so a SILENTLY-dropped (uncompilable-shape) policy is observable (ADR-0056 D4). */
313
+ private logger?;
314
+ setLogger(logger: {
315
+ warn?: (message: string, meta?: Record<string, unknown>) => void;
316
+ } | undefined): void;
261
317
  /**
262
318
  * Compile RLS policies into a query filter for the given user context.
263
319
  * Multiple policies for the same object/operation are OR-combined (any match allows access).
@@ -275,10 +331,17 @@ declare class RLSCompiler {
275
331
  /**
276
332
  * Compile a single RLS expression into a query filter.
277
333
  *
278
- * Supports simple expressions like:
279
- * - "field_name = current_user.property"
280
- * - "field_name IN (current_user.array_property)"
281
- * - "field_name = 'literal_value'"
334
+ * This reference compiler recognizes exactly four forms — anything else
335
+ * returns `null` and (via {@link compileFilter}) fails closed:
336
+ * - `field = current_user.property` → `{ field: <value> }`
337
+ * - `field = 'literal_value'` → `{ field: 'literal_value' }`
338
+ * - `field IN (current_user.array)` → `{ field: { $in: [...] } }`
339
+ * (the array may be a §7.3.1 pre-resolved membership set)
340
+ * - `1 = 1` → `{}` (always-true / no restriction)
341
+ *
342
+ * There is intentionally no support for subqueries, `LIKE`/`ILIKE`,
343
+ * regex, `ANY`/`ALL`, `AND`/`OR`/`NOT`, or `NULL` checks — express those
344
+ * needs as a `current_user.*` property the runtime pre-resolves instead.
282
345
  */
283
346
  compileExpression(expression: string, userCtx: RLSUserContext): Record<string, unknown> | null;
284
347
  /**
@@ -355,7 +418,7 @@ declare const securityObjects: ((Omit<{
355
418
  abstract: boolean;
356
419
  datasource: string;
357
420
  fields: Record<string, {
358
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
421
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
359
422
  required: boolean;
360
423
  searchable: boolean;
361
424
  multiple: boolean;
@@ -561,7 +624,7 @@ declare const securityObjects: ((Omit<{
561
624
  description?: string | undefined;
562
625
  icon?: string | undefined;
563
626
  tags?: string[] | undefined;
564
- managedBy?: "system" | "platform" | "config" | "append-only" | "better-auth" | undefined;
627
+ managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
565
628
  userActions?: {
566
629
  create?: boolean | undefined;
567
630
  import?: boolean | undefined;
@@ -790,6 +853,29 @@ declare const securityObjects: ((Omit<{
790
853
  titleField: string;
791
854
  progressField?: string | undefined;
792
855
  dependenciesField?: string | undefined;
856
+ colorField?: string | undefined;
857
+ parentField?: string | undefined;
858
+ typeField?: string | undefined;
859
+ baselineStartField?: string | undefined;
860
+ baselineEndField?: string | undefined;
861
+ groupByField?: string | undefined;
862
+ resourceView?: boolean | undefined;
863
+ assigneeField?: string | undefined;
864
+ effortField?: string | undefined;
865
+ capacity?: number | undefined;
866
+ tooltipFields?: (string | {
867
+ field: string;
868
+ label?: string | undefined;
869
+ })[] | undefined;
870
+ quickFilters?: {
871
+ field: string;
872
+ label?: string | undefined;
873
+ options?: (string | {
874
+ value: string | number;
875
+ label?: string | undefined;
876
+ })[] | undefined;
877
+ }[] | undefined;
878
+ autoZoomToFilter?: boolean | undefined;
793
879
  } | undefined;
794
880
  gallery?: {
795
881
  coverFit: "cover" | "contain";
@@ -953,7 +1039,7 @@ declare const securityObjects: ((Omit<{
953
1039
  clone: boolean;
954
1040
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
955
1041
  } | undefined;
956
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
1042
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
957
1043
  publicSharing?: {
958
1044
  enabled: boolean;
959
1045
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -985,7 +1071,7 @@ declare const securityObjects: ((Omit<{
985
1071
  } | {
986
1072
  language: "js";
987
1073
  source: string;
988
- capabilities: ("api.read" | "api.write" | "crypto.uuid" | "crypto.hash" | "log")[];
1074
+ capabilities: ("api.read" | "api.write" | "api.transaction" | "crypto.uuid" | "crypto.hash" | "log")[];
989
1075
  timeoutMs?: number | undefined;
990
1076
  memoryMb?: number | undefined;
991
1077
  } | undefined;
@@ -2677,7 +2763,7 @@ declare const securityObjects: ((Omit<{
2677
2763
  abstract: boolean;
2678
2764
  datasource: string;
2679
2765
  fields: Record<string, {
2680
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
2766
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
2681
2767
  required: boolean;
2682
2768
  searchable: boolean;
2683
2769
  multiple: boolean;
@@ -2883,7 +2969,7 @@ declare const securityObjects: ((Omit<{
2883
2969
  description?: string | undefined;
2884
2970
  icon?: string | undefined;
2885
2971
  tags?: string[] | undefined;
2886
- managedBy?: "system" | "platform" | "config" | "append-only" | "better-auth" | undefined;
2972
+ managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
2887
2973
  userActions?: {
2888
2974
  create?: boolean | undefined;
2889
2975
  import?: boolean | undefined;
@@ -3112,6 +3198,29 @@ declare const securityObjects: ((Omit<{
3112
3198
  titleField: string;
3113
3199
  progressField?: string | undefined;
3114
3200
  dependenciesField?: string | undefined;
3201
+ colorField?: string | undefined;
3202
+ parentField?: string | undefined;
3203
+ typeField?: string | undefined;
3204
+ baselineStartField?: string | undefined;
3205
+ baselineEndField?: string | undefined;
3206
+ groupByField?: string | undefined;
3207
+ resourceView?: boolean | undefined;
3208
+ assigneeField?: string | undefined;
3209
+ effortField?: string | undefined;
3210
+ capacity?: number | undefined;
3211
+ tooltipFields?: (string | {
3212
+ field: string;
3213
+ label?: string | undefined;
3214
+ })[] | undefined;
3215
+ quickFilters?: {
3216
+ field: string;
3217
+ label?: string | undefined;
3218
+ options?: (string | {
3219
+ value: string | number;
3220
+ label?: string | undefined;
3221
+ })[] | undefined;
3222
+ }[] | undefined;
3223
+ autoZoomToFilter?: boolean | undefined;
3115
3224
  } | undefined;
3116
3225
  gallery?: {
3117
3226
  coverFit: "cover" | "contain";
@@ -3275,7 +3384,7 @@ declare const securityObjects: ((Omit<{
3275
3384
  clone: boolean;
3276
3385
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
3277
3386
  } | undefined;
3278
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
3387
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
3279
3388
  publicSharing?: {
3280
3389
  enabled: boolean;
3281
3390
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -3307,7 +3416,7 @@ declare const securityObjects: ((Omit<{
3307
3416
  } | {
3308
3417
  language: "js";
3309
3418
  source: string;
3310
- capabilities: ("api.read" | "api.write" | "crypto.uuid" | "crypto.hash" | "log")[];
3419
+ capabilities: ("api.read" | "api.write" | "api.transaction" | "crypto.uuid" | "crypto.hash" | "log")[];
3311
3420
  timeoutMs?: number | undefined;
3312
3421
  memoryMb?: number | undefined;
3313
3422
  } | undefined;
@@ -5428,7 +5537,7 @@ declare const securityObjects: ((Omit<{
5428
5537
  abstract: boolean;
5429
5538
  datasource: string;
5430
5539
  fields: Record<string, {
5431
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
5540
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
5432
5541
  required: boolean;
5433
5542
  searchable: boolean;
5434
5543
  multiple: boolean;
@@ -5634,7 +5743,7 @@ declare const securityObjects: ((Omit<{
5634
5743
  description?: string | undefined;
5635
5744
  icon?: string | undefined;
5636
5745
  tags?: string[] | undefined;
5637
- managedBy?: "system" | "platform" | "config" | "append-only" | "better-auth" | undefined;
5746
+ managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
5638
5747
  userActions?: {
5639
5748
  create?: boolean | undefined;
5640
5749
  import?: boolean | undefined;
@@ -5863,6 +5972,29 @@ declare const securityObjects: ((Omit<{
5863
5972
  titleField: string;
5864
5973
  progressField?: string | undefined;
5865
5974
  dependenciesField?: string | undefined;
5975
+ colorField?: string | undefined;
5976
+ parentField?: string | undefined;
5977
+ typeField?: string | undefined;
5978
+ baselineStartField?: string | undefined;
5979
+ baselineEndField?: string | undefined;
5980
+ groupByField?: string | undefined;
5981
+ resourceView?: boolean | undefined;
5982
+ assigneeField?: string | undefined;
5983
+ effortField?: string | undefined;
5984
+ capacity?: number | undefined;
5985
+ tooltipFields?: (string | {
5986
+ field: string;
5987
+ label?: string | undefined;
5988
+ })[] | undefined;
5989
+ quickFilters?: {
5990
+ field: string;
5991
+ label?: string | undefined;
5992
+ options?: (string | {
5993
+ value: string | number;
5994
+ label?: string | undefined;
5995
+ })[] | undefined;
5996
+ }[] | undefined;
5997
+ autoZoomToFilter?: boolean | undefined;
5866
5998
  } | undefined;
5867
5999
  gallery?: {
5868
6000
  coverFit: "cover" | "contain";
@@ -6026,7 +6158,7 @@ declare const securityObjects: ((Omit<{
6026
6158
  clone: boolean;
6027
6159
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
6028
6160
  } | undefined;
6029
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
6161
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
6030
6162
  publicSharing?: {
6031
6163
  enabled: boolean;
6032
6164
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -6058,7 +6190,7 @@ declare const securityObjects: ((Omit<{
6058
6190
  } | {
6059
6191
  language: "js";
6060
6192
  source: string;
6061
- capabilities: ("api.read" | "api.write" | "crypto.uuid" | "crypto.hash" | "log")[];
6193
+ capabilities: ("api.read" | "api.write" | "api.transaction" | "crypto.uuid" | "crypto.hash" | "log")[];
6062
6194
  timeoutMs?: number | undefined;
6063
6195
  memoryMb?: number | undefined;
6064
6196
  } | undefined;
@@ -7271,7 +7403,7 @@ declare const securityObjects: ((Omit<{
7271
7403
  abstract: boolean;
7272
7404
  datasource: string;
7273
7405
  fields: Record<string, {
7274
- type: "number" | "boolean" | "tags" | "select" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
7406
+ type: "number" | "boolean" | "tags" | "select" | "email" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "lookup" | "master_detail" | "currency" | "percent" | "password" | "secret" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "toggle" | "multiselect" | "radio" | "checkboxes" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
7275
7407
  required: boolean;
7276
7408
  searchable: boolean;
7277
7409
  multiple: boolean;
@@ -7477,7 +7609,7 @@ declare const securityObjects: ((Omit<{
7477
7609
  description?: string | undefined;
7478
7610
  icon?: string | undefined;
7479
7611
  tags?: string[] | undefined;
7480
- managedBy?: "system" | "platform" | "config" | "append-only" | "better-auth" | undefined;
7612
+ managedBy?: "platform" | "system" | "config" | "append-only" | "better-auth" | undefined;
7481
7613
  userActions?: {
7482
7614
  create?: boolean | undefined;
7483
7615
  import?: boolean | undefined;
@@ -7706,6 +7838,29 @@ declare const securityObjects: ((Omit<{
7706
7838
  titleField: string;
7707
7839
  progressField?: string | undefined;
7708
7840
  dependenciesField?: string | undefined;
7841
+ colorField?: string | undefined;
7842
+ parentField?: string | undefined;
7843
+ typeField?: string | undefined;
7844
+ baselineStartField?: string | undefined;
7845
+ baselineEndField?: string | undefined;
7846
+ groupByField?: string | undefined;
7847
+ resourceView?: boolean | undefined;
7848
+ assigneeField?: string | undefined;
7849
+ effortField?: string | undefined;
7850
+ capacity?: number | undefined;
7851
+ tooltipFields?: (string | {
7852
+ field: string;
7853
+ label?: string | undefined;
7854
+ })[] | undefined;
7855
+ quickFilters?: {
7856
+ field: string;
7857
+ label?: string | undefined;
7858
+ options?: (string | {
7859
+ value: string | number;
7860
+ label?: string | undefined;
7861
+ })[] | undefined;
7862
+ }[] | undefined;
7863
+ autoZoomToFilter?: boolean | undefined;
7709
7864
  } | undefined;
7710
7865
  gallery?: {
7711
7866
  coverFit: "cover" | "contain";
@@ -7869,7 +8024,7 @@ declare const securityObjects: ((Omit<{
7869
8024
  clone: boolean;
7870
8025
  apiMethods?: ("aggregate" | "update" | "delete" | "restore" | "purge" | "search" | "create" | "import" | "list" | "get" | "upsert" | "bulk" | "history" | "export")[] | undefined;
7871
8026
  } | undefined;
7872
- sharingModel?: "full" | "read" | "private" | "read_write" | undefined;
8027
+ sharingModel?: "full" | "read" | "private" | "public_read" | "public_read_write" | "controlled_by_parent" | "read_write" | undefined;
7873
8028
  publicSharing?: {
7874
8029
  enabled: boolean;
7875
8030
  allowedAudiences?: ("email" | "public" | "link_only" | "signed_in")[] | undefined;
@@ -7901,7 +8056,7 @@ declare const securityObjects: ((Omit<{
7901
8056
  } | {
7902
8057
  language: "js";
7903
8058
  source: string;
7904
- capabilities: ("api.read" | "api.write" | "crypto.uuid" | "crypto.hash" | "log")[];
8059
+ capabilities: ("api.read" | "api.write" | "api.transaction" | "crypto.uuid" | "crypto.hash" | "log")[];
7905
8060
  timeoutMs?: number | undefined;
7906
8061
  memoryMb?: number | undefined;
7907
8062
  } | undefined;
@@ -8800,6 +8955,7 @@ declare const securityObjects: ((Omit<{
8800
8955
  declare const securityDefaultPermissionSets: {
8801
8956
  name: string;
8802
8957
  isProfile: boolean;
8958
+ isDefault: boolean;
8803
8959
  objects: Record<string, {
8804
8960
  allowCreate: boolean;
8805
8961
  allowRead: boolean;
@@ -8884,4 +9040,79 @@ declare function backfillOrgAdminGrants(ql: any, options?: {
8884
9040
  skipped: number;
8885
9041
  }>;
8886
9042
 
8887
- export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, backfillOrgAdminGrants, isPermissionDeniedError, reconcileOrgAdminGrant, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };
9043
+ /**
9044
+ * bootstrapPlatformAdmin — first-boot platform admin promotion.
9045
+ *
9046
+ * Two responsibilities, both idempotent and run on `kernel:ready`:
9047
+ *
9048
+ * 1. **Seed `sys_permission_set` rows** for each `defaultPermissionSets`
9049
+ * entry (admin_full_access / member_default / viewer_readonly).
9050
+ *
9051
+ * 2. **Promote the first registered user to platform admin** by
9052
+ * inserting a `sys_user_permission_set` row that points at
9053
+ * `admin_full_access` with `organization_id = NULL` (= cross-tenant).
9054
+ * If a platform admin already exists, this is a no-op forever.
9055
+ *
9056
+ * The "create a Default Organization for the freshly-promoted admin"
9057
+ * behavior moved to `@objectstack/plugin-org-scoping` (see
9058
+ * `ensureDefaultOrganization`). Install that plugin to get
9059
+ * multi-tenant bootstrap.
9060
+ */
9061
+
9062
+ interface BootstrapOptions {
9063
+ /** Logger from PluginContext. */
9064
+ logger?: {
9065
+ info: (message: string, meta?: Record<string, any>) => void;
9066
+ warn: (message: string, meta?: Record<string, any>) => void;
9067
+ };
9068
+ }
9069
+ /**
9070
+ * Persist seed permission sets and promote the first registered user to
9071
+ * platform admin. Safe to call multiple times.
9072
+ */
9073
+ declare function bootstrapPlatformAdmin(ql: any, bootstrapPermissionSets: PermissionSet[], options?: BootstrapOptions): Promise<{
9074
+ seeded: number;
9075
+ adminPromoted: boolean;
9076
+ reason?: string;
9077
+ /** Count of seeded rows re-owned to the freshly-promoted admin. */
9078
+ ownershipClaimed?: number;
9079
+ }>;
9080
+
9081
+ interface ClaimOwnershipOptions {
9082
+ logger?: {
9083
+ info: (message: string, meta?: Record<string, any>) => void;
9084
+ warn: (message: string, meta?: Record<string, any>) => void;
9085
+ };
9086
+ }
9087
+ /**
9088
+ * Re-own every orphan seed row (owner_id NULL or usr_system) to `adminUserId`.
9089
+ *
9090
+ * Walks `ql.registry.getAllObjects()`, filters to schemas that
9091
+ * (a) are not `managedBy` (skip sys_/auth/platform tables),
9092
+ * (b) are not `sys_*`-namespaced,
9093
+ * (c) declare an `owner_id` field,
9094
+ * and updates the unowned rows as `isSystem`. Returns a per-object summary.
9095
+ */
9096
+ declare function claimSeedOwnership(ql: any, adminUserId: string, options?: ClaimOwnershipOptions): Promise<{
9097
+ object: string;
9098
+ count: number;
9099
+ }[]>;
9100
+
9101
+ /**
9102
+ * ADR-0056 D7 — resolve the app-declared default profile NAME from a stack's
9103
+ * `permissions[]` array.
9104
+ *
9105
+ * A permission set marked `isProfile && isDefault` declares the app's default
9106
+ * access posture for users with no explicit grants. The {@link SecurityPlugin}
9107
+ * constructor scans its `defaultPermissionSets` option for that flag — but the
9108
+ * CLI constructs `new SecurityPlugin()` with NO options, so an `isDefault`
9109
+ * profile declared purely in app METADATA would never be honored. The CLI calls
9110
+ * this helper to pull the name out of the stack and pass it as
9111
+ * `fallbackPermissionSet`, wiring the declaration through to `pnpm dev`.
9112
+ *
9113
+ * Returns the first matching profile's `name`, or `undefined` when none is
9114
+ * declared (callers then keep the built-in `member_default` fallback).
9115
+ */
9116
+ declare function appDefaultProfileName(permissions: unknown): string | undefined;
9117
+
9118
+ export { FieldMasker, PermissionDeniedError, PermissionEvaluator, RLSCompiler, RLS_DENY_FILTER, SECURITY_PLUGIN_ID, SECURITY_PLUGIN_VERSION, SecurityPlugin, appDefaultProfileName, backfillOrgAdminGrants, bootstrapPlatformAdmin, claimSeedOwnership, isPermissionDeniedError, reconcileOrgAdminGrant, securityDefaultPermissionSets, securityObjects, securityPluginManifestHeader };