@theford/runly 0.0.0-stage → 0.20.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.
Files changed (128) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +157 -2
  3. package/dist/cli/client.d.ts +80 -0
  4. package/dist/cli/client.js +294 -0
  5. package/dist/cli/client.js.map +1 -0
  6. package/dist/cli/daemon-unit.d.ts +58 -0
  7. package/dist/cli/daemon-unit.js +289 -0
  8. package/dist/cli/daemon-unit.js.map +1 -0
  9. package/dist/cli/index.d.ts +2 -0
  10. package/dist/cli/index.js +1482 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/install.d.ts +9 -0
  13. package/dist/cli/install.js +53 -0
  14. package/dist/cli/install.js.map +1 -0
  15. package/dist/cli/logs.d.ts +43 -0
  16. package/dist/cli/logs.js +232 -0
  17. package/dist/cli/logs.js.map +1 -0
  18. package/dist/cli/tether.d.ts +3 -0
  19. package/dist/cli/tether.js +65 -0
  20. package/dist/cli/tether.js.map +1 -0
  21. package/dist/cli/watchdog.d.ts +1 -0
  22. package/dist/cli/watchdog.js +81 -0
  23. package/dist/cli/watchdog.js.map +1 -0
  24. package/dist/core/builds.d.ts +20 -0
  25. package/dist/core/builds.js +93 -0
  26. package/dist/core/builds.js.map +1 -0
  27. package/dist/core/caller-env.d.ts +17 -0
  28. package/dist/core/caller-env.js +77 -0
  29. package/dist/core/caller-env.js.map +1 -0
  30. package/dist/core/diagnostics.d.ts +68 -0
  31. package/dist/core/diagnostics.js +34 -0
  32. package/dist/core/diagnostics.js.map +1 -0
  33. package/dist/core/env-vars.d.ts +35 -0
  34. package/dist/core/env-vars.js +32 -0
  35. package/dist/core/env-vars.js.map +1 -0
  36. package/dist/core/events.d.ts +10 -0
  37. package/dist/core/events.js +42 -0
  38. package/dist/core/events.js.map +1 -0
  39. package/dist/core/exec.d.ts +22 -0
  40. package/dist/core/exec.js +84 -0
  41. package/dist/core/exec.js.map +1 -0
  42. package/dist/core/journal.d.ts +227 -0
  43. package/dist/core/journal.js +395 -0
  44. package/dist/core/journal.js.map +1 -0
  45. package/dist/core/logs.d.ts +56 -0
  46. package/dist/core/logs.js +259 -0
  47. package/dist/core/logs.js.map +1 -0
  48. package/dist/core/manifest.d.ts +292 -0
  49. package/dist/core/manifest.js +179 -0
  50. package/dist/core/manifest.js.map +1 -0
  51. package/dist/core/namespaces.d.ts +7 -0
  52. package/dist/core/namespaces.js +59 -0
  53. package/dist/core/namespaces.js.map +1 -0
  54. package/dist/core/paths.d.ts +31 -0
  55. package/dist/core/paths.js +65 -0
  56. package/dist/core/paths.js.map +1 -0
  57. package/dist/core/policy.d.ts +94 -0
  58. package/dist/core/policy.js +122 -0
  59. package/dist/core/policy.js.map +1 -0
  60. package/dist/core/ports.d.ts +52 -0
  61. package/dist/core/ports.js +94 -0
  62. package/dist/core/ports.js.map +1 -0
  63. package/dist/core/presets.d.ts +25 -0
  64. package/dist/core/presets.js +86 -0
  65. package/dist/core/presets.js.map +1 -0
  66. package/dist/core/procscan.d.ts +111 -0
  67. package/dist/core/procscan.js +342 -0
  68. package/dist/core/procscan.js.map +1 -0
  69. package/dist/core/retention.d.ts +97 -0
  70. package/dist/core/retention.js +322 -0
  71. package/dist/core/retention.js.map +1 -0
  72. package/dist/core/sleep.d.ts +51 -0
  73. package/dist/core/sleep.js +99 -0
  74. package/dist/core/sleep.js.map +1 -0
  75. package/dist/core/tree-ledger.d.ts +39 -0
  76. package/dist/core/tree-ledger.js +92 -0
  77. package/dist/core/tree-ledger.js.map +1 -0
  78. package/dist/core/types.d.ts +21 -0
  79. package/dist/core/types.js +6 -0
  80. package/dist/core/types.js.map +1 -0
  81. package/dist/core/units.d.ts +20 -0
  82. package/dist/core/units.js +69 -0
  83. package/dist/core/units.js.map +1 -0
  84. package/dist/core/upkeep.d.ts +90 -0
  85. package/dist/core/upkeep.js +176 -0
  86. package/dist/core/upkeep.js.map +1 -0
  87. package/dist/core/util.d.ts +46 -0
  88. package/dist/core/util.js +168 -0
  89. package/dist/core/util.js.map +1 -0
  90. package/dist/core/version.d.ts +39 -0
  91. package/dist/core/version.js +144 -0
  92. package/dist/core/version.js.map +1 -0
  93. package/dist/core/worktree.d.ts +51 -0
  94. package/dist/core/worktree.js +309 -0
  95. package/dist/core/worktree.js.map +1 -0
  96. package/dist/daemon/budget.d.ts +155 -0
  97. package/dist/daemon/budget.js +288 -0
  98. package/dist/daemon/budget.js.map +1 -0
  99. package/dist/daemon/election.d.ts +9 -0
  100. package/dist/daemon/election.js +140 -0
  101. package/dist/daemon/election.js.map +1 -0
  102. package/dist/daemon/engine.d.ts +1703 -0
  103. package/dist/daemon/engine.js +5305 -0
  104. package/dist/daemon/engine.js.map +1 -0
  105. package/dist/daemon/index.d.ts +2 -0
  106. package/dist/daemon/index.js +412 -0
  107. package/dist/daemon/index.js.map +1 -0
  108. package/dist/daemon/proxy.d.ts +108 -0
  109. package/dist/daemon/proxy.js +615 -0
  110. package/dist/daemon/proxy.js.map +1 -0
  111. package/dist/daemon/supervisor.d.ts +101 -0
  112. package/dist/daemon/supervisor.js +493 -0
  113. package/dist/daemon/supervisor.js.map +1 -0
  114. package/dist/drivers/appliances.d.ts +11 -0
  115. package/dist/drivers/appliances.js +174 -0
  116. package/dist/drivers/appliances.js.map +1 -0
  117. package/dist/drivers/datastores.d.ts +144 -0
  118. package/dist/drivers/datastores.js +775 -0
  119. package/dist/drivers/datastores.js.map +1 -0
  120. package/dist/drivers/preview.d.ts +69 -0
  121. package/dist/drivers/preview.js +605 -0
  122. package/dist/drivers/preview.js.map +1 -0
  123. package/dist/drivers/types.d.ts +70 -0
  124. package/dist/drivers/types.js +13 -0
  125. package/dist/drivers/types.js.map +1 -0
  126. package/package.json +63 -4
  127. package/schema/backlot.schema.json +549 -0
  128. package/schema/runly.schema.json +556 -0
@@ -0,0 +1,549 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/backlot-dev/backlot/main/schema/backlot.schema.json",
4
+ "title": "Runly stack manifest (runly.yml; backlot.yml and stack.yaml accepted)",
5
+ "description": "What a repo declares so runly can run environments for it, in the worktree the manifest lives in. Everything {{...}} is injected by the engine (ports, public_ports, services.<svc>.url, datastores.<ds>.url/.ns). Policy (TTLs, pool caps, the load budget, substrates) never lives here.",
6
+ "type": "object",
7
+ "required": [
8
+ "name",
9
+ "services"
10
+ ],
11
+ "additionalProperties": false,
12
+ "properties": {
13
+ "name": {
14
+ "type": "string",
15
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
16
+ "description": "Stack name. A stack is this name plus the worktree the manifest is in; each worktree has one environment."
17
+ },
18
+ "services": {
19
+ "type": "object",
20
+ "minProperties": 1,
21
+ "additionalProperties": {
22
+ "$ref": "#/$defs/service"
23
+ },
24
+ "description": "Supervised shell commands, one per service, run in the worktree. Commands, not containers."
25
+ },
26
+ "appliances": {
27
+ "type": "object",
28
+ "description": "Singular, machine-shared backing services (a Postgres server, a Redis) that environments point at but never own. Ensured (probe -> start once machine-wide -> wait), never supervised, never stopped automatically.",
29
+ "additionalProperties": {
30
+ "$ref": "#/$defs/appliance"
31
+ }
32
+ },
33
+ "datastores": {
34
+ "type": "object",
35
+ "additionalProperties": {
36
+ "$ref": "#/$defs/datastore"
37
+ },
38
+ "description": "Data an environment owns: one namespace per environment (and per `runly db` copy) on an externally run server, or a sqlite file."
39
+ },
40
+ "builds": {
41
+ "enum": [
42
+ "parallel",
43
+ "serial"
44
+ ],
45
+ "default": "parallel",
46
+ "description": "How one `up` (or `warm`) runs its services' builds. `parallel` (default): in depends_on waves \u2014 a build runs after the builds of what its service depends on, and the builds of one wave run at once (the load budget holds the costliest wave's summed resources). `serial`: one at a time, in manifest order \u2014 for builds that share an output or a lock the build tool does not handle."
47
+ },
48
+ "caches": {
49
+ "type": "array",
50
+ "items": {
51
+ "type": "string"
52
+ },
53
+ "description": "Build and install output inside the worktree (node_modules, **/obj, .angular, ...). Environments run in the worktree (decision 0032), so this is what is NOT source: an upkeep `when:` glob never matches it. runly never deletes it."
54
+ },
55
+ "sync": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "properties": {
59
+ "keep": {
60
+ "type": "array",
61
+ "items": {
62
+ "type": "string"
63
+ },
64
+ "description": "Accepted and ignored."
65
+ },
66
+ "include": {
67
+ "type": "array",
68
+ "items": {
69
+ "type": "string"
70
+ },
71
+ "description": "Git-ignored files that still count as source (.env.local), so an upkeep `when:` glob can match them."
72
+ }
73
+ },
74
+ "description": "Only `include` is read; `keep` is accepted and ignored."
75
+ },
76
+ "outputs": {
77
+ "type": "array",
78
+ "items": {
79
+ "type": "string"
80
+ },
81
+ "description": "Accepted and ignored. Build outputs that decide a restart are declared per service (`services.<name>.outputs`)."
82
+ },
83
+ "upkeep": {
84
+ "type": "array",
85
+ "items": {
86
+ "$ref": "#/$defs/upkeepRule"
87
+ },
88
+ "description": "Closed list of (trigger files -> command) rules, run in order by `up` and `runly warm` when the content of a rule's trigger files changed since it last ran in this worktree."
89
+ },
90
+ "auth": {
91
+ "type": "object",
92
+ "additionalProperties": false,
93
+ "properties": {
94
+ "logins": {
95
+ "oneOf": [
96
+ {
97
+ "$ref": "#/$defs/login"
98
+ },
99
+ {
100
+ "type": "array",
101
+ "minItems": 1,
102
+ "items": {
103
+ "$ref": "#/$defs/login"
104
+ }
105
+ }
106
+ ],
107
+ "description": "Seeded login(s) consumers may use. Dev-grade by definition. A single object is one login; a list declares several, and the FIRST is the primary one `ctx.logins` reports."
108
+ },
109
+ "token": {
110
+ "type": "string",
111
+ "description": "Command printing an auth token on stdout (trimmed; `runly token` wraps it as {token, role}); {{role}} available, also as RUNLY_ROLE in its environment. Runs in the worktree."
112
+ }
113
+ }
114
+ },
115
+ "checks": {
116
+ "type": "object",
117
+ "description": "Accepted and ignored, with a one-line warning: runly runs no checks. Repo scripts read the environment from `runly ctx --env`."
118
+ },
119
+ "preview": {
120
+ "type": "object",
121
+ "additionalProperties": false,
122
+ "properties": {
123
+ "forbidden": {
124
+ "type": "boolean",
125
+ "description": "When true, `runly preview` is refused (work-error), and a bind ends a live preview. Stacks with fixed dev credentials must set this."
126
+ },
127
+ "publisher": {
128
+ "type": "string",
129
+ "description": "Named preview publisher adapter (default: cloudflare-quick). Known: cloudflare-quick, cloudflare-named, tailscale (tailnet-only HTTPS via `tailscale serve`)."
130
+ },
131
+ "domain": {
132
+ "type": "string",
133
+ "description": "Zone a naming publisher publishes under, e.g. example.dev. Required by cloudflare-named; ignored by cloudflare-quick."
134
+ },
135
+ "prefix": {
136
+ "type": "string",
137
+ "description": "What makes a hostname this environment's own (<service>-<prefix>.<domain>). Unset means the environment id, which is unique by construction; set it only to pin a memorable name, and only where one environment of this stack runs at a time."
138
+ },
139
+ "https_port": {
140
+ "type": "integer",
141
+ "minimum": 1,
142
+ "maximum": 65535,
143
+ "description": "tailscale publisher only: the HTTPS port on this machine's tailnet name. Unset means a port derived from the environment id and service, stable for the environment's lifetime. Pin it only where one environment of this stack publishes at a time; ignored by the Cloudflare publishers."
144
+ }
145
+ }
146
+ }
147
+ },
148
+ "$defs": {
149
+ "login": {
150
+ "type": "object",
151
+ "required": [
152
+ "user",
153
+ "password"
154
+ ],
155
+ "additionalProperties": false,
156
+ "properties": {
157
+ "user": {
158
+ "type": "string"
159
+ },
160
+ "password": {
161
+ "type": "string"
162
+ },
163
+ "role": {
164
+ "type": "string",
165
+ "description": "The role this login stands for, matching the {{role}} an `auth.token` hook takes. Informational."
166
+ },
167
+ "description": {
168
+ "type": "string",
169
+ "description": "What this login is FOR \u2014 the scope or permissions it exercises, so a consumer can pick the right one without reading the seed."
170
+ }
171
+ }
172
+ },
173
+ "service": {
174
+ "type": "object",
175
+ "required": [
176
+ "run"
177
+ ],
178
+ "additionalProperties": false,
179
+ "properties": {
180
+ "run": {
181
+ "type": "string",
182
+ "description": "The supervised process; the daemon is its parent. Runs in the worktree (or `cwd`) under `sh` (dash on Ubuntu, bash-as-sh on macOS) \u2014 write POSIX sh, no bashisms."
183
+ },
184
+ "build": {
185
+ "oneOf": [
186
+ {
187
+ "type": "string"
188
+ },
189
+ {
190
+ "type": "object",
191
+ "required": [
192
+ "run"
193
+ ],
194
+ "additionalProperties": false,
195
+ "properties": {
196
+ "run": {
197
+ "type": "string",
198
+ "description": "The build command."
199
+ },
200
+ "when": {
201
+ "type": "array",
202
+ "minItems": 1,
203
+ "items": {
204
+ "type": "string"
205
+ },
206
+ "description": "Globs (relative to the stack root, git-listed, minus caches:) of what the build reads. While none of the matched files changed (path, size, mtime) since the last SUCCESSFUL build of this service in this worktree, the build is skipped and `up` says so; `up --rebuild` forces it."
207
+ },
208
+ "serial": {
209
+ "type": "boolean",
210
+ "default": false,
211
+ "description": "Run this build alone, never at the same time as another service's build. By default the builds of services that do not depend on each other run at once (depends_on waves)."
212
+ }
213
+ }
214
+ }
215
+ ],
216
+ "description": "String form: runs on every `up` that starts or keeps this service, and on every `runly warm` \u2014 runly keeps no build cache; the build tool decides what is current. Object form `{run, when, serial}`: skipped while its `when:` files are unchanged since the last successful build and its `outputs:` are still what that build left; `serial: true` runs it alone. Builds otherwise run in depends_on waves (see top-level `builds`). Runs under `sh` (dash on Ubuntu, bash-as-sh on macOS) \u2014 write POSIX sh, no bashisms."
217
+ },
218
+ "outputs": {
219
+ "oneOf": [
220
+ {
221
+ "type": "string"
222
+ },
223
+ {
224
+ "type": "array",
225
+ "items": {
226
+ "type": "string"
227
+ }
228
+ },
229
+ {
230
+ "type": "object",
231
+ "required": [
232
+ "paths"
233
+ ],
234
+ "additionalProperties": false,
235
+ "properties": {
236
+ "paths": {
237
+ "type": "array",
238
+ "minItems": 1,
239
+ "items": {
240
+ "type": "string"
241
+ }
242
+ },
243
+ "compare": {
244
+ "enum": [
245
+ "stat",
246
+ "content"
247
+ ],
248
+ "default": "stat",
249
+ "description": "stat: path, size and mtime (default). content: hash the files, so a build that rewrites identical files does not restart the service."
250
+ }
251
+ }
252
+ }
253
+ ],
254
+ "description": "Globs (relative to the stack root) of what `build:` produces, e.g. `backend/Host/bin/**` or `dist/web`. Every `up` snapshots them around the build and restarts this service only when they changed. Without it, a service with a build is restarted after every build. The object form chooses how they are compared."
255
+ },
256
+ "watch_run": {
257
+ "type": "string",
258
+ "description": "Accepted and ignored; the service always runs `run:`."
259
+ },
260
+ "cwd": {
261
+ "type": "string",
262
+ "description": "Working directory relative to the repo root."
263
+ },
264
+ "port": {
265
+ "type": "string",
266
+ "pattern": "^[a-z][a-z0-9-]*$",
267
+ "description": "Symbolic port name; the engine allocates a public port (held by the daemon's proxy) and starts the service on an internal one. In this service's own run/env, {{ports.<name>}} is the internal port to listen on; everywhere else it is the public port ({{public_ports.<name>}} is always public). Omit for portless services (workers)."
268
+ },
269
+ "env": {
270
+ "type": "object",
271
+ "additionalProperties": {
272
+ "type": "string"
273
+ },
274
+ "description": "Injected environment; values may template {{ports.*}}, {{public_ports.*}}, {{services.*.url}}, {{datastores.*.url}}, {{datastores.*.ns}}."
275
+ },
276
+ "env_from": {
277
+ "type": "object",
278
+ "propertyNames": {
279
+ "pattern": "^(?!BACKLOT_)[A-Za-z_][A-Za-z0-9_]*$"
280
+ },
281
+ "additionalProperties": {
282
+ "enum": [
283
+ "required",
284
+ "optional"
285
+ ]
286
+ },
287
+ "description": "Allowlisted caller environment inputs: `required` refuses an `up` without the variable, `optional` may be missing. Every `up` refreshes the values from its caller; reset-data keeps them. A supplied value overrides a same-named env entry; a missing optional input keeps an explicit env default, otherwise masks the daemon's value. Memory only: re-run up after a daemon restart. Not passed to builds, upkeep or exec."
288
+ },
289
+ "ready": {
290
+ "$ref": "#/$defs/readiness"
291
+ },
292
+ "fatal_logs": {
293
+ "type": "string",
294
+ "description": "ERE of log markers that fail a boot in seconds instead of polling readiness to timeout."
295
+ },
296
+ "depends_on": {
297
+ "type": "array",
298
+ "items": {
299
+ "type": "string"
300
+ },
301
+ "description": "Services that must be ready before this one starts. `up <svc>` starts them too, and a wake starts them through their ports; a service whose readiness needs another must list it."
302
+ },
303
+ "hot_reload": {
304
+ "type": "boolean",
305
+ "description": "Accepted and ignored. Whether an `up` restarts a running service is decided by its build `outputs:`; a service without a build keeps running."
306
+ },
307
+ "idle": {
308
+ "oneOf": [
309
+ {
310
+ "type": "string",
311
+ "pattern": "^(never|off|[0-9]+(\\.[0-9]+)?\\s*(ms|s|m|h))$"
312
+ },
313
+ {
314
+ "type": "number",
315
+ "minimum": 0
316
+ }
317
+ ],
318
+ "description": "How long this service may go without client bytes through its public port and without a runly verb on its environment before it is stopped; the next connection to its port starts it again (leased environments only). A duration (90s, 10m, 2h), seconds as a number, or `never`/`off`. Default BACKLOT_SERVICE_IDLE_MS (10 minutes)."
319
+ },
320
+ "resources": {
321
+ "$ref": "#/$defs/resources"
322
+ }
323
+ }
324
+ },
325
+ "readiness": {
326
+ "type": "object",
327
+ "additionalProperties": false,
328
+ "minProperties": 1,
329
+ "properties": {
330
+ "http": {
331
+ "type": "string",
332
+ "description": "Path requested on the service's internal port until it answers 200."
333
+ },
334
+ "log": {
335
+ "type": "string",
336
+ "description": "ERE that must appear in the service log (portless services)."
337
+ },
338
+ "cmd": {
339
+ "type": "string",
340
+ "description": "Command that must exit 0 (run in the worktree)."
341
+ },
342
+ "timeout": {
343
+ "type": "number",
344
+ "default": 120,
345
+ "description": "Seconds before service-not-ready."
346
+ }
347
+ }
348
+ },
349
+ "datastore": {
350
+ "type": "object",
351
+ "required": [
352
+ "driver"
353
+ ],
354
+ "additionalProperties": false,
355
+ "properties": {
356
+ "driver": {
357
+ "type": "string",
358
+ "enum": [
359
+ "sqlite",
360
+ "postgres",
361
+ "mssql",
362
+ "mysql",
363
+ "redis"
364
+ ]
365
+ },
366
+ "server": {
367
+ "type": "string",
368
+ "enum": [
369
+ "external"
370
+ ],
371
+ "default": "external",
372
+ "description": "runly never runs datastore servers; it probes them."
373
+ },
374
+ "probe": {
375
+ "type": "string",
376
+ "description": "host:port TCP-probed before anything starts (server drivers). Failure is an infra-error, never code blame."
377
+ },
378
+ "url": {
379
+ "type": "string",
380
+ "description": "Connection-string template handed to services, `ctx` and `runly ctx --env`; {{ns}} available. Required for server drivers; ignored for sqlite (the ns IS the file path)."
381
+ },
382
+ "create": {
383
+ "type": "string",
384
+ "description": "Repo command that creates+seeds a namespace; {{ns}} and {{preset}} available."
385
+ },
386
+ "drop": {
387
+ "type": "string",
388
+ "description": "Repo command that drops a namespace; {{ns}} available. Run before a restore, at teardown and for copies (recorded when the namespace is created, so a later drop needs no manifest). Required for `runly db` copies of a server datastore."
389
+ },
390
+ "template_restore": {
391
+ "type": "string",
392
+ "description": "Repo command that clones a baked template namespace into a fresh one; {{template}} and {{ns}} available (e.g. postgres: createdb -T {{template}} {{ns}}). When present, `create` bakes a template once per preset and template key (the create command, plus the trigger content of an @rebake-template rule), and every bind and copy restores from it."
393
+ },
394
+ "presets": {
395
+ "type": "array",
396
+ "items": {
397
+ "type": "string"
398
+ },
399
+ "description": "Named seed states; `--preset` and `default_preset` must name one of them. Omitted: the preset is `default` (or the default_preset name)."
400
+ },
401
+ "default_preset": {
402
+ "description": "The preset a datastore is created with when none is named (default: the first of `presets`). A preset name, or `{session, run}` read as one value (session, then run).",
403
+ "oneOf": [
404
+ {
405
+ "type": "string",
406
+ "minLength": 1
407
+ },
408
+ {
409
+ "type": "object",
410
+ "additionalProperties": false,
411
+ "properties": {
412
+ "run": {
413
+ "type": "string"
414
+ },
415
+ "session": {
416
+ "type": "string"
417
+ }
418
+ }
419
+ }
420
+ ]
421
+ },
422
+ "template": {
423
+ "type": "boolean",
424
+ "default": false,
425
+ "description": "sqlite only: bake a template file per preset and template key, and restore by copy. Server drivers use template_restore instead."
426
+ },
427
+ "ephemeral": {
428
+ "type": "boolean",
429
+ "default": false,
430
+ "description": "No presets or templates; `create` runs on first bind and a reset runs `drop` as the flush (Redis-class)."
431
+ },
432
+ "copies_only": {
433
+ "type": "boolean",
434
+ "default": false,
435
+ "description": "Never provisioned for an environment: the datastore exists only as the source of `runly db new|with` copies (its template is baked on the first copy and reused after). No service may template it, `ctx` and `ctx --env` do not list it, and `up --preset` / `reset-data` cannot name it."
436
+ },
437
+ "share_templates": {
438
+ "type": "boolean",
439
+ "default": true,
440
+ "description": "Share this datastore's baked templates with every worktree of the same stack name on this daemon (decision 0044). Applies only to a content-keyed template — one an `@rebake-template` rule names, so its key is the create command plus the content of the rule's trigger files; without such a rule templates stay per worktree. Set false when the baked content depends on the worktree beyond those files (an absolute path written into the data, say)."
441
+ },
442
+ "list": {
443
+ "type": "string",
444
+ "description": "Repo command printing the namespaces ({{ns}} values) of this datastore that exist on its server, one per line. Read only by `runly pool doctor`: a listed name in runly's naming (backlot_...) that no journal row or template marker references is an orphan, which `--fix` drops with `drop:` (decision 0037)."
445
+ }
446
+ }
447
+ },
448
+ "upkeepRule": {
449
+ "type": "object",
450
+ "required": [
451
+ "when",
452
+ "run"
453
+ ],
454
+ "additionalProperties": false,
455
+ "properties": {
456
+ "when": {
457
+ "type": "string",
458
+ "description": "A file path or glob (optionally written glob(...)), relative to the stack root, whose matched files' content triggers the rule. Recorded per worktree, direction-agnostic: a changed hash in either direction runs it again."
459
+ },
460
+ "run": {
461
+ "type": "string",
462
+ "description": "Repo command run in the worktree, or the engine built-in `@rebake-template <datastore>` (rebakes that datastore's template)."
463
+ },
464
+ "timeout": {
465
+ "type": "number",
466
+ "exclusiveMinimum": 0,
467
+ "description": "Command deadline in seconds (default 300). BACKLOT_CMD_TIMEOUT_S overrides this value."
468
+ }
469
+ }
470
+ },
471
+ "appliance": {
472
+ "type": "object",
473
+ "required": [
474
+ "probe"
475
+ ],
476
+ "additionalProperties": false,
477
+ "properties": {
478
+ "probe": {
479
+ "type": "string",
480
+ "description": "host:port TCP-probed; reachability IS the appliance's identity \u2014 whoever answers is the appliance, no matter who started it."
481
+ },
482
+ "start": {
483
+ "type": "string",
484
+ "description": "Daemonizing repo command (e.g. docker run -d ...) run ONCE machine-wide when the probe fails. Absent = today's behavior: unreachable is an infra-error you resolve yourself."
485
+ },
486
+ "stop": {
487
+ "type": "string",
488
+ "description": "Repo command for the explicit `runly appliance stop` verb. Never run automatically \u2014 appliances are durable; heat is the point."
489
+ },
490
+ "ready": {
491
+ "type": "string",
492
+ "description": "Optional readiness gate polled after TCP accepts (exit 0 = ready), e.g. pg_isready \u2014 TCP-accept alone can precede real readiness."
493
+ },
494
+ "timeout": {
495
+ "type": "number",
496
+ "default": 60,
497
+ "description": "Seconds to wait after start for probe (and ready:) to pass before infra-error."
498
+ },
499
+ "resources": {
500
+ "$ref": "#/$defs/resources",
501
+ "description": "What the appliance costs while running; counted against the load budget when an `up` would start it. Its build: entry is ignored."
502
+ }
503
+ }
504
+ },
505
+ "size": {
506
+ "oneOf": [
507
+ {
508
+ "type": "string",
509
+ "pattern": "^[0-9]+(\\.[0-9]+)?\\s*([KMGT]i?B?|[kmgt]i?b?)?$"
510
+ },
511
+ {
512
+ "type": "number",
513
+ "minimum": 0
514
+ }
515
+ ],
516
+ "description": "A size: bytes as a number, or a string with a K/M/G/T suffix (600M, 2G, 1.5GiB)."
517
+ },
518
+ "resources": {
519
+ "type": "object",
520
+ "additionalProperties": false,
521
+ "description": "What this costs the box, for the server-wide load budget. Left out = a conservative default (run 512M / 0.5 cpu; build 1G / 1 cpu), which `runly plan` says it assumed.",
522
+ "properties": {
523
+ "memory": {
524
+ "$ref": "#/$defs/size",
525
+ "description": "Resident memory while running."
526
+ },
527
+ "cpu": {
528
+ "type": "number",
529
+ "exclusiveMinimum": 0,
530
+ "description": "Cores while running."
531
+ },
532
+ "build": {
533
+ "type": "object",
534
+ "additionalProperties": false,
535
+ "description": "What its `build:` costs while it runs; held only while building.",
536
+ "properties": {
537
+ "memory": {
538
+ "$ref": "#/$defs/size"
539
+ },
540
+ "cpu": {
541
+ "type": "number",
542
+ "exclusiveMinimum": 0
543
+ }
544
+ }
545
+ }
546
+ }
547
+ }
548
+ }
549
+ }