@objectstack/platform-objects 0.1.0 → 4.1.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/apps/index.d.mts +16 -48
- package/dist/apps/index.d.ts +16 -48
- package/dist/apps/index.js +139 -215
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/index.mjs +140 -210
- package/dist/apps/index.mjs.map +1 -1
- package/dist/audit/index.d.mts +40249 -150
- package/dist/audit/index.d.ts +40249 -150
- package/dist/audit/index.js +1428 -0
- package/dist/audit/index.js.map +1 -1
- package/dist/audit/index.mjs +1417 -1
- package/dist/audit/index.mjs.map +1 -1
- package/dist/identity/index.d.mts +18792 -2520
- package/dist/identity/index.d.ts +18792 -2520
- package/dist/identity/index.js +1107 -6
- package/dist/identity/index.js.map +1 -1
- package/dist/identity/index.mjs +1106 -7
- package/dist/identity/index.mjs.map +1 -1
- package/dist/index.d.mts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +3939 -1504
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3917 -1487
- package/dist/index.mjs.map +1 -1
- package/dist/integration/index.d.mts +2905 -0
- package/dist/integration/index.d.ts +2905 -0
- package/dist/integration/index.js +140 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/integration/index.mjs +138 -0
- package/dist/integration/index.mjs.map +1 -0
- package/dist/metadata/index.d.mts +1426 -19092
- package/dist/metadata/index.d.ts +1426 -19092
- package/dist/metadata/index.js +29 -619
- package/dist/metadata/index.js.map +1 -1
- package/dist/metadata/index.mjs +30 -615
- package/dist/metadata/index.mjs.map +1 -1
- package/dist/security/index.d.mts +10759 -60
- package/dist/security/index.d.ts +10759 -60
- package/dist/security/index.js +786 -0
- package/dist/security/index.js.map +1 -1
- package/dist/security/index.mjs +782 -1
- package/dist/security/index.mjs.map +1 -1
- package/dist/system/index.d.mts +8409 -0
- package/dist/system/index.d.ts +8409 -0
- package/dist/system/index.js +395 -0
- package/dist/system/index.js.map +1 -0
- package/dist/system/index.mjs +391 -0
- package/dist/system/index.mjs.map +1 -0
- package/package.json +13 -8
- package/dist/tenant/index.d.mts +0 -16454
- package/dist/tenant/index.d.ts +0 -16454
- package/dist/tenant/index.js +0 -741
- package/dist/tenant/index.js.map +0 -1
- package/dist/tenant/index.mjs +0 -733
- package/dist/tenant/index.mjs.map +0 -1
- /package/dist/{state-machine.zod-BFg-VE0M.d-Ek3_yo9P.d.mts → state-machine.zod-BNanU03M.d-Ek3_yo9P.d.mts} +0 -0
- /package/dist/{state-machine.zod-BFg-VE0M.d-Ek3_yo9P.d.ts → state-machine.zod-BNanU03M.d-Ek3_yo9P.d.ts} +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var data = require('@objectstack/spec/data');
|
|
4
|
+
|
|
5
|
+
// src/system/sys-setting.object.ts
|
|
6
|
+
var SysSetting = data.ObjectSchema.create({
|
|
7
|
+
name: "sys_setting",
|
|
8
|
+
label: "Setting",
|
|
9
|
+
pluralLabel: "Settings",
|
|
10
|
+
icon: "sliders",
|
|
11
|
+
isSystem: true,
|
|
12
|
+
managedBy: "system",
|
|
13
|
+
description: "Generic K/V store backing the SettingsManifest contract.",
|
|
14
|
+
displayNameField: "key",
|
|
15
|
+
titleFormat: "{namespace}.{key}",
|
|
16
|
+
compactLayout: ["namespace", "key", "scope", "updated_at"],
|
|
17
|
+
listViews: {
|
|
18
|
+
by_namespace: {
|
|
19
|
+
type: "grid",
|
|
20
|
+
name: "by_namespace",
|
|
21
|
+
label: "By Namespace",
|
|
22
|
+
data: { provider: "object", object: "sys_setting" },
|
|
23
|
+
columns: ["namespace", "key", "scope", "encrypted", "updated_by", "updated_at"],
|
|
24
|
+
sort: [{ field: "namespace", order: "asc" }, { field: "key", order: "asc" }],
|
|
25
|
+
grouping: { fields: [{ field: "namespace", order: "asc", collapsed: false }] },
|
|
26
|
+
pagination: { pageSize: 200 }
|
|
27
|
+
},
|
|
28
|
+
tenant_only: {
|
|
29
|
+
type: "grid",
|
|
30
|
+
name: "tenant_only",
|
|
31
|
+
label: "Tenant",
|
|
32
|
+
data: { provider: "object", object: "sys_setting" },
|
|
33
|
+
columns: ["namespace", "key", "encrypted", "updated_by", "updated_at"],
|
|
34
|
+
filter: [{ field: "scope", operator: "equals", value: "tenant" }],
|
|
35
|
+
sort: [{ field: "namespace", order: "asc" }, { field: "key", order: "asc" }],
|
|
36
|
+
pagination: { pageSize: 200 }
|
|
37
|
+
},
|
|
38
|
+
user_only: {
|
|
39
|
+
type: "grid",
|
|
40
|
+
name: "user_only",
|
|
41
|
+
label: "User",
|
|
42
|
+
data: { provider: "object", object: "sys_setting" },
|
|
43
|
+
columns: ["user_id", "namespace", "key", "updated_at"],
|
|
44
|
+
filter: [{ field: "scope", operator: "equals", value: "user" }],
|
|
45
|
+
sort: [{ field: "user_id", order: "asc" }, { field: "namespace", order: "asc" }],
|
|
46
|
+
pagination: { pageSize: 200 }
|
|
47
|
+
},
|
|
48
|
+
all_settings: {
|
|
49
|
+
type: "grid",
|
|
50
|
+
name: "all_settings",
|
|
51
|
+
label: "All",
|
|
52
|
+
data: { provider: "object", object: "sys_setting" },
|
|
53
|
+
columns: ["namespace", "key", "scope", "user_id", "encrypted", "updated_at"],
|
|
54
|
+
sort: [{ field: "updated_at", order: "desc" }],
|
|
55
|
+
pagination: { pageSize: 100 }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
fields: {
|
|
59
|
+
id: data.Field.text({
|
|
60
|
+
label: "Setting ID",
|
|
61
|
+
required: true,
|
|
62
|
+
readonly: true
|
|
63
|
+
}),
|
|
64
|
+
created_at: data.Field.datetime({
|
|
65
|
+
label: "Created At",
|
|
66
|
+
defaultValue: "NOW()",
|
|
67
|
+
readonly: true
|
|
68
|
+
}),
|
|
69
|
+
updated_at: data.Field.datetime({
|
|
70
|
+
label: "Updated At",
|
|
71
|
+
defaultValue: "NOW()",
|
|
72
|
+
readonly: true
|
|
73
|
+
}),
|
|
74
|
+
namespace: data.Field.text({
|
|
75
|
+
label: "Namespace",
|
|
76
|
+
required: true,
|
|
77
|
+
maxLength: 64,
|
|
78
|
+
description: "Manifest namespace (e.g. mail, branding, feature_flags)."
|
|
79
|
+
}),
|
|
80
|
+
key: data.Field.text({
|
|
81
|
+
label: "Key",
|
|
82
|
+
required: true,
|
|
83
|
+
maxLength: 128,
|
|
84
|
+
description: "Specifier key inside the namespace (snake_case)."
|
|
85
|
+
}),
|
|
86
|
+
scope: data.Field.select(
|
|
87
|
+
[
|
|
88
|
+
{ label: "Global", value: "global" },
|
|
89
|
+
{ label: "Tenant", value: "tenant" },
|
|
90
|
+
{ label: "User", value: "user" },
|
|
91
|
+
{ label: "Runtime", value: "runtime" }
|
|
92
|
+
],
|
|
93
|
+
{
|
|
94
|
+
label: "Scope",
|
|
95
|
+
required: true,
|
|
96
|
+
defaultValue: "tenant",
|
|
97
|
+
description: "Which layer of the config-resolution hierarchy this row belongs to."
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
user_id: data.Field.lookup("sys_user", {
|
|
101
|
+
label: "User",
|
|
102
|
+
description: "Owning user when scope=user; null otherwise."
|
|
103
|
+
}),
|
|
104
|
+
value: data.Field.json({
|
|
105
|
+
label: "Value",
|
|
106
|
+
description: "JSON-encoded value. Null when encrypted=true (see value_enc)."
|
|
107
|
+
}),
|
|
108
|
+
encrypted: data.Field.boolean({
|
|
109
|
+
label: "Encrypted",
|
|
110
|
+
defaultValue: false,
|
|
111
|
+
description: "When true, the value is stored encrypted-at-rest in value_enc; value column is null."
|
|
112
|
+
}),
|
|
113
|
+
locked: data.Field.boolean({
|
|
114
|
+
label: "Locked",
|
|
115
|
+
defaultValue: false,
|
|
116
|
+
description: "When true, lower-scope rows cannot override this value; writes against lower scopes return 409. Used by platform administrators to pin a global value for all tenants (Phase 2 cascade)."
|
|
117
|
+
}),
|
|
118
|
+
locked_reason: data.Field.text({
|
|
119
|
+
label: "Lock Reason",
|
|
120
|
+
description: "Human-readable explanation surfaced in the UI tooltip when locked=true."
|
|
121
|
+
}),
|
|
122
|
+
value_enc: data.Field.text({
|
|
123
|
+
label: "Encrypted Value",
|
|
124
|
+
readonly: true,
|
|
125
|
+
description: "Ciphertext payload (KMS-wrapped). Set only when encrypted=true."
|
|
126
|
+
}),
|
|
127
|
+
updated_by: data.Field.lookup("sys_user", {
|
|
128
|
+
label: "Updated By",
|
|
129
|
+
readonly: true,
|
|
130
|
+
description: "Last actor who wrote this row via SettingsService.set()."
|
|
131
|
+
})
|
|
132
|
+
},
|
|
133
|
+
indexes: [
|
|
134
|
+
// Primary lookup path: (namespace, key, scope, user_id?) is what
|
|
135
|
+
// SettingsService.get hits on every resolve. The composite UNIQUE
|
|
136
|
+
// covers both the row-identity constraint and the read path.
|
|
137
|
+
{ fields: ["namespace", "key", "scope", "user_id"], unique: true },
|
|
138
|
+
// Common range read: full namespace dump for SettingsService.getNamespace.
|
|
139
|
+
{ fields: ["namespace", "scope"], unique: false },
|
|
140
|
+
// Per-user listing on the user-prefs scope.
|
|
141
|
+
{ fields: ["user_id", "namespace"], unique: false }
|
|
142
|
+
],
|
|
143
|
+
enable: {
|
|
144
|
+
// History on settings is opt-in per namespace (handled at service
|
|
145
|
+
// layer when needed) to avoid bloating sys_history with churn from
|
|
146
|
+
// feature flags and similar high-frequency toggles.
|
|
147
|
+
trackHistory: false,
|
|
148
|
+
searchable: false,
|
|
149
|
+
apiEnabled: true,
|
|
150
|
+
// Direct data API exposed for the admin grid view, but writes from
|
|
151
|
+
// the UI MUST go through /api/settings/:namespace so the resolver
|
|
152
|
+
// and audit hooks fire. The grid is diagnostic-only.
|
|
153
|
+
apiMethods: ["get", "list"],
|
|
154
|
+
trash: false,
|
|
155
|
+
mru: false
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
var SysSecret = data.ObjectSchema.create({
|
|
159
|
+
name: "sys_secret",
|
|
160
|
+
label: "Secret",
|
|
161
|
+
pluralLabel: "Secrets",
|
|
162
|
+
icon: "key",
|
|
163
|
+
isSystem: true,
|
|
164
|
+
managedBy: "system",
|
|
165
|
+
description: "Cipher store referenced by sys_setting handles. Never holds plaintext.",
|
|
166
|
+
scope: "tenant",
|
|
167
|
+
compactLayout: ["namespace", "key", "kms_key_id", "version", "rotated_at"],
|
|
168
|
+
defaultViewName: "all",
|
|
169
|
+
views: {
|
|
170
|
+
all: {
|
|
171
|
+
type: "list",
|
|
172
|
+
name: "all",
|
|
173
|
+
label: "All Secrets",
|
|
174
|
+
columns: ["namespace", "key", "kms_key_id", "version", "rotated_at", "created_at"]
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
fields: {
|
|
178
|
+
id: data.Field.text({
|
|
179
|
+
label: "ID",
|
|
180
|
+
readonly: true,
|
|
181
|
+
description: "Opaque handle referenced by `sys_setting.value_enc`."
|
|
182
|
+
}),
|
|
183
|
+
created_at: data.Field.datetime({
|
|
184
|
+
label: "Created At",
|
|
185
|
+
readonly: true,
|
|
186
|
+
description: "When the cipher was first written."
|
|
187
|
+
}),
|
|
188
|
+
rotated_at: data.Field.datetime({
|
|
189
|
+
label: "Rotated At",
|
|
190
|
+
readonly: true,
|
|
191
|
+
description: "When the cipher was last re-wrapped under a new KMS key."
|
|
192
|
+
}),
|
|
193
|
+
/**
|
|
194
|
+
* Namespace/key duplicated from `sys_setting` for forensic
|
|
195
|
+
* convenience — lets operators answer "which secret backs
|
|
196
|
+
* mail.api_key right now?" without joining the K/V table.
|
|
197
|
+
* The authoritative link is `sys_setting.value_enc → sys_secret.id`.
|
|
198
|
+
*/
|
|
199
|
+
namespace: data.Field.text({
|
|
200
|
+
label: "Namespace",
|
|
201
|
+
required: true,
|
|
202
|
+
maxLength: 128,
|
|
203
|
+
description: "Settings namespace this secret belongs to."
|
|
204
|
+
}),
|
|
205
|
+
key: data.Field.text({
|
|
206
|
+
label: "Key",
|
|
207
|
+
required: true,
|
|
208
|
+
maxLength: 128,
|
|
209
|
+
description: "Specifier key within the namespace."
|
|
210
|
+
}),
|
|
211
|
+
/** Identifier of the KMS key used to wrap `ciphertext`. */
|
|
212
|
+
kms_key_id: data.Field.text({
|
|
213
|
+
label: "KMS Key ID",
|
|
214
|
+
required: true,
|
|
215
|
+
maxLength: 256,
|
|
216
|
+
description: "External KMS handle (ARN, GCP resource id, or `local`)."
|
|
217
|
+
}),
|
|
218
|
+
/** Algorithm tag (e.g. `aes-256-gcm`). Used by the provider on decrypt. */
|
|
219
|
+
alg: data.Field.text({
|
|
220
|
+
label: "Algorithm",
|
|
221
|
+
required: true,
|
|
222
|
+
defaultValue: "aes-256-gcm",
|
|
223
|
+
maxLength: 64,
|
|
224
|
+
description: "Cipher/AEAD algorithm tag."
|
|
225
|
+
}),
|
|
226
|
+
/** Wrapping version — bumps on every rotate(). */
|
|
227
|
+
version: data.Field.number({
|
|
228
|
+
label: "Version",
|
|
229
|
+
required: true,
|
|
230
|
+
defaultValue: 1,
|
|
231
|
+
description: "Bumps each time rotateKey() re-wraps this row."
|
|
232
|
+
}),
|
|
233
|
+
ciphertext: data.Field.text({
|
|
234
|
+
label: "Ciphertext",
|
|
235
|
+
required: true,
|
|
236
|
+
readonly: true,
|
|
237
|
+
description: "Provider-encoded ciphertext blob (base64 / JSON). Implementation-defined; only the matching ICryptoProvider can read it."
|
|
238
|
+
})
|
|
239
|
+
},
|
|
240
|
+
indexes: [
|
|
241
|
+
// Operators frequently look up by (namespace, key) to inspect or rotate.
|
|
242
|
+
{ fields: ["namespace", "key"], unique: false },
|
|
243
|
+
{ fields: ["kms_key_id"], unique: false }
|
|
244
|
+
],
|
|
245
|
+
enable: {
|
|
246
|
+
trackHistory: false,
|
|
247
|
+
// rotation events are recorded by sys_setting_audit
|
|
248
|
+
audit: true
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
var SysSettingAudit = data.ObjectSchema.create({
|
|
252
|
+
name: "sys_setting_audit",
|
|
253
|
+
label: "Setting Audit Entry",
|
|
254
|
+
pluralLabel: "Setting Audit",
|
|
255
|
+
icon: "history",
|
|
256
|
+
isSystem: true,
|
|
257
|
+
managedBy: "system",
|
|
258
|
+
description: "Append-only audit trail for SettingsService mutations.",
|
|
259
|
+
scope: "tenant",
|
|
260
|
+
compactLayout: ["namespace", "key", "scope", "action", "actor_id", "created_at"],
|
|
261
|
+
defaultViewName: "recent",
|
|
262
|
+
views: {
|
|
263
|
+
recent: {
|
|
264
|
+
type: "list",
|
|
265
|
+
name: "recent",
|
|
266
|
+
label: "Recent",
|
|
267
|
+
columns: ["created_at", "namespace", "key", "scope", "action", "actor_id", "source"],
|
|
268
|
+
sort: [{ field: "created_at", order: "desc" }]
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
fields: {
|
|
272
|
+
id: data.Field.text({
|
|
273
|
+
label: "ID",
|
|
274
|
+
readonly: true
|
|
275
|
+
}),
|
|
276
|
+
created_at: data.Field.datetime({
|
|
277
|
+
label: "Created At",
|
|
278
|
+
readonly: true,
|
|
279
|
+
description: "When the mutation was recorded."
|
|
280
|
+
}),
|
|
281
|
+
namespace: data.Field.text({
|
|
282
|
+
label: "Namespace",
|
|
283
|
+
required: true,
|
|
284
|
+
maxLength: 128
|
|
285
|
+
}),
|
|
286
|
+
key: data.Field.text({
|
|
287
|
+
label: "Key",
|
|
288
|
+
required: true,
|
|
289
|
+
maxLength: 128
|
|
290
|
+
}),
|
|
291
|
+
scope: data.Field.select(
|
|
292
|
+
[
|
|
293
|
+
{ label: "Global", value: "global" },
|
|
294
|
+
{ label: "Tenant", value: "tenant" },
|
|
295
|
+
{ label: "User", value: "user" }
|
|
296
|
+
],
|
|
297
|
+
{
|
|
298
|
+
label: "Scope",
|
|
299
|
+
required: true,
|
|
300
|
+
description: "Cascade layer the row was written to."
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
action: data.Field.select(
|
|
304
|
+
[
|
|
305
|
+
{ label: "Set", value: "set" },
|
|
306
|
+
{ label: "Reset", value: "reset" },
|
|
307
|
+
{ label: "Lock", value: "lock" },
|
|
308
|
+
{ label: "Unlock", value: "unlock" },
|
|
309
|
+
{ label: "Rotate", value: "rotate" }
|
|
310
|
+
],
|
|
311
|
+
{
|
|
312
|
+
label: "Action",
|
|
313
|
+
required: true,
|
|
314
|
+
description: "Mutation kind."
|
|
315
|
+
}
|
|
316
|
+
),
|
|
317
|
+
actor_id: data.Field.lookup("sys_user", {
|
|
318
|
+
label: "Actor",
|
|
319
|
+
description: "User who performed the mutation; null for system jobs."
|
|
320
|
+
}),
|
|
321
|
+
/**
|
|
322
|
+
* Where the write originated. Lets operators distinguish admin UI
|
|
323
|
+
* activity from migration jobs and bulk imports during incident
|
|
324
|
+
* analysis.
|
|
325
|
+
*/
|
|
326
|
+
source: data.Field.select(
|
|
327
|
+
[
|
|
328
|
+
{ label: "UI", value: "ui" },
|
|
329
|
+
{ label: "API", value: "api" },
|
|
330
|
+
{ label: "Migration", value: "migration" },
|
|
331
|
+
{ label: "Import", value: "import" },
|
|
332
|
+
{ label: "System", value: "system" }
|
|
333
|
+
],
|
|
334
|
+
{
|
|
335
|
+
label: "Source",
|
|
336
|
+
required: true,
|
|
337
|
+
defaultValue: "api",
|
|
338
|
+
description: "Mutation entry-point."
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/** Optional free-text reason (Phase 3+ change-management hook). */
|
|
342
|
+
reason: data.Field.text({
|
|
343
|
+
label: "Reason",
|
|
344
|
+
description: "Free-text justification provided by the actor (optional)."
|
|
345
|
+
}),
|
|
346
|
+
/**
|
|
347
|
+
* Content digest of the previous value. Never the plaintext —
|
|
348
|
+
* lets operators detect duplicate writes without leaking secrets.
|
|
349
|
+
* Format: hex SHA-256 of the canonicalised JSON, or null when
|
|
350
|
+
* the previous value was unset.
|
|
351
|
+
*/
|
|
352
|
+
old_hash: data.Field.text({
|
|
353
|
+
label: "Old Hash",
|
|
354
|
+
readonly: true,
|
|
355
|
+
maxLength: 128,
|
|
356
|
+
description: "SHA-256 of the previous value (canonicalised). Null when previously unset."
|
|
357
|
+
}),
|
|
358
|
+
/** Content digest of the new value. Null on `reset`. */
|
|
359
|
+
new_hash: data.Field.text({
|
|
360
|
+
label: "New Hash",
|
|
361
|
+
readonly: true,
|
|
362
|
+
maxLength: 128,
|
|
363
|
+
description: "SHA-256 of the new value (canonicalised). Null on reset."
|
|
364
|
+
}),
|
|
365
|
+
/** True when the field is encrypted — flags secret rotation events. */
|
|
366
|
+
encrypted: data.Field.boolean({
|
|
367
|
+
label: "Encrypted",
|
|
368
|
+
defaultValue: false,
|
|
369
|
+
description: "True when the field carries secret material (rotation is interesting)."
|
|
370
|
+
}),
|
|
371
|
+
/** Request id from the originating HTTP/CLI invocation. */
|
|
372
|
+
request_id: data.Field.text({
|
|
373
|
+
label: "Request ID",
|
|
374
|
+
maxLength: 128,
|
|
375
|
+
description: "Correlates with sys_audit_log / tracing."
|
|
376
|
+
})
|
|
377
|
+
},
|
|
378
|
+
indexes: [
|
|
379
|
+
// Most common query: "what changed for namespace X in the last 7 days?"
|
|
380
|
+
{ fields: ["namespace", "created_at"], unique: false },
|
|
381
|
+
// Per-actor lookup for compliance reviews.
|
|
382
|
+
{ fields: ["actor_id", "created_at"], unique: false }
|
|
383
|
+
],
|
|
384
|
+
enable: {
|
|
385
|
+
trackHistory: false,
|
|
386
|
+
audit: false
|
|
387
|
+
// this IS the audit; no recursion
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
exports.SysSecret = SysSecret;
|
|
392
|
+
exports.SysSetting = SysSetting;
|
|
393
|
+
exports.SysSettingAudit = SysSettingAudit;
|
|
394
|
+
//# sourceMappingURL=index.js.map
|
|
395
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/system/sys-setting.object.ts","../../src/system/sys-secret.object.ts","../../src/system/sys-setting-audit.object.ts"],"names":["ObjectSchema","Field"],"mappings":"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","file":"index.js","sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_setting — Generic K/V store backing the SettingsManifest contract\n *\n * Single physical table that holds *every* value for *every* settings\n * namespace declared by a `SettingsManifest`. Plugins MUST NOT define\n * per-namespace tables (e.g. `sys_mail_config`); they declare a manifest\n * and the value persists here.\n *\n * Row identity: (namespace, key, scope, user_id?).\n *\n * Resolution order (handled by `SettingsService.get`):\n * 1. process.env override (source='env', locked=true)\n * 2. sys_setting WHERE scope='global' (source='global')\n * 3. sys_setting WHERE scope='tenant' (source='tenant')\n * 4. sys_setting WHERE scope='user' (source='user')\n * 5. manifest specifier.default (source='default')\n *\n * Encryption: rows with `encrypted=true` store ciphertext in `value_enc`\n * and leave `value` null. The plain value is never written to audit log\n * or history snapshots — only an `'<encrypted>'` placeholder + a digest.\n *\n * managedBy: 'system' — the admin grid in Setup is a diagnostic surface\n * only; all writes flow through `SettingsService.set()` so the resolver\n * stays the single source of truth.\n *\n * See ADR-0007 (Settings Manifest + K/V Store + Resolver).\n *\n * @namespace sys\n */\nexport const SysSetting = ObjectSchema.create({\n name: 'sys_setting',\n label: 'Setting',\n pluralLabel: 'Settings',\n icon: 'sliders',\n isSystem: true,\n managedBy: 'system',\n description: 'Generic K/V store backing the SettingsManifest contract.',\n displayNameField: 'key',\n titleFormat: '{namespace}.{key}',\n compactLayout: ['namespace', 'key', 'scope', 'updated_at'],\n\n listViews: {\n by_namespace: {\n type: 'grid',\n name: 'by_namespace',\n label: 'By Namespace',\n data: { provider: 'object', object: 'sys_setting' },\n columns: ['namespace', 'key', 'scope', 'encrypted', 'updated_by', 'updated_at'],\n sort: [{ field: 'namespace', order: 'asc' }, { field: 'key', order: 'asc' }],\n grouping: { fields: [{ field: 'namespace', order: 'asc', collapsed: false }] },\n pagination: { pageSize: 200 },\n },\n tenant_only: {\n type: 'grid',\n name: 'tenant_only',\n label: 'Tenant',\n data: { provider: 'object', object: 'sys_setting' },\n columns: ['namespace', 'key', 'encrypted', 'updated_by', 'updated_at'],\n filter: [{ field: 'scope', operator: 'equals', value: 'tenant' }],\n sort: [{ field: 'namespace', order: 'asc' }, { field: 'key', order: 'asc' }],\n pagination: { pageSize: 200 },\n },\n user_only: {\n type: 'grid',\n name: 'user_only',\n label: 'User',\n data: { provider: 'object', object: 'sys_setting' },\n columns: ['user_id', 'namespace', 'key', 'updated_at'],\n filter: [{ field: 'scope', operator: 'equals', value: 'user' }],\n sort: [{ field: 'user_id', order: 'asc' }, { field: 'namespace', order: 'asc' }],\n pagination: { pageSize: 200 },\n },\n all_settings: {\n type: 'grid',\n name: 'all_settings',\n label: 'All',\n data: { provider: 'object', object: 'sys_setting' },\n columns: ['namespace', 'key', 'scope', 'user_id', 'encrypted', 'updated_at'],\n sort: [{ field: 'updated_at', order: 'desc' }],\n pagination: { pageSize: 100 },\n },\n },\n\n fields: {\n id: Field.text({\n label: 'Setting ID',\n required: true,\n readonly: true,\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n namespace: Field.text({\n label: 'Namespace',\n required: true,\n maxLength: 64,\n description: 'Manifest namespace (e.g. mail, branding, feature_flags).',\n }),\n\n key: Field.text({\n label: 'Key',\n required: true,\n maxLength: 128,\n description: 'Specifier key inside the namespace (snake_case).',\n }),\n\n scope: Field.select(\n [\n { label: 'Global', value: 'global' },\n { label: 'Tenant', value: 'tenant' },\n { label: 'User', value: 'user' },\n { label: 'Runtime',value: 'runtime' },\n ],\n {\n label: 'Scope',\n required: true,\n defaultValue: 'tenant',\n description: 'Which layer of the config-resolution hierarchy this row belongs to.',\n },\n ),\n\n user_id: Field.lookup('sys_user', {\n label: 'User',\n description: 'Owning user when scope=user; null otherwise.',\n }),\n\n value: Field.json({\n label: 'Value',\n description: 'JSON-encoded value. Null when encrypted=true (see value_enc).',\n }),\n\n encrypted: Field.boolean({\n label: 'Encrypted',\n defaultValue: false,\n description: 'When true, the value is stored encrypted-at-rest in value_enc; value column is null.',\n }),\n\n locked: Field.boolean({\n label: 'Locked',\n defaultValue: false,\n description:\n 'When true, lower-scope rows cannot override this value; writes against lower scopes return 409. ' +\n 'Used by platform administrators to pin a global value for all tenants (Phase 2 cascade).',\n }),\n\n locked_reason: Field.text({\n label: 'Lock Reason',\n description: 'Human-readable explanation surfaced in the UI tooltip when locked=true.',\n }),\n\n value_enc: Field.text({\n label: 'Encrypted Value',\n readonly: true,\n description: 'Ciphertext payload (KMS-wrapped). Set only when encrypted=true.',\n }),\n\n updated_by: Field.lookup('sys_user', {\n label: 'Updated By',\n readonly: true,\n description: 'Last actor who wrote this row via SettingsService.set().',\n }),\n },\n\n indexes: [\n // Primary lookup path: (namespace, key, scope, user_id?) is what\n // SettingsService.get hits on every resolve. The composite UNIQUE\n // covers both the row-identity constraint and the read path.\n { fields: ['namespace', 'key', 'scope', 'user_id'], unique: true },\n // Common range read: full namespace dump for SettingsService.getNamespace.\n { fields: ['namespace', 'scope'], unique: false },\n // Per-user listing on the user-prefs scope.\n { fields: ['user_id', 'namespace'], unique: false },\n ],\n\n enable: {\n // History on settings is opt-in per namespace (handled at service\n // layer when needed) to avoid bloating sys_history with churn from\n // feature flags and similar high-frequency toggles.\n trackHistory: false,\n searchable: false,\n apiEnabled: true,\n // Direct data API exposed for the admin grid view, but writes from\n // the UI MUST go through /api/settings/:namespace so the resolver\n // and audit hooks fire. The grid is diagnostic-only.\n apiMethods: ['get', 'list'],\n trash: false,\n mru: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_secret — Separated cipher store for sensitive settings values.\n *\n * Phase 3 of the settings roadmap splits secret material out of\n * `sys_setting` so they can carry their own retention/rotation/KMS\n * policies without bloating the regular settings audit trail. The\n * value column in `sys_setting` for an encrypted specifier holds a\n * *handle* (the `id` of a row here), never the ciphertext itself —\n * the resolver dereferences on read.\n *\n * Why split:\n * 1. **Key rotation.** KMS adapters (AWS/GCP) rotate keys on a\n * different cadence than user-visible settings; tracking\n * `kms_key_id` + `version` per cipher lets us re-wrap without\n * touching the value lifecycle.\n * 2. **Backup hygiene.** Operators can replicate `sys_setting` to\n * analytics/lower environments while keeping `sys_secret` pinned\n * to the primary KMS region.\n * 3. **Audit symmetry.** Every secret read can record an access row\n * (Phase 4) without polluting `sys_setting_audit` with plaintext\n * reads of e.g. feature flags.\n *\n * managedBy: 'system' — never edited from a generic Object grid. All\n * writes flow through `SettingsService` and an `ICryptoProvider`.\n *\n * @namespace sys\n */\nexport const SysSecret = ObjectSchema.create({\n name: 'sys_secret',\n label: 'Secret',\n pluralLabel: 'Secrets',\n icon: 'key',\n isSystem: true,\n managedBy: 'system',\n description: 'Cipher store referenced by sys_setting handles. Never holds plaintext.',\n scope: 'tenant',\n compactLayout: ['namespace', 'key', 'kms_key_id', 'version', 'rotated_at'],\n defaultViewName: 'all',\n views: {\n all: {\n type: 'list',\n name: 'all',\n label: 'All Secrets',\n columns: ['namespace', 'key', 'kms_key_id', 'version', 'rotated_at', 'created_at'],\n },\n },\n\n fields: {\n id: Field.text({\n label: 'ID',\n readonly: true,\n description: 'Opaque handle referenced by `sys_setting.value_enc`.',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n readonly: true,\n description: 'When the cipher was first written.',\n }),\n\n rotated_at: Field.datetime({\n label: 'Rotated At',\n readonly: true,\n description: 'When the cipher was last re-wrapped under a new KMS key.',\n }),\n\n /**\n * Namespace/key duplicated from `sys_setting` for forensic\n * convenience — lets operators answer \"which secret backs\n * mail.api_key right now?\" without joining the K/V table.\n * The authoritative link is `sys_setting.value_enc → sys_secret.id`.\n */\n namespace: Field.text({\n label: 'Namespace',\n required: true,\n maxLength: 128,\n description: 'Settings namespace this secret belongs to.',\n }),\n\n key: Field.text({\n label: 'Key',\n required: true,\n maxLength: 128,\n description: 'Specifier key within the namespace.',\n }),\n\n /** Identifier of the KMS key used to wrap `ciphertext`. */\n kms_key_id: Field.text({\n label: 'KMS Key ID',\n required: true,\n maxLength: 256,\n description: 'External KMS handle (ARN, GCP resource id, or `local`).',\n }),\n\n /** Algorithm tag (e.g. `aes-256-gcm`). Used by the provider on decrypt. */\n alg: Field.text({\n label: 'Algorithm',\n required: true,\n defaultValue: 'aes-256-gcm',\n maxLength: 64,\n description: 'Cipher/AEAD algorithm tag.',\n }),\n\n /** Wrapping version — bumps on every rotate(). */\n version: Field.number({\n label: 'Version',\n required: true,\n defaultValue: 1,\n description: 'Bumps each time rotateKey() re-wraps this row.',\n }),\n\n ciphertext: Field.text({\n label: 'Ciphertext',\n required: true,\n readonly: true,\n description:\n 'Provider-encoded ciphertext blob (base64 / JSON). Implementation-defined; only the matching ICryptoProvider can read it.',\n }),\n },\n\n indexes: [\n // Operators frequently look up by (namespace, key) to inspect or rotate.\n { fields: ['namespace', 'key'], unique: false },\n { fields: ['kms_key_id'], unique: false },\n ],\n\n enable: {\n trackHistory: false, // rotation events are recorded by sys_setting_audit\n audit: true,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_setting_audit — Append-only audit trail for every settings mutation.\n *\n * Phase 3 of the settings roadmap. Each call to `SettingsService.set()`\n * (and any other mutation hook) writes a row here BEFORE returning to\n * the caller. The row records who, when, where (scope), and what\n * changed — but never the plaintext value of an encrypted field; only\n * a content digest is stored so an operator can verify \"this is the\n * same value as last week\" without exposing the secret.\n *\n * Why separate from `sys_audit_log`:\n * - The generic audit log is a high-traffic firehose (every CRUD\n * on every business object). Settings rows are low-traffic and\n * operationally critical, so they deserve dedicated retention and\n * indexing.\n * - The schema here carries settings-specific fields (`scope`,\n * `cascade_source`) that don't make sense on a generic row.\n *\n * Append-only contract: enforced at the application layer (the only\n * writer is SettingsService). Operators MUST NOT delete rows; instead\n * use lifecycle policies to archive cold rows to a separate bucket.\n *\n * @namespace sys\n */\nexport const SysSettingAudit = ObjectSchema.create({\n name: 'sys_setting_audit',\n label: 'Setting Audit Entry',\n pluralLabel: 'Setting Audit',\n icon: 'history',\n isSystem: true,\n managedBy: 'system',\n description: 'Append-only audit trail for SettingsService mutations.',\n scope: 'tenant',\n compactLayout: ['namespace', 'key', 'scope', 'action', 'actor_id', 'created_at'],\n defaultViewName: 'recent',\n views: {\n recent: {\n type: 'list',\n name: 'recent',\n label: 'Recent',\n columns: ['created_at', 'namespace', 'key', 'scope', 'action', 'actor_id', 'source'],\n sort: [{ field: 'created_at', order: 'desc' }],\n },\n },\n\n fields: {\n id: Field.text({\n label: 'ID',\n readonly: true,\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n readonly: true,\n description: 'When the mutation was recorded.',\n }),\n\n namespace: Field.text({\n label: 'Namespace',\n required: true,\n maxLength: 128,\n }),\n\n key: Field.text({\n label: 'Key',\n required: true,\n maxLength: 128,\n }),\n\n scope: Field.select(\n [\n { label: 'Global', value: 'global' },\n { label: 'Tenant', value: 'tenant' },\n { label: 'User', value: 'user' },\n ],\n {\n label: 'Scope',\n required: true,\n description: 'Cascade layer the row was written to.',\n },\n ),\n\n action: Field.select(\n [\n { label: 'Set', value: 'set' },\n { label: 'Reset', value: 'reset' },\n { label: 'Lock', value: 'lock' },\n { label: 'Unlock', value: 'unlock' },\n { label: 'Rotate', value: 'rotate' },\n ],\n {\n label: 'Action',\n required: true,\n description: 'Mutation kind.',\n },\n ),\n\n actor_id: Field.lookup('sys_user', {\n label: 'Actor',\n description: 'User who performed the mutation; null for system jobs.',\n }),\n\n /**\n * Where the write originated. Lets operators distinguish admin UI\n * activity from migration jobs and bulk imports during incident\n * analysis.\n */\n source: Field.select(\n [\n { label: 'UI', value: 'ui' },\n { label: 'API', value: 'api' },\n { label: 'Migration', value: 'migration' },\n { label: 'Import', value: 'import' },\n { label: 'System', value: 'system' },\n ],\n {\n label: 'Source',\n required: true,\n defaultValue: 'api',\n description: 'Mutation entry-point.',\n },\n ),\n\n /** Optional free-text reason (Phase 3+ change-management hook). */\n reason: Field.text({\n label: 'Reason',\n description: 'Free-text justification provided by the actor (optional).',\n }),\n\n /**\n * Content digest of the previous value. Never the plaintext —\n * lets operators detect duplicate writes without leaking secrets.\n * Format: hex SHA-256 of the canonicalised JSON, or null when\n * the previous value was unset.\n */\n old_hash: Field.text({\n label: 'Old Hash',\n readonly: true,\n maxLength: 128,\n description: 'SHA-256 of the previous value (canonicalised). Null when previously unset.',\n }),\n\n /** Content digest of the new value. Null on `reset`. */\n new_hash: Field.text({\n label: 'New Hash',\n readonly: true,\n maxLength: 128,\n description: 'SHA-256 of the new value (canonicalised). Null on reset.',\n }),\n\n /** True when the field is encrypted — flags secret rotation events. */\n encrypted: Field.boolean({\n label: 'Encrypted',\n defaultValue: false,\n description: 'True when the field carries secret material (rotation is interesting).',\n }),\n\n /** Request id from the originating HTTP/CLI invocation. */\n request_id: Field.text({\n label: 'Request ID',\n maxLength: 128,\n description: 'Correlates with sys_audit_log / tracing.',\n }),\n },\n\n indexes: [\n // Most common query: \"what changed for namespace X in the last 7 days?\"\n { fields: ['namespace', 'created_at'], unique: false },\n // Per-actor lookup for compliance reviews.\n { fields: ['actor_id', 'created_at'], unique: false },\n ],\n\n enable: {\n trackHistory: false,\n audit: false, // this IS the audit; no recursion\n },\n});\n"]}
|