@homeflare/alchemy 0.31.1 → 0.32.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/README.md +44 -7
- package/dist/grafana/index.d.ts +6 -5
- package/dist/grafana/index.d.ts.map +1 -1
- package/dist/linux/container-form.d.ts +129 -0
- package/dist/linux/container-form.d.ts.map +1 -0
- package/dist/linux/container-generator.d.ts +74 -0
- package/dist/linux/container-generator.d.ts.map +1 -0
- package/dist/linux/container-handlers.d.ts +15 -0
- package/dist/linux/container-handlers.d.ts.map +1 -0
- package/dist/linux/container-lifecycle.d.ts +37 -0
- package/dist/linux/container-lifecycle.d.ts.map +1 -0
- package/dist/linux/container-preflight.d.ts +34 -0
- package/dist/linux/container-preflight.d.ts.map +1 -0
- package/dist/linux/container-secrets.d.ts +37 -0
- package/dist/linux/container-secrets.d.ts.map +1 -0
- package/dist/linux/container-settle.d.ts +34 -0
- package/dist/linux/container-settle.d.ts.map +1 -0
- package/dist/linux/container.d.ts +30 -0
- package/dist/linux/container.d.ts.map +1 -0
- package/dist/linux/fake-quadlet.d.ts +25 -0
- package/dist/linux/fake-quadlet.d.ts.map +1 -0
- package/dist/linux/index.d.ts +6 -3
- package/dist/linux/index.d.ts.map +1 -1
- package/dist/linux/index.js +641 -268
- package/dist/linux/index.js.map +15 -7
- package/dist/linux/providers.d.ts +1 -1
- package/dist/linux/providers.d.ts.map +1 -1
- package/dist/linux/systemctl.d.ts +7 -1
- package/dist/linux/systemctl.d.ts.map +1 -1
- package/dist/opnsense/alias-form.d.ts +29 -0
- package/dist/opnsense/alias-form.d.ts.map +1 -0
- package/dist/opnsense/alias.d.ts +81 -0
- package/dist/opnsense/alias.d.ts.map +1 -0
- package/dist/opnsense/category-form.d.ts +10 -0
- package/dist/opnsense/category-form.d.ts.map +1 -0
- package/dist/opnsense/category.d.ts +58 -0
- package/dist/opnsense/category.d.ts.map +1 -0
- package/dist/opnsense/credentials.d.ts +36 -0
- package/dist/opnsense/credentials.d.ts.map +1 -0
- package/dist/opnsense/fake-opnsense.d.ts +51 -0
- package/dist/opnsense/fake-opnsense.d.ts.map +1 -0
- package/dist/opnsense/group-form.d.ts +11 -0
- package/dist/opnsense/group-form.d.ts.map +1 -0
- package/dist/opnsense/group.d.ts +62 -0
- package/dist/opnsense/group.d.ts.map +1 -0
- package/dist/opnsense/index.d.ts +16 -0
- package/dist/opnsense/index.d.ts.map +1 -0
- package/dist/opnsense/index.js +211 -0
- package/dist/opnsense/index.js.map +20 -0
- package/dist/opnsense/policy.d.ts +20 -0
- package/dist/opnsense/policy.d.ts.map +1 -0
- package/dist/opnsense/providers.d.ts +13 -0
- package/dist/opnsense/providers.d.ts.map +1 -0
- package/dist/opnsense/resource.d.ts +74 -0
- package/dist/opnsense/resource.d.ts.map +1 -0
- package/dist/opnsense/wire.d.ts +39 -0
- package/dist/opnsense/wire.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token.d.ts.map +1 -1
- package/dist/proxmox/group-wire.d.ts +44 -5
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/group.d.ts.map +1 -1
- package/dist/proxmox/index.js +326 -264
- package/dist/proxmox/index.js.map +13 -11
- package/dist/proxmox/role.d.ts.map +1 -1
- package/dist/proxmox/storage-form.d.ts +54 -0
- package/dist/proxmox/storage-form.d.ts.map +1 -0
- package/dist/proxmox/storage-wire.d.ts +56 -0
- package/dist/proxmox/storage-wire.d.ts.map +1 -0
- package/dist/proxmox/storage.d.ts +55 -60
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/user-wire.d.ts +34 -5
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +6 -6
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/unifi/fake-unifi.d.ts +48 -0
- package/dist/unifi/fake-unifi.d.ts.map +1 -0
- package/dist/unifi/firewall-zone-form.d.ts +40 -0
- package/dist/unifi/firewall-zone-form.d.ts.map +1 -0
- package/dist/unifi/firewall-zone.d.ts +50 -0
- package/dist/unifi/firewall-zone.d.ts.map +1 -0
- package/dist/unifi/index.d.ts +13 -0
- package/dist/unifi/index.d.ts.map +1 -0
- package/dist/unifi/index.js +190 -0
- package/dist/unifi/index.js.map +16 -0
- package/dist/unifi/network-form.d.ts +67 -0
- package/dist/unifi/network-form.d.ts.map +1 -0
- package/dist/unifi/network.d.ts +67 -0
- package/dist/unifi/network.d.ts.map +1 -0
- package/dist/unifi/policy.d.ts +20 -0
- package/dist/unifi/policy.d.ts.map +1 -0
- package/dist/unifi/providers.d.ts +13 -0
- package/dist/unifi/providers.d.ts.map +1 -0
- package/dist/unifi/resource.d.ts +64 -0
- package/dist/unifi/resource.d.ts.map +1 -0
- package/dist/verify/fake-engine.d.ts +14 -0
- package/dist/verify/fake-engine.d.ts.map +1 -1
- package/docs/grafana.md +47 -17
- package/docs/opnsense.md +146 -0
- package/docs/provider-standard.md +1 -1
- package/docs/quadlet-container.md +132 -0
- package/docs/unifi.md +93 -0
- package/docs/upstream-conformance.md +14 -4
- package/package.json +13 -3
- package/src/grafana/index.ts +6 -5
- package/src/linux/container-fixture.test.ts +92 -0
- package/src/linux/container-form.ts +243 -0
- package/src/linux/container-generator.test.ts +195 -0
- package/src/linux/container-generator.ts +113 -0
- package/src/linux/container-handlers.ts +73 -0
- package/src/linux/container-lifecycle.test.ts +209 -0
- package/src/linux/container-lifecycle.ts +217 -0
- package/src/linux/container-preflight.ts +97 -0
- package/src/linux/container-secrets.test.ts +171 -0
- package/src/linux/container-secrets.ts +112 -0
- package/src/linux/container-settle.ts +97 -0
- package/src/linux/container.ts +60 -0
- package/src/linux/fake-quadlet.ts +127 -0
- package/src/linux/index.ts +11 -3
- package/src/linux/providers.ts +2 -0
- package/src/linux/systemctl.ts +9 -0
- package/src/opnsense/alias-form.test.ts +89 -0
- package/src/opnsense/alias-form.ts +53 -0
- package/src/opnsense/alias.test.ts +203 -0
- package/src/opnsense/alias.ts +117 -0
- package/src/opnsense/category-form.test.ts +54 -0
- package/src/opnsense/category-form.ts +20 -0
- package/src/opnsense/category.test.ts +138 -0
- package/src/opnsense/category.ts +82 -0
- package/src/opnsense/credentials.ts +41 -0
- package/src/opnsense/fake-opnsense.ts +79 -0
- package/src/opnsense/group-form.test.ts +75 -0
- package/src/opnsense/group-form.ts +25 -0
- package/src/opnsense/group.test.ts +141 -0
- package/src/opnsense/group.ts +78 -0
- package/src/opnsense/index.ts +45 -0
- package/src/opnsense/policy.ts +49 -0
- package/src/opnsense/providers.ts +21 -0
- package/src/opnsense/resource.ts +146 -0
- package/src/opnsense/wire.ts +56 -0
- package/src/opnsense/write-refusal.test.ts +148 -0
- package/src/proxmox/acl.ts +10 -4
- package/src/proxmox/api-token.ts +20 -12
- package/src/proxmox/group-wire.ts +62 -8
- package/src/proxmox/group.ts +23 -35
- package/src/proxmox/read-failure-propagation.test.ts +183 -0
- package/src/proxmox/role.ts +9 -2
- package/src/proxmox/storage-form.ts +144 -0
- package/src/proxmox/storage-read-failure.test.ts +214 -0
- package/src/proxmox/storage-wire.ts +124 -0
- package/src/proxmox/storage.ts +165 -175
- package/src/proxmox/user-wire.ts +44 -8
- package/src/proxmox/user.ts +29 -27
- package/src/unifi/fake-unifi.ts +60 -0
- package/src/unifi/firewall-zone-form.ts +65 -0
- package/src/unifi/firewall-zone.test.ts +88 -0
- package/src/unifi/firewall-zone.ts +66 -0
- package/src/unifi/index.ts +26 -0
- package/src/unifi/network-form.ts +207 -0
- package/src/unifi/network.test.ts +209 -0
- package/src/unifi/network.ts +83 -0
- package/src/unifi/policy.ts +37 -0
- package/src/unifi/providers.ts +15 -0
- package/src/unifi/resource.test.ts +133 -0
- package/src/unifi/resource.ts +131 -0
- package/src/verify/fake-engine.ts +23 -0
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@ bun add @homeflare/alchemy alchemy@2.0.0-beta.79 effect@4.0.0-rc.115 \
|
|
|
7
7
|
@effect/platform-node@4.0.0-rc.115 mime@4.1.0 \
|
|
8
8
|
@distilled.cloud/cloudflare@1.0.0-rc.12 @distilled.cloud/forgejo@1.0.0-rc.12 \
|
|
9
9
|
@distilled.cloud/discord@1.0.0-rc.12 @distilled.cloud/google-workspace@1.0.0-rc.12 \
|
|
10
|
-
@distilled.cloud/grafana@1.0.0-rc.12 \
|
|
11
10
|
@effect/sql-pg@4.0.0-rc.115
|
|
12
11
|
```
|
|
13
12
|
|
|
@@ -149,14 +148,16 @@ one is logged (argv only, never content) before it runs. A plan never calls sudo
|
|
|
149
148
|
|
|
150
149
|
## Linux hosts — `@homeflare/alchemy/linux`
|
|
151
150
|
|
|
152
|
-
The same `HostRunner` seam, over ssh, plus the
|
|
151
|
+
The same `HostRunner` seam, over ssh, plus the four families the launchd subpath had no Linux twin
|
|
153
152
|
for: `HostDirectory` (because no file resource creates a parent), `RemoteFile` (a whole file, or one
|
|
154
|
-
**managed block** inside a file somebody else owns)
|
|
155
|
-
`
|
|
153
|
+
**managed block** inside a file somebody else owns), `SystemdUnit` / `SystemdTimer`, and
|
|
154
|
+
`PodmanContainer` (one Quadlet `.container` file and the systemd unit its generator produces).
|
|
155
|
+
`linuxProviders(await sshRunner({ host }))` provides all five.
|
|
156
156
|
|
|
157
157
|
```ts
|
|
158
158
|
RemoteFile('block', { path: '/etc/example.conf', region: { name: 'homeflare' }, content: 'a line\n' });
|
|
159
159
|
SystemdUnit('thing', { name: 'thing.service', sections: [...], restartOn: [config.sha256] });
|
|
160
|
+
PodmanContainer('thing', { name: 'thing', container: { image: 'example/thing:1', network: 'host' } });
|
|
160
161
|
```
|
|
161
162
|
|
|
162
163
|
- ⛔ **A deploy never mass-restarts.** A unit restarts only when its own file changed, when state or
|
|
@@ -167,7 +168,12 @@ SystemdUnit('thing', { name: 'thing.service', sections: [...], restartOn: [confi
|
|
|
167
168
|
without its framing marker is an Error — never "nothing is there".
|
|
168
169
|
- ⛔ **No silent sudo:** a root-owned path needs a root ssh destination or your own privileged runner.
|
|
169
170
|
- ⛔ **The directive set is systemd's:** unit files render verbatim; nothing here invents a schema.
|
|
171
|
+
- ⛔ **`PodmanContainer`'s `[Container]` section IS typed and doc-checked** (Podman 5.4,
|
|
172
|
+
`podman-systemd.unit(5)`) — Quadlet's own schema, unlike bare `systemd.unit(5)`. It has no
|
|
173
|
+
`enabled` prop and never calls `systemctl enable`: Quadlet applies `[Install]` itself at every
|
|
174
|
+
`daemon-reload`, and a generator failure is a typed `QuadletGeneratorError`, never "absent".
|
|
170
175
|
- Guide, the measured `systemctl` shapes and the limits: [docs/linux-host.md](./docs/linux-host.md).
|
|
176
|
+
`PodmanContainer`'s own guide: [docs/quadlet-container.md](./docs/quadlet-container.md).
|
|
171
177
|
|
|
172
178
|
## Release binaries — `@homeflare/alchemy/release`
|
|
173
179
|
|
|
@@ -249,6 +255,17 @@ endpoint lives in ONE `general_settings` field (a whole-list read-modify-write),
|
|
|
249
255
|
declared in `config.yaml` is refused rather than silently overridden, and a literal secret in a
|
|
250
256
|
forwarded header is refused at plan: [docs/litellm.md](./docs/litellm.md).
|
|
251
257
|
|
|
258
|
+
## OPNsense — `@homeflare/alchemy/opnsense`
|
|
259
|
+
|
|
260
|
+
`Opnsense.Firewall.Alias`, `.Category` and `.Group` — READ-ONLY BY DESIGN, generated
|
|
261
|
+
from `@distilled.cloud/opnsense` (an interim alias, like NetBox/Proxmox above — not a
|
|
262
|
+
peer, `bun add` resolves it from the tarball). ⛔ `reconcile` and `delete` always
|
|
263
|
+
refuse with a typed error; there is no flag that lifts it, and no write operation is
|
|
264
|
+
reachable from any handler (`write-refusal.test.ts` proves it against a fake that
|
|
265
|
+
fails on any non-GET). `read` answers `Unowned` on every match, `adopt(true)` is on
|
|
266
|
+
by default, and a pure declaration renderer turns one live read into the props a
|
|
267
|
+
noop declaration needs: [docs/opnsense.md](./docs/opnsense.md).
|
|
268
|
+
|
|
252
269
|
## Discord — `@homeflare/alchemy/discord`
|
|
253
270
|
|
|
254
271
|
`Discord.ApplicationCommand` (global) and `Discord.GuildApplicationCommand` (guild-scoped)
|
|
@@ -271,6 +288,24 @@ export const help =
|
|
|
271
288
|
});
|
|
272
289
|
```
|
|
273
290
|
|
|
291
|
+
## UniFi Network — `@homeflare/alchemy/unifi`
|
|
292
|
+
|
|
293
|
+
`Unifi.Network` and `Unifi.FirewallZone`, generated from Ubiquiti's own UniFi Network
|
|
294
|
+
Integration API 10.4.57 via `@distilled.cloud/unifi-network`. ⛔ **READ-ONLY, by Tim's rule
|
|
295
|
+
(2026-09-24):** `reconcile`/`delete` both fail with a typed `UnifiWriteRefused` naming the
|
|
296
|
+
policy — this family never calls a write operation, only `getNetworkDetails`/`getFirewallZone`
|
|
297
|
+
and their list counterparts. ⛔ `read` answers `Unowned` on every match (never a silent adopt —
|
|
298
|
+
`adopt(true)` is on by default via the `network`/`firewallZone` constructors), and a UniFi
|
|
299
|
+
**Console ID** (part of a cloud-connector base URL) is never logged, committed or written to
|
|
300
|
+
state: [docs/unifi.md](./docs/unifi.md).
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
import { declareNetwork, network, providers } from '@homeflare/alchemy/unifi';
|
|
304
|
+
|
|
305
|
+
// A later import script writes rows like this one from a live read:
|
|
306
|
+
export const lan = network('lan', declareNetwork(liveNetworkDetails, 'site-id'));
|
|
307
|
+
```
|
|
308
|
+
|
|
274
309
|
## Google Workspace — `@homeflare/alchemy/google-workspace`
|
|
275
310
|
|
|
276
311
|
`Group`, `GroupMember`, `DomainAlias` and `OrgUnit` over the Admin SDK Directory API, generated
|
|
@@ -283,9 +318,11 @@ schema carries a `password` field, and Alchemy persists props unencrypted. Crede
|
|
|
283
318
|
|
|
284
319
|
`Grafana.Datasource` declares one data source, keyed by `uid`, generated from
|
|
285
320
|
`@distilled.cloud/grafana`'s own typed `addDataSource`/`getDataSourceByUID`/
|
|
286
|
-
`updateDataSourceByUID`/`deleteDataSourceByUID` operations.
|
|
287
|
-
|
|
288
|
-
|
|
321
|
+
`updateDataSourceByUID`/`deleteDataSourceByUID` operations. The SDK's earlier folder/dashboard/
|
|
322
|
+
alerting-provisioning gap is fixed (`@distilled.cloud/grafana`, aliased onto
|
|
323
|
+
`@homeflare/distilled-grafana` — see [docs/distilled-interim.md](./docs/distilled-interim.md)); only
|
|
324
|
+
`Datasource` ships as a house resource so far, the rest is follow-up work:
|
|
325
|
+
[docs/grafana.md](./docs/grafana.md).
|
|
289
326
|
|
|
290
327
|
## GitHub — `@homeflare/alchemy/github`
|
|
291
328
|
|
package/dist/grafana/index.d.ts
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* the symbols a real stack consumes; the rest of the files (resource.ts's engine, fake-grafana.ts)
|
|
6
6
|
* are internals a provider needs but a consumer should not depend on.
|
|
7
7
|
*
|
|
8
|
-
* ⛔ ONLY `Grafana.Datasource` SHIPS HERE. `@distilled.cloud/grafana
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* ⛔ ONLY `Grafana.Datasource` SHIPS HERE. `@distilled.cloud/grafana` (aliased onto
|
|
9
|
+
* `@homeflare/distilled-grafana` — see docs/distilled-interim.md) now HAS folder, dashboard and
|
|
10
|
+
* alerting-provisioning CRUD operations — the SDK-level gap docs/grafana.md and
|
|
11
|
+
* docs/upstream-conformance.md recorded is fixed. Building `Grafana.Folder`, `Grafana.Dashboard`,
|
|
12
|
+
* `Grafana.AlertRule` etc. on top of them is follow-up work this PR deliberately does not do —
|
|
13
|
+
* see docs/grafana.md for what changed and what is still open.
|
|
13
14
|
*/
|
|
14
15
|
export { type DatasourceAttributes, type DatasourceProps, GrafanaDatasource, GrafanaDatasourceProvider, GrafanaSecretRefUnsetError, } from './datasource.ts';
|
|
15
16
|
export { type GrafanaTarget, grafanaCredentials } from './credentials.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grafana/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grafana/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,aAAa,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Podman.Container` props, attributes and the `.container` file renderer — the pure half of the
|
|
3
|
+
* family, mirroring unit-form.ts's split for Systemd.Unit.
|
|
4
|
+
*
|
|
5
|
+
* ★ WHY THE `[Container]` SECTION IS TYPED, UNLIKE `Systemd.Unit`'s SECTIONS. unit-form.ts renders
|
|
6
|
+
* `[Unit]`/`[Service]`/`[Install]` verbatim because there is no machine-readable schema of
|
|
7
|
+
* `systemd.unit(5)` to check a key against. Quadlet's `[Container]` section is different: it is
|
|
8
|
+
* ONE vendor's documented, versioned surface — `podman-systemd.unit(5)`, Podman 5.4 (checked
|
|
9
|
+
* against `containers/podman` tag `v5.4.0`,
|
|
10
|
+
* `docs/source/markdown/podman-systemd.unit.5.md`, read 2026-09-24) — so every key below is
|
|
11
|
+
* checked against that page, not invented. `[Unit]`/`[Service]`/`[Install]` stay close to
|
|
12
|
+
* unit-form.ts's own verbatim-lines approach, because THOSE three sections are still systemd's
|
|
13
|
+
* own directive set, which Quadlet passes straight through (same doc, "All options and tables
|
|
14
|
+
* available in standard systemd unit files are supported").
|
|
15
|
+
* ⛔ `[Container] Environment=` IS NEVER A SECRET — enforced in container-secrets.ts, called from
|
|
16
|
+
* `containerProblems` below. A container that needs a secret takes `EnvironmentFile=` pointing at
|
|
17
|
+
* a host path an out-of-band secret renderer (openbao-agent) maintains; this family never reads
|
|
18
|
+
* or renders that file's contents.
|
|
19
|
+
* ⛔ `[Install]` SUPPORTS ONLY `Alias=`/`WantedBy=`/`RequiredBy=` FOR A `.container` FILE (same doc,
|
|
20
|
+
* "Currently, only the Alias, WantedBy and RequiredBy keys are supported") — NOT `Also=`, which
|
|
21
|
+
* plain `systemd.unit(5)` allows and unit-form.ts's `INSTALL_KEYS` therefore lists. See
|
|
22
|
+
* container-generator.ts's header for why there is no `enabled` prop at all.
|
|
23
|
+
*/
|
|
24
|
+
import { configDigest, digestOf } from './unit-form.ts';
|
|
25
|
+
/** ★ Reused, not redefined: same digest and `restartOn`-join contract as Systemd.Unit. */
|
|
26
|
+
export { configDigest, digestOf };
|
|
27
|
+
export type UnitLine = readonly [key: string, value: string];
|
|
28
|
+
/**
|
|
29
|
+
* The `[Container]` keys this family supports — a deliberate SUBSET of the ~80 keys
|
|
30
|
+
* `podman-systemd.unit(5)` documents (task scope: `Image`, `ContainerName`, `Network`, `Volume`,
|
|
31
|
+
* `EnvironmentFile`, `Environment`, `PublishPort`, `Exec`, `User`, `AutoUpdate`, `PodmanArgs`).
|
|
32
|
+
* Adding another key means adding a field here and checking it against the same doc — never a
|
|
33
|
+
* free-text escape hatch, or `Environment=` secret-checking (below) could be bypassed through it.
|
|
34
|
+
*/
|
|
35
|
+
export type ContainerSection = {
|
|
36
|
+
/** `Image=`. Required. "recommended to use a fully qualified image name" (doc). */
|
|
37
|
+
readonly image: string;
|
|
38
|
+
/** `ContainerName=`. @default quadlet's own `systemd-<name>` (doc) when omitted. */
|
|
39
|
+
readonly containerName?: string;
|
|
40
|
+
/** `Network=`. MUST support `'host'` — every container this rollout declares runs host netns. */
|
|
41
|
+
readonly network?: string;
|
|
42
|
+
/** `Volume=`, repeatable. */
|
|
43
|
+
readonly volume?: readonly string[];
|
|
44
|
+
/** `EnvironmentFile=`, repeatable. The ONLY way a secret reaches the container — see file header. */
|
|
45
|
+
readonly environmentFile?: readonly string[];
|
|
46
|
+
/** `Environment=`, repeatable `KEY=VALUE`. NEVER a secret — see container-secrets.ts. */
|
|
47
|
+
readonly environment?: Readonly<Record<string, string>>;
|
|
48
|
+
/** `PublishPort=`, repeatable. Refused together with `network: 'host'` — see `containerProblems`. */
|
|
49
|
+
readonly publishPort?: readonly string[];
|
|
50
|
+
/** `Exec=`. Appended after the image's entrypoint, "the same way as ... a Kubernetes pod" (doc). */
|
|
51
|
+
readonly exec?: string;
|
|
52
|
+
/** `User=`. The numeric UID inside the container (doc: "does not need to match the UID on the host"). */
|
|
53
|
+
readonly user?: string;
|
|
54
|
+
/** `AutoUpdate=`. Podman's own two values (doc): `'registry'` or `'local'`. */
|
|
55
|
+
readonly autoUpdate?: 'registry' | 'local';
|
|
56
|
+
/** `PodmanArgs=`, repeatable. Doc: "not recommended" — the generator does not understand it. */
|
|
57
|
+
readonly podmanArgs?: readonly string[];
|
|
58
|
+
};
|
|
59
|
+
export type ContainerProps = {
|
|
60
|
+
/** The Quadlet file's basename, WITHOUT `.container`. Written as `<name>.container`. */
|
|
61
|
+
name: string;
|
|
62
|
+
/** @default QUADLET_DEFAULT_DIRECTORY. Must be one of QUADLET_SEARCH_DIRECTORIES (below). */
|
|
63
|
+
directory?: string;
|
|
64
|
+
/** `[Unit]` — `Description=`/`Documentation=` typed, everything else verbatim (see file header). */
|
|
65
|
+
unit?: {
|
|
66
|
+
description?: string;
|
|
67
|
+
documentation?: string;
|
|
68
|
+
lines?: readonly UnitLine[];
|
|
69
|
+
};
|
|
70
|
+
/** `[Container]` — Quadlet's own, typed and doc-checked. */
|
|
71
|
+
container: ContainerSection;
|
|
72
|
+
/** `[Service]` — `Restart=`/`RestartSec=` typed, everything else verbatim. */
|
|
73
|
+
service?: {
|
|
74
|
+
restart?: string;
|
|
75
|
+
restartSec?: string;
|
|
76
|
+
lines?: readonly UnitLine[];
|
|
77
|
+
};
|
|
78
|
+
/** `[Install]` — the three keys Quadlet honours for a `.container` file (see file header). */
|
|
79
|
+
install?: {
|
|
80
|
+
wantedBy?: readonly string[];
|
|
81
|
+
requiredBy?: readonly string[];
|
|
82
|
+
alias?: readonly string[];
|
|
83
|
+
};
|
|
84
|
+
/** `systemctl start`/`stop` the GENERATED `<name>.service`. @default true. No `enabled` prop —
|
|
85
|
+
* see container-generator.ts's header for why boot-time enablement is not a systemctl call here. */
|
|
86
|
+
started?: boolean;
|
|
87
|
+
/** Same contract as Systemd.Unit's `restartOn`: digests of things read outside the `.container` file. */
|
|
88
|
+
restartOn?: readonly string[];
|
|
89
|
+
};
|
|
90
|
+
export type ContainerAttributes = {
|
|
91
|
+
name: string;
|
|
92
|
+
/** The GENERATED unit's name: `<name>.service`. Never `.container` — systemctl never sees that. */
|
|
93
|
+
serviceName: string;
|
|
94
|
+
containerPath: string;
|
|
95
|
+
/** SHA-256 of the `.container` file this resource wrote. */
|
|
96
|
+
containerSha256: string;
|
|
97
|
+
configSha256: string;
|
|
98
|
+
loadState: string;
|
|
99
|
+
activeState: string;
|
|
100
|
+
unitFileState?: string;
|
|
101
|
+
/** `systemctl show`'s `SourcePath=` on the generated unit — measured on CT100 2026-09-24 to be
|
|
102
|
+
* the `.container` file's own path; used to prove the generated unit is really ours. */
|
|
103
|
+
sourcePath?: string;
|
|
104
|
+
active: boolean;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Podman's OWN rootful search path, most specific first — `podman-systemd.unit(5)`, "Podman Unit
|
|
108
|
+
* Search Path", Podman 5.4, read 2026-09-24: "Quadlet files for the root user can be placed in the
|
|
109
|
+
* following directories ordered in precedence" (`/run` over `/etc` over `/usr/share`).
|
|
110
|
+
* ★ CT100's `caddy.container` lives in the middle one — measured live, `cat` over `ssh ct100`,
|
|
111
|
+
* 2026-09-24: `/etc/containers/systemd/caddy.container`.
|
|
112
|
+
* ⛔ A directory outside this list is invisible to Quadlet: the generator only reads these three, so
|
|
113
|
+
* a file elsewhere is a `.container` file with no `.service` ever generated from it — silently,
|
|
114
|
+
* unlike `Systemd.Unit`'s directory check, which at least writes a unit systemd could load if only
|
|
115
|
+
* it were enabled. See `containerProblems`.
|
|
116
|
+
*/
|
|
117
|
+
export declare const QUADLET_SEARCH_DIRECTORIES: readonly ['/run/containers/systemd', '/etc/containers/systemd', '/usr/share/containers/systemd'];
|
|
118
|
+
export declare const QUADLET_DEFAULT_DIRECTORY = "/etc/containers/systemd";
|
|
119
|
+
/** ★ A conservative subset of what Quadlet itself accepts as a unit name: no template `@`, no `/`. */
|
|
120
|
+
export declare const NAME: RegExp;
|
|
121
|
+
export declare const containerPathFor: (props: Pick<ContainerProps, 'name' | 'directory'>) => string;
|
|
122
|
+
/** The unit Quadlet generates FROM the `.container` file — systemctl only ever sees this name. */
|
|
123
|
+
export declare const serviceNameFor: (props: Pick<ContainerProps, 'name'>) => string;
|
|
124
|
+
/** The whole `.container` file. ★ Reuses unit-form.ts's `renderUnit` — same `[Section]` INI shape. */
|
|
125
|
+
export declare const renderContainerFile: (props: ContainerProps) => string;
|
|
126
|
+
export declare const refuseContainer: (name: string, message: string) => Error;
|
|
127
|
+
/** Every refusal at once, so one plan shows the whole list — mirrors `unitProblems`'s shape. */
|
|
128
|
+
export declare const containerProblems: (props: ContainerProps) => string[];
|
|
129
|
+
//# sourceMappingURL=container-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-form.d.ts","sourceRoot":"","sources":["../../src/linux/container-form.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quadlet is a systemd GENERATOR (`systemd.generator(7)`, tag `v257`, `man/systemd.generator.xml`,
|
|
3
|
+
* read 2026-09-24), not a unit reader — this is the one fact the whole lifecycle is built around.
|
|
4
|
+
*
|
|
5
|
+
* ★ "Generator output is supposed to last only until the next `daemon-reload`... Units written by
|
|
6
|
+
* generators are removed when the configuration is reloaded" (same doc). `daemon-reload` DELETES
|
|
7
|
+
* `/run/systemd/generator` wholesale and reruns every generator from the `.container` files
|
|
8
|
+
* currently on disk. So a `.container` file the generator refuses produces NO generated unit —
|
|
9
|
+
* even one that generated fine a moment ago, if `daemon-reload` runs again over a now-broken file.
|
|
10
|
+
* That is why `container-lifecycle.ts` restores the LAST-KNOWN-GOOD file and reloads again on a
|
|
11
|
+
* failed update, rather than leaving the bad file in place the way `Systemd.Unit` leaves a bad
|
|
12
|
+
* update for the next deploy to retry (unit-lifecycle.ts): here, "the next deploy" might be a
|
|
13
|
+
* reboot, and by then the container this resource already promised running has no systemd unit
|
|
14
|
+
* at all, not merely a stale one.
|
|
15
|
+
* ⛔ THE GENERATED UNIT CANNOT BE `systemctl enable`D — `podman-systemd.unit(5)`, Podman 5.4, "Podman
|
|
16
|
+
* unit files": "The services created by Podman are considered transient by systemd... it is not
|
|
17
|
+
* possible to 'systemctl enable' them... To compensate... the generator manually applies the
|
|
18
|
+
* [Install] section... during generation, in the same way `systemctl enable` does when run
|
|
19
|
+
* later." MEASURED on CT100 2026-09-24 (`ssh ct100`, read-only): `caddy.container` declares
|
|
20
|
+
* `[Install] WantedBy=multi-user.target` and nothing ever ran `systemctl enable caddy`; yet
|
|
21
|
+
* `systemctl is-enabled caddy.service` prints `generated`, `UnitFileState=generated`, and
|
|
22
|
+
* `/run/systemd/generator/multi-user.target.wants/caddy.service` is a symlink the GENERATOR put
|
|
23
|
+
* there. So `Podman.Container` has no `enabled` prop and never calls `enableUnit`/`disableUnit`
|
|
24
|
+
* (systemctl.ts) — declaring `install.wantedBy` IS the enablement, applied by the generator on
|
|
25
|
+
* every `daemon-reload`, exactly the way `Systemd.Unit`'s enable/disable calls are applied by
|
|
26
|
+
* this family's own `settle` step. That is the one place this family's "enable logic" differs.
|
|
27
|
+
* ★ SourcePath TIES THE GENERATED UNIT BACK TO OUR FILE. MEASURED on CT100 2026-09-24:
|
|
28
|
+
* `systemctl show caddy.service -p SourcePath` -> `/etc/containers/systemd/caddy.container`.
|
|
29
|
+
* `verifyGenerated` below uses it the way `unit-preflight.ts`'s `assertUnclaimed` uses a file
|
|
30
|
+
* digest: a generated unit whose SourcePath is NOT our path belongs to a same-named `.container`
|
|
31
|
+
* file earlier in `QUADLET_SEARCH_DIRECTORIES` (container-form.ts) — a takeover, not a success.
|
|
32
|
+
* ⚠️ REASONED, NOT MEASURED — THE ONE GAP THIS VERIFICATION HAS: whether an ALREADY-ACTIVE unit's
|
|
33
|
+
* `LoadState` stays `loaded` (systemd does not garbage-collect a referenced/active unit object
|
|
34
|
+
* merely because a reload failed to reproduce it) even when the UPDATED `.container` file the
|
|
35
|
+
* generator just refused would, on its own, generate nothing. Measuring this needs writing a
|
|
36
|
+
* broken file to a live host, which read-only access forbids. If that is how a live host
|
|
37
|
+
* behaves, `LoadState`/`SourcePath` alone would not catch a failed update to a container that
|
|
38
|
+
* was already running — only a CREATE, or an update to an already-INACTIVE one, is verified with
|
|
39
|
+
* full confidence by this check alone. Restoring the last-known-good file on ANY verification
|
|
40
|
+
* failure (container-lifecycle.ts) does not depend on this gap: it always leaves the host at the
|
|
41
|
+
* file that is known to generate, closing the risk window even where detection is uncertain.
|
|
42
|
+
* ⛔ `needsVerificationReload` (below) is what stops a NEW resource from getting stuck — found on
|
|
43
|
+
* adversarial review. `container-lifecycle.ts`'s `needsReload` used to be `wrote || stale ||
|
|
44
|
+
* preStatus.needDaemonReload`; for an INTERRUPTED CREATE (the write landed, the apply crashed
|
|
45
|
+
* before `daemon-reload` ran) the retry sees `wrote: false` (the file already holds `desired`)
|
|
46
|
+
* and `stale: false` (there is no PRIOR state to disagree with — this is a create), so nothing
|
|
47
|
+
* ever reloaded and `verifyGenerated` threw on the still-missing generated unit, forever: loud,
|
|
48
|
+
* but stuck, since nothing about a plain retry ever changed. The same holds for adopting a file
|
|
49
|
+
* whose generated unit is missing or does not match. `needsVerificationReload` shares
|
|
50
|
+
* `generatedMatches` with `verifyGenerated` itself, so the reload gate and the verification it
|
|
51
|
+
* is gating can never disagree about what "already proven" means.
|
|
52
|
+
*/
|
|
53
|
+
import type { UnitStatus } from './systemctl.ts';
|
|
54
|
+
/** MEASURED on CT100 2026-09-24: `systemctl show caddy.service -p FragmentPath`. */
|
|
55
|
+
export declare const GENERATED_UNIT_DIRECTORY = "/run/systemd/generator";
|
|
56
|
+
export declare class QuadletGeneratorError extends Error {
|
|
57
|
+
constructor(name: string, detail: string);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether `container-lifecycle.ts` must `daemon-reload` before trusting `status` as proof of
|
|
61
|
+
* anything — true whenever `status` does not already show a matching generation. Called on the
|
|
62
|
+
* PRE-write status, so it costs nothing when `wrote`/`stale`/`NeedDaemonReload` already force a
|
|
63
|
+
* reload; it earns its keep exactly when none of them do but nothing has actually been proven yet
|
|
64
|
+
* (a fresh create or adopt with no prior state to compare against).
|
|
65
|
+
*/
|
|
66
|
+
export declare const needsVerificationReload: (containerPath: string, status: UnitStatus) => boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Throws a typed, loud `QuadletGeneratorError` — never lets a generator failure read as "the
|
|
69
|
+
* resource is simply absent", which is what a plain `LoadState=not-found` would otherwise look
|
|
70
|
+
* like to a caller that only knows the `Systemd.Unit` shape (a unit that has genuinely never been
|
|
71
|
+
* declared also reads `not-found`).
|
|
72
|
+
*/
|
|
73
|
+
export declare const verifyGenerated: (name: string, containerPath: string, serviceName: string, status: UnitStatus) => void;
|
|
74
|
+
//# sourceMappingURL=container-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-generator.d.ts","sourceRoot":"","sources":["../../src/linux/container-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,2BAA2B,CAAC;AAEjE,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAGvC;CACF;AAUD;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,kBAAmB,MAAM,UAAU,UAAU,KAAG,OAC1C,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SACpB,MAAM,iBACG,MAAM,eACR,MAAM,UACX,UAAU,KACjB,IAmBF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Diff } from 'alchemy/Diff';
|
|
2
|
+
import type { Input } from 'alchemy/Input';
|
|
3
|
+
import * as Effect from 'effect/Effect';
|
|
4
|
+
import type { HostRunner } from '../launchd/runner.ts';
|
|
5
|
+
import type { ContainerAttributes, ContainerProps } from './container-form.ts';
|
|
6
|
+
export declare const readHandler: (runner: HostRunner, olds: ContainerProps, output: ContainerAttributes | undefined) => Effect.Effect<ContainerAttributes | undefined, Error>;
|
|
7
|
+
export declare const diffHandler: (runner: HostRunner, instanceId: string, news: Input<ContainerProps>, output: ContainerAttributes | undefined) => Effect.Effect<Diff | undefined, Error>;
|
|
8
|
+
export declare const reconcileHandler: (runner: HostRunner, args: {
|
|
9
|
+
fqn: string;
|
|
10
|
+
instanceId: string;
|
|
11
|
+
news: ContainerProps;
|
|
12
|
+
output: ContainerAttributes | undefined;
|
|
13
|
+
}) => Effect.Effect<ContainerAttributes, Error>;
|
|
14
|
+
export declare const deleteHandler: (runner: HostRunner, output: ContainerAttributes) => Effect.Effect<void, Error>;
|
|
15
|
+
//# sourceMappingURL=container-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-handlers.d.ts","sourceRoot":"","sources":["../../src/linux/container-handlers.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAS/E,eAAO,MAAM,WAAW,WACd,UAAU,QACZ,cAAc,UACZ,mBAAmB,GAAG,SAAS,KACtC,MAAM,CAAC,MAAM,CAAC,mBAAmB,GAAG,SAAS,EAAE,KAAK,CAKnD,CAAC;AAEL,eAAO,MAAM,WAAW,WACd,UAAU,cACN,MAAM,QACZ,KAAK,CAAC,cAAc,CAAC,UACnB,mBAAmB,GAAG,SAAS,KACtC,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,SAAS,EAAE,KAAK,CAgBvC,CAAC;AAEF,eAAO,MAAM,gBAAgB,WACnB,UAAU,QACZ;IACJ,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC;CACzC,KACA,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,KAAK,CAGxC,CAAC;AAEJ,eAAO,MAAM,aAAa,WAChB,UAAU,UACV,mBAAmB,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAgD,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Podman.Container`'s read / diff / reconcile / delete as plain async functions over a
|
|
3
|
+
* HostRunner — same split as `unit-lifecycle.ts`, so the whole lifecycle runs against a fake host
|
|
4
|
+
* (fake-quadlet.ts) with no real systemd or Podman.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ WHAT A DEPLOY DOES NOT DO — same rule `Systemd.Unit` was built around (unit-lifecycle.ts
|
|
7
|
+
* header): it NEVER mass-restarts. A container is restarted only when THIS resource's rendered
|
|
8
|
+
* `.container` file changed, or a digest the declaration listed in `restartOn` changed.
|
|
9
|
+
* ★ THE ORDER IS WRITE, RELOAD, VERIFY THE GENERATOR, THEN START OR RESTART. `daemon-reload` is
|
|
10
|
+
* where Quadlet turns the `.container` file into `<name>.service` (container-generator.ts) —
|
|
11
|
+
* writing it changes nothing on its own, and a write that fails leaves the old file (and its
|
|
12
|
+
* already-generated unit) exactly as they were.
|
|
13
|
+
* ⛔ A GENERATOR FAILURE IS ROLLED BACK, NOT LEFT FOR THE NEXT DEPLOY. `container-generator.ts`'s
|
|
14
|
+
* header explains why: `daemon-reload` deletes ALL generator output before regenerating, so a
|
|
15
|
+
* `.container` file the generator refuses has NO generated unit at all, even if the file it
|
|
16
|
+
* replaced generated one fine. An UPDATE that fails verification restores the previous file and
|
|
17
|
+
* reloads again, so the container this resource already promised running never loses its unit; a
|
|
18
|
+
* CREATE that fails removes the file it wrote, same as `Systemd.Unit`'s undo.
|
|
19
|
+
* ⛔ NO ENABLE/DISABLE — see container-generator.ts and container-settle.ts headers.
|
|
20
|
+
*/
|
|
21
|
+
import type { Diff } from 'alchemy/Diff';
|
|
22
|
+
import type { HostRunner } from '../launchd/runner.ts';
|
|
23
|
+
import { type ContainerAttributes, type ContainerProps } from './container-form.ts';
|
|
24
|
+
/** What is on the host now: the `.container` file's digest and the GENERATED unit's status. */
|
|
25
|
+
export declare const readContainer: (runner: HostRunner, props: ContainerProps) => Promise<ContainerAttributes | undefined>;
|
|
26
|
+
export declare const diffContainer: (runner: HostRunner, news: ContainerProps, output: ContainerAttributes) => Promise<Diff>;
|
|
27
|
+
/**
|
|
28
|
+
* `adopt` is whether adoption is on AND this apply is a create or an unfinished generation of our
|
|
29
|
+
* own — same meaning as `Systemd.Unit`'s (unit-lifecycle.ts reconcileUnit).
|
|
30
|
+
*/
|
|
31
|
+
export declare const reconcileContainer: (runner: HostRunner, props: ContainerProps, output: ContainerAttributes | undefined, adopt?: boolean) => Promise<ContainerAttributes>;
|
|
32
|
+
/**
|
|
33
|
+
* Stop, remove the `.container` file, reload — clearing the orphaned generated unit. Idempotent.
|
|
34
|
+
* No `disableUnit`: never enabled through systemctl in the first place (container-settle.ts).
|
|
35
|
+
*/
|
|
36
|
+
export declare const deleteContainer: (runner: HostRunner, output: ContainerAttributes) => Promise<void>;
|
|
37
|
+
//# sourceMappingURL=container-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-lifecycle.d.ts","sourceRoot":"","sources":["../../src/linux/container-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIvD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAOpB,MAAM,qBAAqB,CAAC;AAY7B,+FAA+F;AAC/F,eAAO,MAAM,aAAa,WAChB,UAAU,SACX,cAAc,KACpB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAczC,CAAC;AAEF,eAAO,MAAM,aAAa,WAChB,UAAU,QACZ,cAAc,UACZ,mBAAmB,KAC1B,OAAO,CAAC,IAAI,CAwBd,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,WACrB,UAAU,SACX,cAAc,UACb,mBAAmB,GAAG,SAAS,sBAEtC,OAAO,CAAC,mBAAmB,CAyF7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,WAClB,UAAU,UACV,mBAAmB,KAC1B,OAAO,CAAC,IAAI,CAad,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything `Podman.Container` checks before it touches the host — split from
|
|
3
|
+
* container-lifecycle.ts so a reconcile and a plan-time REPLACE run the very same checks, exactly
|
|
4
|
+
* the reason unit-preflight.ts exists (see that file's header; this mirrors it).
|
|
5
|
+
*
|
|
6
|
+
* ★ EVERY CHECK HERE IS READ-ONLY: validation, `systemctl show`, a file read and
|
|
7
|
+
* `HostRunner.checkWrite`. Nothing here writes, reloads or drives Podman.
|
|
8
|
+
*/
|
|
9
|
+
import { type HostRunner } from '../launchd/runner.ts';
|
|
10
|
+
import { type UnitStatus } from './systemctl.ts';
|
|
11
|
+
import { type ContainerAttributes, type ContainerProps } from './container-form.ts';
|
|
12
|
+
/** Throw every refusal at once, so one plan shows the whole list. */
|
|
13
|
+
export declare const assertValid: (props: ContainerProps) => void;
|
|
14
|
+
/** ⛔ Writing the file and driving systemctl/Podman are root's — refuse up front, not mid-apply. */
|
|
15
|
+
export declare const assertMayWrite: (runner: HostRunner, name: string) => void;
|
|
16
|
+
/**
|
|
17
|
+
* ⛔ A MASKED GENERATED UNIT IS SOMEONE'S DECISION, NOT DRIFT — same rule as `Systemd.Unit`
|
|
18
|
+
* (unit-preflight.ts `assertUsable`). Masking a generated unit is done the ordinary way, by
|
|
19
|
+
* symlinking its name to `/dev/null` in a directory Quadlet's generator output does not win
|
|
20
|
+
* against (`/etc/systemd/system` outranks `/run/systemd/generator`); unmasking here would
|
|
21
|
+
* silently overrule that.
|
|
22
|
+
*/
|
|
23
|
+
export declare const assertUsable: (props: Pick<ContainerProps, 'name'>, status: UnitStatus) => void;
|
|
24
|
+
/**
|
|
25
|
+
* ⛔ A `.container` FILE THIS RESOURCE DOES NOT OWN IS NEVER OVERWRITTEN — same rule and same
|
|
26
|
+
* exemption as `unit-preflight.ts`'s `assertUnclaimed`: a file byte-identical to our render is
|
|
27
|
+
* this declaration's own leftover, not someone else's.
|
|
28
|
+
*/
|
|
29
|
+
export declare const assertUnclaimed: (runner: HostRunner, props: ContainerProps) => Promise<void>;
|
|
30
|
+
/** The half of a rename check that needs only the NEW NAME — mirrors `assertRenameTarget`. */
|
|
31
|
+
export declare const assertRenameTarget: (runner: HostRunner, old: ContainerAttributes, name: string) => Promise<void>;
|
|
32
|
+
/** A rename, checked at plan time, before the plan may promise a delete-first replace. */
|
|
33
|
+
export declare const assertReplaceable: (runner: HostRunner, next: ContainerProps, old: ContainerAttributes) => Promise<void>;
|
|
34
|
+
//# sourceMappingURL=container-preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-preflight.d.ts","sourceRoot":"","sources":["../../src/linux/container-preflight.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,UAAU,EAAgB,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAMpB,MAAM,qBAAqB,CAAC;AAI7B,qEAAqE;AACrE,eAAO,MAAM,WAAW,UAAW,cAAc,KAAG,IAGnD,CAAC;AAEF,mGAAmG;AACnG,eAAO,MAAM,cAAc,WAAY,UAAU,QAAQ,MAAM,KAAG,IAQjE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,UAAW,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,UAAU,UAAU,KAAG,IAQtF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,WAAkB,UAAU,SAAS,cAAc,KAAG,OAAO,CAAC,IAAI,CAW7F,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,WACrB,UAAU,OACb,mBAAmB,QAClB,MAAM,KACX,OAAO,CAAC,IAAI,CAId,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,iBAAiB,WACpB,UAAU,QACZ,cAAc,OACf,mBAAmB,KACvB,OAAO,CAAC,IAAI,CAKd,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A best-effort refusal of an inline secret ANYWHERE in a rendered `.container` file — checked
|
|
3
|
+
* from `containerProblems` (container-form.ts), so it runs at plan AND apply time, before anything
|
|
4
|
+
* is written.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ THIS IS NOT A SECRET SCANNER. It cannot prove a value is safe, only refuse the OBVIOUS cases: a
|
|
7
|
+
* key whose name says what it holds, or a value shaped like a real vendor token. The actual
|
|
8
|
+
* contract is `EnvironmentFile=` (container-form.ts's header): a secret belongs in a host file an
|
|
9
|
+
* out-of-band renderer (openbao-agent) maintains, never in a prop, because the WHOLE file is
|
|
10
|
+
* rendered into a 0644 file (`unit-form.ts` `UNIT_WRITE`) AND persisted unencrypted in Alchemy
|
|
11
|
+
* state — the same two reasons `Systemd.Unit`'s `content` is never secret. A determined caller can
|
|
12
|
+
* still name a variable `CFG` and put a token in it; this check is a guardrail against the common
|
|
13
|
+
* mistake, not a boundary a hostile caller cannot cross.
|
|
14
|
+
* ★ KEY-NAME FIRST, VALUE SECOND. The name check catches the overwhelming majority of real
|
|
15
|
+
* mistakes (copying a `docker run -e SECRET=...` line verbatim) and has no false-negative risk
|
|
16
|
+
* from an unfamiliar token format; the value check is defense in depth for a handful of
|
|
17
|
+
* widely-recognised vendor prefixes, not an attempt at general entropy detection — an entropy
|
|
18
|
+
* check has enough false positives on ordinary config (long image digests, base64 Caddyfile
|
|
19
|
+
* snippets) that it would train callers to ignore the refusal.
|
|
20
|
+
* ⛔ CHECKED EVERYWHERE, NOT ONLY `container.environment` — found on adversarial review.
|
|
21
|
+
* `container.environment` is the field most likely to carry one BY ACCIDENT, and gets the
|
|
22
|
+
* precise per-key `secretLikeEnvironment` check below with the best error message. But every
|
|
23
|
+
* other free-text field renders into the SAME 0644 file and the SAME state row: `exec`,
|
|
24
|
+
* `podmanArgs` (each entry rendered verbatim), and the escape-hatch `unit.lines`/`service.lines`
|
|
25
|
+
* — a `service.lines` entry can spell a native systemd `Environment=` directly, which never goes
|
|
26
|
+
* through the typed `container.environment` map at all. `secretLikeLines` below runs the same two
|
|
27
|
+
* heuristics — value-shape, and an embedded `KEY=VALUE` whose KEY looks like a secret — over every
|
|
28
|
+
* OTHER rendered line, and `container-form.ts`'s `containerProblems` calls both, so nothing
|
|
29
|
+
* rendered escapes either check.
|
|
30
|
+
*/
|
|
31
|
+
export declare const secretLikeEnvironment: (environment: Readonly<Record<string, string>>) => string[];
|
|
32
|
+
/** The same two heuristics as `secretLikeEnvironment`, over any OTHER rendered `[key, value]` line
|
|
33
|
+
* (`Exec=`, `PodmanArgs=`, and the `unit.lines`/`service.lines` escape hatches) — see file header. */
|
|
34
|
+
export declare const secretLikeLines: (lines: readonly (readonly [key: string, value: string])[]) => string[];
|
|
35
|
+
/** `PodmanArgs=` entries that try to set a container env var outright — see `ENV_FLAG`'s comment. */
|
|
36
|
+
export declare const podmanArgsProblems: (args: readonly string[]) => string[];
|
|
37
|
+
//# sourceMappingURL=container-secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-secrets.d.ts","sourceRoot":"","sources":["../../src/linux/container-secrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA6BH,eAAO,MAAM,qBAAqB,gBAAiB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAG,MAAM,EAkB3F,CAAC;AAEF;uGACuG;AACvG,eAAO,MAAM,eAAe,UACnB,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,KACxD,MAAM,EAkBR,CAAC;AAEF,qGAAqG;AACrG,eAAO,MAAM,kBAAkB,SAAU,SAAS,MAAM,EAAE,KAAG,MAAM,EAQ9D,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step that decides whether the GENERATED `<name>.service` actually moves: running state and —
|
|
3
|
+
* only for a real change — a restart. Split out for the same reason unit-settle.ts is split from
|
|
4
|
+
* unit-lifecycle.ts: it is the restart contract, and it should be readable on its own.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ THE RESTART CONDITION IS THE WHOLE CONTRACT, same as `Systemd.Unit`'s (unit-settle.ts):
|
|
7
|
+
* `changed` comes from the caller (container-lifecycle.ts `reconcileContainer`), `configChanged`
|
|
8
|
+
* is a digest the DECLARATION itself listed. Nothing else restarts a container.
|
|
9
|
+
* ★ NO ENABLE/DISABLE HERE — the one place this file is SHORTER than unit-settle.ts, not longer.
|
|
10
|
+
* Quadlet's generator applies `[Install]` itself on every `daemon-reload` (container-generator.ts
|
|
11
|
+
* header); calling `systemctl enable`/`disable` on a generated unit does not work at all (same
|
|
12
|
+
* header, doc-cited). So there is no enablement branch to settle — only started/stopped and the
|
|
13
|
+
* restart-on-change rule.
|
|
14
|
+
*/
|
|
15
|
+
import type { HostRunner } from '../launchd/runner.ts';
|
|
16
|
+
import { type UnitStatus } from './systemctl.ts';
|
|
17
|
+
import { type ContainerAttributes, type ContainerProps } from './container-form.ts';
|
|
18
|
+
export declare const attributesOf: (props: ContainerProps, configSha256: string, containerSha256: string, containerPath: string, serviceName: string, status: UnitStatus) => ContainerAttributes;
|
|
19
|
+
export type Settle = {
|
|
20
|
+
readonly before: Uint8Array | undefined;
|
|
21
|
+
/** The `.container` file or systemd's view of it changed in a way that earns a restart. */
|
|
22
|
+
readonly changed: boolean;
|
|
23
|
+
readonly configChanged: boolean;
|
|
24
|
+
readonly desired: string;
|
|
25
|
+
readonly path: string;
|
|
26
|
+
readonly serviceName: string;
|
|
27
|
+
readonly status: UnitStatus;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Bring running state to the declaration, restarting ONLY for a real change. No enable/disable —
|
|
31
|
+
* see file header.
|
|
32
|
+
*/
|
|
33
|
+
export declare const settle: (runner: HostRunner, props: ContainerProps, configSha256: string, step: Settle) => Promise<ContainerAttributes>;
|
|
34
|
+
//# sourceMappingURL=container-settle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container-settle.d.ts","sourceRoot":"","sources":["../../src/linux/container-settle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EACL,KAAK,UAAU,EAMhB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EAEpB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,YAAY,UAChB,cAAc,gBACP,MAAM,mBACH,MAAM,iBACR,MAAM,eACR,MAAM,UACX,UAAU,KACjB,mBAWD,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,WACT,UAAU,SACX,cAAc,gBACP,MAAM,QACd,MAAM,KACX,OAAO,CAAC,mBAAmB,CA0B7B,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Podman.Container` — one Quadlet `.container` file, declared, plus the systemd unit Podman's own
|
|
3
|
+
* generator turns it into.
|
|
4
|
+
*
|
|
5
|
+
* - create / update — render, write the `.container` file atomically, `daemon-reload`, VERIFY the
|
|
6
|
+
* generator actually produced `<name>.service` (container-generator.ts), then start or RESTART
|
|
7
|
+
* the generated unit only when something changed (container-lifecycle.ts, container-settle.ts).
|
|
8
|
+
* - read — the `.container` file's SHA-256 plus `systemctl show <name>.service`.
|
|
9
|
+
* - diff — the rendered digest against stored and on-disk, running state against the declaration,
|
|
10
|
+
* and systemd's own `NeedDaemonReload`.
|
|
11
|
+
* - replace — only when the file's name or directory changes, delete-first.
|
|
12
|
+
* - delete — stop the generated unit, remove the `.container` file, reload.
|
|
13
|
+
*
|
|
14
|
+
* ⛔ A DEPLOY NEVER MASS-RESTARTS — see container-lifecycle.ts's header.
|
|
15
|
+
* ⛔ THE `.container` FILE IS NEVER A SECRET, and `[Container] Environment=` is refused if it looks
|
|
16
|
+
* like one — see container-form.ts and container-secrets.ts.
|
|
17
|
+
* ⛔ THE GENERATED UNIT IS NEVER `systemctl enable`D — see container-generator.ts's header for why,
|
|
18
|
+
* measured and doc-cited, and what stands in for it (`install.wantedBy`).
|
|
19
|
+
* ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt` — same contract as every family here.
|
|
20
|
+
*/
|
|
21
|
+
import { Resource } from 'alchemy';
|
|
22
|
+
import * as Provider from 'alchemy/Provider';
|
|
23
|
+
import { HostRunnerService } from '../launchd/runner.ts';
|
|
24
|
+
import type { ContainerAttributes, ContainerProps } from './container-form.ts';
|
|
25
|
+
export type { ContainerAttributes as PodmanContainerAttributes, ContainerProps as PodmanContainerProps, ContainerSection, UnitLine as PodmanUnitLine, } from './container-form.ts';
|
|
26
|
+
export interface PodmanContainer extends Resource<'Podman.Container', ContainerProps, ContainerAttributes> {
|
|
27
|
+
}
|
|
28
|
+
export declare const PodmanContainer: import("alchemy").ResourceClass<PodmanContainer>;
|
|
29
|
+
export declare const PodmanContainerProvider: () => import("effect/Layer").Layer<Provider.Provider<PodmanContainer>, never, HostRunnerService>;
|
|
30
|
+
//# sourceMappingURL=container.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/linux/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG/E,YAAY,EACV,mBAAmB,IAAI,yBAAyB,EAChD,cAAc,IAAI,oBAAoB,EACtC,gBAAgB,EAChB,QAAQ,IAAI,cAAc,GAC3B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,cAAc,EACd,mBAAmB,CACpB;CAAG;AAEJ,eAAO,MAAM,eAAe,kDAAgD,CAAC;AAE7E,eAAO,MAAM,uBAAuB,kGAiBjC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type FakeLinuxOptions } from './fake-linux-host.ts';
|
|
2
|
+
import type { HostRunner } from '../launchd/runner.ts';
|
|
3
|
+
/** A line no real Quadlet key ever spells, so a test can force the (fake) generator to refuse a file. */
|
|
4
|
+
export declare const FAIL_GENERATOR_MARKER = "X-Test-Fail-Generator=1";
|
|
5
|
+
export declare const fakeQuadletHost: (options?: FakeLinuxOptions) => {
|
|
6
|
+
calls: string[][];
|
|
7
|
+
dirs: Map<string, number>;
|
|
8
|
+
expectUnit: (path: string) => void;
|
|
9
|
+
files: Map<string, {
|
|
10
|
+
bytes: Uint8Array;
|
|
11
|
+
mode: number;
|
|
12
|
+
uid: number;
|
|
13
|
+
gid: number;
|
|
14
|
+
kind: import("./index.ts").FileStat['kind'];
|
|
15
|
+
}>;
|
|
16
|
+
modes: Map<string, {
|
|
17
|
+
mode: number;
|
|
18
|
+
uid: number;
|
|
19
|
+
gid: number;
|
|
20
|
+
}>;
|
|
21
|
+
placeUnit: (path: string, text: string, state?: Partial<import("./fake-systemd.ts").UnitState>) => void;
|
|
22
|
+
units: Map<string, import("./fake-systemd.ts").UnitState>;
|
|
23
|
+
runner: HostRunner;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=fake-quadlet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake-quadlet.d.ts","sourceRoot":"","sources":["../../src/linux/fake-quadlet.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,gBAAgB,EAAiB,MAAM,sBAAsB,CAAC;AAG5E,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEnE,yGAAyG;AACzG,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAO/D,eAAO,MAAM,eAAe,aAAa,gBAAgB;;;;;;;;;;;;;;;;;;;CAiGxD,CAAC"}
|