@homeflare/alchemy 0.10.1 → 0.12.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 +3 -3
- package/dist/caddy/admin-guard.d.ts +1 -1
- package/dist/caddy/index.js.map +4 -4
- package/dist/cloudflare/client.d.ts +1 -1
- package/dist/cloudflare/index.js.map +2 -2
- package/dist/cloudflare/lock-rules.d.ts +1 -1
- package/dist/forgejo/branch-protection-form.d.ts +1 -1
- package/dist/forgejo/branch-protection.d.ts +1 -1
- package/dist/forgejo/index.js.map +10 -10
- package/dist/forgejo/org-actions-secrets.d.ts +1 -1
- package/dist/forgejo/org-label.d.ts +1 -1
- package/dist/forgejo/org-team.d.ts +1 -1
- package/dist/forgejo/repo-webhook.d.ts +1 -1
- package/dist/forgejo/repository.d.ts +2 -2
- package/dist/forgejo/repository.d.ts.map +1 -1
- package/dist/forgejo/resource.d.ts +2 -2
- package/dist/forgejo/team-member.d.ts +1 -1
- package/dist/launchd/index.js.map +2 -2
- package/dist/openbao/auth-role.d.ts +1 -1
- package/dist/openbao/bao-http.d.ts +4 -4
- package/dist/openbao/cloudflare-permission-groups.d.ts +1 -1
- package/dist/openbao/cloudflare-role-form.d.ts +2 -2
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -1
- package/dist/openbao/cloudflare-roles-expand.d.ts +1 -1
- package/dist/openbao/digest.d.ts +1 -1
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +2 -2
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -1
- package/dist/openbao/forgejo-bootstrap.d.ts +1 -1
- package/dist/openbao/index.js +10 -8
- package/dist/openbao/index.js.map +18 -18
- package/dist/openbao/mount.d.ts +1 -1
- package/dist/openbao/pki-role.d.ts +1 -1
- package/dist/openbao/policy.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
- package/dist/openbao/proxmox-role.d.ts +10 -10
- package/dist/openbao/ssh-role.d.ts +3 -3
- package/dist/ownership/adopting.d.ts +34 -0
- package/dist/ownership/adopting.d.ts.map +1 -0
- package/dist/ownership/probe.d.ts +15 -5
- package/dist/ownership/probe.d.ts.map +1 -1
- package/dist/ownership/rows.d.ts +12 -0
- package/dist/ownership/rows.d.ts.map +1 -1
- package/dist/ownership/store-fixture.d.ts +10 -0
- package/dist/ownership/store-fixture.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts +2 -2
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/api-token-form.d.ts +2 -2
- package/dist/proxmox/api-token.d.ts +11 -11
- package/dist/proxmox/ceph-daemon-form.d.ts +3 -3
- package/dist/proxmox/ceph-daemon.d.ts +19 -19
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts +5 -5
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -5
- package/dist/proxmox/ceph-fs.d.ts +16 -16
- package/dist/proxmox/ceph-osd-tree.d.ts +16 -16
- package/dist/proxmox/ceph-osd.d.ts +5 -5
- package/dist/proxmox/ceph-pool-form.d.ts +13 -13
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts +3 -3
- package/dist/proxmox/client.d.ts +2 -2
- package/dist/proxmox/credentials.d.ts +8 -8
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/fake-pve-lxc.d.ts +8 -0
- package/dist/proxmox/fake-pve-lxc.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-form.d.ts +1 -1
- package/dist/proxmox/firewall-alias.d.ts +1 -1
- package/dist/proxmox/generated/pbs.d.ts +1 -1
- package/dist/proxmox/generated/pve.d.ts +1 -1
- package/dist/proxmox/group.d.ts +8 -8
- package/dist/proxmox/ha-resource.d.ts +3 -2
- package/dist/proxmox/ha-resource.d.ts.map +1 -1
- package/dist/proxmox/ha-rule-form.d.ts +5 -5
- package/dist/proxmox/ha-rule.d.ts +3 -3
- package/dist/proxmox/index.d.ts +6 -0
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +465 -217
- package/dist/proxmox/index.js.map +63 -58
- package/dist/proxmox/lease-cache.d.ts +4 -4
- package/dist/proxmox/lxc-adoption.d.ts +32 -0
- package/dist/proxmox/lxc-adoption.d.ts.map +1 -0
- package/dist/proxmox/lxc-harness.d.ts +12 -0
- package/dist/proxmox/lxc-harness.d.ts.map +1 -1
- package/dist/proxmox/lxc-props.d.ts +2 -2
- package/dist/proxmox/lxc.d.ts +8 -8
- package/dist/proxmox/lxc.d.ts.map +1 -1
- package/dist/proxmox/members.d.ts +3 -3
- package/dist/proxmox/network-apply-read.d.ts +5 -5
- package/dist/proxmox/network-apply.d.ts +9 -9
- package/dist/proxmox/node-network-form.d.ts +6 -6
- package/dist/proxmox/node-network.d.ts +17 -17
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-target.d.ts +3 -2
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +1 -1
- package/dist/proxmox/pbs-datastore.d.ts +10 -10
- package/dist/proxmox/pbs-prune-job.d.ts +2 -2
- package/dist/proxmox/pbs-sync-job.d.ts +4 -4
- package/dist/proxmox/pbs-verify-job.d.ts +1 -1
- package/dist/proxmox/pool.d.ts +4 -4
- package/dist/proxmox/provision-baseline.d.ts +74 -0
- package/dist/proxmox/provision-baseline.d.ts.map +1 -0
- package/dist/proxmox/provision-bootstrap.d.ts +33 -0
- package/dist/proxmox/provision-bootstrap.d.ts.map +1 -0
- package/dist/proxmox/provision-cli-fake.d.ts +38 -0
- package/dist/proxmox/provision-cli-fake.d.ts.map +1 -0
- package/dist/proxmox/provision-declare.d.ts +25 -0
- package/dist/proxmox/provision-declare.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +9 -8
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +2 -2
- package/dist/proxmox/resource.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts +7 -7
- package/dist/proxmox/sdn-apply.d.ts +1 -1
- package/dist/proxmox/sdn-subnet-form.d.ts +1 -1
- package/dist/proxmox/sdn-subnet.d.ts +5 -5
- package/dist/proxmox/sdn-vnet.d.ts +5 -6
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts +4 -3
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +3 -2
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/values.d.ts +1 -1
- package/dist/proxmox/zfs-pool-write.d.ts +1 -1
- package/dist/proxmox/zfs-pool.d.ts +5 -5
- package/dist/talos/credentials.d.ts +1 -1
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/index.js.map +1 -1
- package/docs/adopt-verify.md +21 -5
- package/docs/adopted-deploys.md +5 -3
- package/docs/ownership.md +27 -2
- package/docs/provision-baseline.md +106 -0
- package/docs/proxmox-lxc-adopt.md +83 -0
- package/docs/proxmox-lxc.md +12 -49
- package/docs/proxmox.md +8 -0
- package/package.json +1 -1
- package/src/caddy/admin-guard.ts +1 -1
- package/src/caddy/local-admin.ts +1 -1
- package/src/cloudflare/client.ts +1 -1
- package/src/cloudflare/lock-rules.ts +1 -1
- package/src/forgejo/branch-protection-form.ts +1 -1
- package/src/forgejo/branch-protection.ts +1 -1
- package/src/forgejo/client.ts +1 -1
- package/src/forgejo/org-actions-secrets.ts +1 -1
- package/src/forgejo/org-label.ts +1 -1
- package/src/forgejo/org-team.ts +1 -1
- package/src/forgejo/repo-webhook.ts +1 -1
- package/src/forgejo/repository.ts +2 -2
- package/src/forgejo/resource.ts +2 -2
- package/src/forgejo/team-member.ts +1 -1
- package/src/openbao/README.md +4 -4
- package/src/openbao/auth-role.ts +1 -1
- package/src/openbao/bao-http.test.ts +2 -2
- package/src/openbao/bao-http.ts +6 -6
- package/src/openbao/bao-status.test.ts +1 -1
- package/src/openbao/bao-status.ts +1 -1
- package/src/openbao/cloudflare-group-scope.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.ts +3 -3
- package/src/openbao/cloudflare-policy.ts +1 -1
- package/src/openbao/cloudflare-role-form.ts +2 -2
- package/src/openbao/cloudflare-roles-expand.ts +1 -1
- package/src/openbao/digest.ts +1 -1
- package/src/openbao/forgejo-bootstrap-parse.test.ts +3 -3
- package/src/openbao/forgejo-bootstrap-parse.ts +2 -2
- package/src/openbao/forgejo-bootstrap.ts +4 -4
- package/src/openbao/kubernetes-role-form.test.ts +5 -5
- package/src/openbao/mount.ts +1 -1
- package/src/openbao/pki-role.ts +1 -1
- package/src/openbao/policy-wire.test.ts +3 -3
- package/src/openbao/policy.ts +2 -2
- package/src/openbao/proxmox-role-form.ts +5 -5
- package/src/openbao/proxmox-role.ts +11 -11
- package/src/openbao/ssh-role.ts +3 -3
- package/src/ownership/adopting.test.ts +63 -0
- package/src/ownership/adopting.ts +46 -0
- package/src/ownership/ownership.test.ts +1 -34
- package/src/ownership/probe.ts +27 -14
- package/src/ownership/rows.ts +8 -2
- package/src/ownership/store-fixture.ts +50 -0
- package/src/proxmox/acl.ts +4 -4
- package/src/proxmox/adopt-noop.test.ts +6 -6
- package/src/proxmox/api-token-form.ts +4 -4
- package/src/proxmox/api-token.ts +13 -13
- package/src/proxmox/ceph-daemon-form.ts +3 -3
- package/src/proxmox/ceph-daemon.ts +20 -20
- package/src/proxmox/ceph-flag.ts +7 -7
- package/src/proxmox/ceph-fs-wire.ts +7 -7
- package/src/proxmox/ceph-fs.ts +18 -18
- package/src/proxmox/ceph-osd-tree.ts +16 -16
- package/src/proxmox/ceph-osd.ts +6 -6
- package/src/proxmox/ceph-pool-adopt.test.ts +14 -14
- package/src/proxmox/ceph-pool-form.ts +13 -13
- package/src/proxmox/ceph-pool.ts +6 -6
- package/src/proxmox/client.test.ts +3 -3
- package/src/proxmox/client.ts +3 -3
- package/src/proxmox/credentials.test.ts +6 -8
- package/src/proxmox/credentials.ts +8 -8
- package/src/proxmox/fake-pve-lxc.ts +13 -1
- package/src/proxmox/firewall-alias-form.ts +1 -1
- package/src/proxmox/firewall-alias.ts +1 -1
- package/src/proxmox/generated/pbs.ts +1 -1
- package/src/proxmox/generated/pve.ts +1 -1
- package/src/proxmox/group.ts +10 -10
- package/src/proxmox/ha-resource.ts +3 -2
- package/src/proxmox/ha-rule-form.ts +5 -5
- package/src/proxmox/ha-rule.ts +3 -3
- package/src/proxmox/index.test.ts +7 -0
- package/src/proxmox/index.ts +14 -0
- package/src/proxmox/lease-cache.test.ts +19 -19
- package/src/proxmox/lease-cache.ts +4 -4
- package/src/proxmox/lxc-adopt.test.ts +16 -23
- package/src/proxmox/lxc-adoption.ts +50 -0
- package/src/proxmox/lxc-create-form.ts +1 -1
- package/src/proxmox/lxc-guards.test.ts +10 -17
- package/src/proxmox/lxc-harness.ts +27 -4
- package/src/proxmox/lxc-judge.test.ts +5 -5
- package/src/proxmox/lxc-lifecycle.ts +1 -1
- package/src/proxmox/lxc-ownership.test.ts +19 -14
- package/src/proxmox/lxc-props.ts +2 -2
- package/src/proxmox/lxc-read.test.ts +3 -3
- package/src/proxmox/lxc-strict-adopt.test.ts +104 -0
- package/src/proxmox/lxc-volume.test.ts +9 -9
- package/src/proxmox/lxc-volume.ts +1 -1
- package/src/proxmox/lxc-wire.ts +1 -1
- package/src/proxmox/lxc.ts +19 -15
- package/src/proxmox/members.test.ts +1 -1
- package/src/proxmox/members.ts +3 -3
- package/src/proxmox/metric-server-otel.ts +2 -2
- package/src/proxmox/network-apply-read.ts +10 -10
- package/src/proxmox/network-apply.ts +10 -10
- package/src/proxmox/node-network-form.ts +7 -7
- package/src/proxmox/node-network.ts +18 -18
- package/src/proxmox/notification-target.ts +3 -2
- package/src/proxmox/pbs-datastore-guard.ts +1 -1
- package/src/proxmox/pbs-datastore.ts +10 -10
- package/src/proxmox/pbs-prune-job.ts +2 -2
- package/src/proxmox/pbs-sync-job.ts +4 -4
- package/src/proxmox/pbs-verify-job.ts +1 -1
- package/src/proxmox/pool.test.ts +2 -2
- package/src/proxmox/pool.ts +4 -4
- package/src/proxmox/provision-baseline.test.ts +87 -0
- package/src/proxmox/provision-baseline.ts +162 -0
- package/src/proxmox/provision-bootstrap.test.ts +132 -0
- package/src/proxmox/provision-bootstrap.ts +135 -0
- package/src/proxmox/provision-cli-fake.ts +217 -0
- package/src/proxmox/provision-declare.test.ts +111 -0
- package/src/proxmox/provision-declare.ts +103 -0
- package/src/proxmox/replication-job.ts +9 -8
- package/src/proxmox/resource.ts +3 -3
- package/src/proxmox/sdn-apply-read.test.ts +16 -16
- package/src/proxmox/sdn-apply-read.ts +7 -7
- package/src/proxmox/sdn-apply.ts +3 -3
- package/src/proxmox/sdn-subnet-form.ts +1 -1
- package/src/proxmox/sdn-subnet.ts +5 -5
- package/src/proxmox/sdn-vnet.ts +5 -6
- package/src/proxmox/storage.ts +4 -3
- package/src/proxmox/user.ts +3 -2
- package/src/proxmox/values.test.ts +2 -2
- package/src/proxmox/values.ts +1 -1
- package/src/proxmox/zfs-pool-write.ts +1 -1
- package/src/proxmox/zfs-pool.ts +6 -6
- package/src/talos/credentials.ts +3 -3
- package/src/talos/values.test.ts +9 -9
- package/src/verify/cli.test.ts +1 -1
package/dist/openbao/mount.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* in OpenBao, not Alchemy state.
|
|
7
7
|
*
|
|
8
8
|
* ★ `defaultRemovalPolicy: 'retain'` — disabling a mount destroys every secret under it.
|
|
9
|
-
* Opt in with `.pipe(RemovalPolicy.destroy())`; see resource.ts in
|
|
9
|
+
* Opt in with `.pipe(RemovalPolicy.destroy())`; see resource.ts in <estate>/proxmox.
|
|
10
10
|
*
|
|
11
11
|
* ★ REPLACE SEMANTICS (audited 2026-09-21, see src/openbao/REPLACE.md):
|
|
12
12
|
* · `type` changed → plans `replace`, and the APPLY FAILS, destroying nothing: the new
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* never a policy one — but neither is a measurement.
|
|
37
37
|
*
|
|
38
38
|
* ★ `defaultRemovalPolicy: 'retain'` — see the delete comment below. Opt in with
|
|
39
|
-
* `.pipe(RemovalPolicy.destroy())`; see resource.ts in
|
|
39
|
+
* `.pipe(RemovalPolicy.destroy())`; see resource.ts in <estate>/proxmox.
|
|
40
40
|
*/
|
|
41
41
|
import { Resource } from 'alchemy';
|
|
42
42
|
import * as Provider from 'alchemy/Provider';
|
package/dist/openbao/policy.d.ts
CHANGED
|
@@ -15,8 +15,8 @@ import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
|
15
15
|
* ⛔ THERE IS NO `Bao.Secret`, AND THERE MUST NEVER BE ONE. Alchemy persists attributes to
|
|
16
16
|
* its state store and does NOT encrypt them: StateEncoding.ts unwraps `Redacted` to
|
|
17
17
|
* plaintext under a marker key, and nothing under alchemy/src/State encrypts. This
|
|
18
|
-
* estate's state store is the `alchemy` Postgres database, which
|
|
19
|
-
*
|
|
18
|
+
* estate's state store is the `alchemy` Postgres database, which a nightly job dumps
|
|
19
|
+
* (it enumerates EVERY non-template database), copies to a backup guest, and PBS backs up
|
|
20
20
|
* from there. A KV value here would exist in four places, none of them OpenBao.
|
|
21
21
|
*
|
|
22
22
|
* So this declares the SHAPE AROUND secrets. Every attribute it stores is a name, a
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export interface BaoProxmoxRoleProps {
|
|
2
|
-
/** Mount path of the proxmox secrets engine, e.g. `proxmox-
|
|
2
|
+
/** Mount path of the proxmox secrets engine, e.g. `proxmox-c1`. A trailing slash is fine. */
|
|
3
3
|
mount: string;
|
|
4
4
|
/**
|
|
5
5
|
* Role name, as `<mount>/roles/<name>` takes it.
|
|
@@ -61,7 +61,7 @@ export interface BaoProxmoxRoleAttributes {
|
|
|
61
61
|
/** SHA-256 of the managed fields — safe to persist; see policy.ts. */
|
|
62
62
|
digest: string;
|
|
63
63
|
}
|
|
64
|
-
/** `proxmox-
|
|
64
|
+
/** `proxmox-c1/roles/read` — read, write and delete all address the role here. */
|
|
65
65
|
export declare const rolePath: (mount: string, name: string) => string;
|
|
66
66
|
export declare const attributesOf: (props: Pick<BaoProxmoxRoleProps, 'mount' | 'name'>, live: Record<string, unknown>) => BaoProxmoxRoleAttributes;
|
|
67
67
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxmox-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/proxmox-role-form.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,mBAAmB;IAClC,
|
|
1
|
+
{"version":3,"file":"proxmox-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/proxmox-role-form.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,mBAAmB;IAClC,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;OAaG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAmBD,kFAAkF;AAClF,eAAO,MAAM,QAAQ,UAAW,MAAM,QAAQ,MAAM,WAAwC,CAAC;AAE7F,eAAO,MAAM,YAAY,UAChB,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,MAAM,CAAC,QAC5C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,wBAYF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS,UACb,mBAAmB,KACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAWzE,CAAC;AAaF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,WACjB,MAAM,YACJ,MAAM,uBACK,MAAM,GAAG,SAAS,KACtC,MAAM,GAAG,SAIyF,CAAC;AAEtG,gGAAgG;AAChG,eAAO,MAAM,OAAO,eAAgB,wBAAwB,SAAS,mBAAmB,YAG9C,CAAC"}
|
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
* mint user, plus the lease ceilings that consumer's tokens get. METADATA ONLY.
|
|
4
4
|
*
|
|
5
5
|
* ★ WHY THIS FAMILY MATTERS MORE THAN IT LOOKS. Declaring these roles is how the estate
|
|
6
|
-
* replaces HAND-MADE PERMANENT PVE TOKENS with short-lived minted ones. On
|
|
7
|
-
* carry expire=0 and privsep=0 — root@pam!
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* replaces HAND-MADE PERMANENT PVE TOKENS with short-lived minted ones. On C1, SEVEN tokens
|
|
7
|
+
* carry expire=0 and privsep=0 — root@pam!inventory-sync, agent@pve!executor,
|
|
8
|
+
* metrics@pve!exporter, app@pve!app, iac@pve!ro, iac@pve!apply and
|
|
9
|
+
* mint@pve!engine. Six of those are one Bao.ProxmoxRole plus a scoped PVE user away from
|
|
10
10
|
* being a 5-minute lease instead of a forever key.
|
|
11
11
|
*
|
|
12
|
-
* The seventh cannot be.
|
|
12
|
+
* The seventh cannot be. mint@pve!engine is the credential the ENGINE ITSELF
|
|
13
13
|
* authenticates with in order to mint anything at all: the thing that issues short-lived
|
|
14
14
|
* tokens needs a standing one, and no role can resolve that chicken-and-egg. It stays
|
|
15
15
|
* permanent, which makes it the single most valuable token on the cluster and the one whose
|
|
16
16
|
* rotation must be a deliberate, hand-run procedure rather than a plan.
|
|
17
17
|
*
|
|
18
|
-
* MEASURED here: `bao read proxmox-
|
|
19
|
-
* `
|
|
18
|
+
* MEASURED here: `bao read proxmox-c1/config` returns parent_token_id
|
|
19
|
+
* `mint@pve!engine`, parent_secret_set true, parent_secret_tail `…eb01` — the engine's
|
|
20
20
|
* own key, present and, by the schema's own "Write-only: never returned by a read", not
|
|
21
21
|
* retrievable. The seven-token inventory was handed to this change and NOT re-counted against
|
|
22
22
|
* the PVE API in this session.
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
*
|
|
28
28
|
* ⛔ ISSUANCE IS NOT THIS RESOURCE, AND MUST NEVER BECOME ONE. `<mount>/creds/<name>` returns a
|
|
29
29
|
* LIVE PVE TOKEN. Alchemy persists attributes unencrypted (StateEncoding.ts tags Redacted
|
|
30
|
-
* values rather than encrypting them) into the `alchemy` Postgres, which
|
|
31
|
-
*
|
|
30
|
+
* values rather than encrypting them) into the `alchemy` Postgres, which a nightly job dumps
|
|
31
|
+
* to a backup guest, from where PBS backs it up. A minted token declared as a resource would
|
|
32
32
|
* outlive its own lease in at least four places, none of them OpenBao. This resource declares
|
|
33
33
|
* the DOOR; walking through it is a runtime call, not a plan.
|
|
34
34
|
*
|
|
35
35
|
* ★ `defaultRemovalPolicy: 'retain'` — deleting a role breaks every consumer that mints from
|
|
36
|
-
* it. Opt in with `.pipe(RemovalPolicy.destroy())`; see resource.ts in
|
|
36
|
+
* it. Opt in with `.pipe(RemovalPolicy.destroy())`; see resource.ts in <estate>/proxmox.
|
|
37
37
|
*/
|
|
38
38
|
import { Resource } from 'alchemy';
|
|
39
39
|
import * as Provider from 'alchemy/Provider';
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* ⛔ THE SIGNING KEY IS OUT OF SCOPE AND MUST STAY OUT. `ssh/config/ca` holds (or generates)
|
|
5
5
|
* the CA private key and `ssh/sign/<role>` issues certificates. Neither is declarable
|
|
6
6
|
* here, for the reason policy.ts spells out: Alchemy persists attributes to its state
|
|
7
|
-
* store WITHOUT encrypting them, that store is the `alchemy` Postgres, and
|
|
8
|
-
* dumps it
|
|
7
|
+
* store WITHOUT encrypting them, that store is the `alchemy` Postgres, and a nightly job
|
|
8
|
+
* dumps it to a backup guest. A CA key there would outlive the vault that guards it. This
|
|
9
9
|
* resource declares the SHAPE a signature must satisfy — principals, extensions, TTLs.
|
|
10
10
|
*
|
|
11
11
|
* ⛔ THE ROLE WRITE IS A FULL REPLACE, NOT A PATCH. OpenBao rebuilds the role entry from the
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*
|
|
30
30
|
* ★ `defaultRemovalPolicy: 'retain'`. Deleting this role locks every operator out of the
|
|
31
31
|
* estate — the memory note "operator SSH is homeflare-bot@<host>" is this CA. Opt in with
|
|
32
|
-
* `.pipe(RemovalPolicy.destroy())`; see resource.ts in
|
|
32
|
+
* `.pipe(RemovalPolicy.destroy())`; see resource.ts in <estate>/proxmox.
|
|
33
33
|
*/
|
|
34
34
|
import { Resource } from 'alchemy';
|
|
35
35
|
import * as Provider from 'alchemy/Provider';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a `diff` or `reconcile` that holds attributes is looking at an ADOPTION — a live object
|
|
3
|
+
* that reached this stack's state by being FOUND, not by this resource's own create — so a family
|
|
4
|
+
* whose adoption must never change what it takes over (`Proxmox.Lxc`) can refuse any difference
|
|
5
|
+
* instead of writing it. adopt.ts and probe.ts decide WHETHER an object may be claimed; this says,
|
|
6
|
+
* once it is claimed, whether the claim is an adoption.
|
|
7
|
+
*
|
|
8
|
+
* ★ STATE ALREADY SAYS WHICH, AT BOTH HALVES OF A DEPLOY (alchemy 2.0.0-beta.79):
|
|
9
|
+
* · PLAN, A COLD ADOPTION. The probe mints an instance id no row can hold (Plan.ts
|
|
10
|
+
* `generateInstanceId()`), and `diff` then runs with that id and the attributes the probe
|
|
11
|
+
* read. So no row holds the id.
|
|
12
|
+
* · APPLY, THE SAME ADOPTION. Apply commits an `updating` row with `adopting: true` under that id
|
|
13
|
+
* BEFORE it calls reconcile, and the flag stays until the update commits (Apply.ts). An
|
|
14
|
+
* adoption whose apply was interrupted keeps it, so the next plan's `diff` sees it too.
|
|
15
|
+
* · A RECOVERED CREATE. A `creating` row with no attributes, whose live object the recovery read
|
|
16
|
+
* found. It is this resource's own create when the row proves it (probe.ts `provenOurs`, the
|
|
17
|
+
* same proof the read asked). Otherwise the plan went on only because `--adopt` said so — an
|
|
18
|
+
* adoption like any other.
|
|
19
|
+
* · Any other row with attributes is state vouching for the object: not an adoption.
|
|
20
|
+
* ⚠️ OUTSIDE A STACK — no Stack or State in context, or a store that errors (rows.ts) — no row
|
|
21
|
+
* holds anything, so every call is an adoption: the strict answer, never the permissive one. A
|
|
22
|
+
* family then refuses an ordinary update's write as if it were an adoption's. `alchemy deploy`
|
|
23
|
+
* and the Alchemist session provide both services.
|
|
24
|
+
*/
|
|
25
|
+
import * as Effect from 'effect/Effect';
|
|
26
|
+
import type { Owner } from './adopt.ts';
|
|
27
|
+
/**
|
|
28
|
+
* Whether `owner`'s call is an adoption (header). `settled` is the family's "live matches these
|
|
29
|
+
* props", asked only for a recovered create, with the props of the row that recorded it.
|
|
30
|
+
* ⛔ A CREATE IS NEVER ONE HERE. `output` undefined is reconcile's create path, which has its own
|
|
31
|
+
* rule for a live object it finds (adopt.ts `refuseTakeover`).
|
|
32
|
+
*/
|
|
33
|
+
export declare const adopting: <E, R>(owner: Owner, settled: (recorded: unknown) => Effect.Effect<boolean, E, R>) => Effect.Effect<boolean, never, R>;
|
|
34
|
+
//# sourceMappingURL=adopting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adopting.d.ts","sourceRoot":"","sources":["../../src/ownership/adopting.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAIxC;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,CAAC,SACpB,KAAK,WACH,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3D,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAO9B,CAAC"}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import * as Effect from 'effect/Effect';
|
|
2
2
|
import type { Owner } from './adopt.ts';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Whether the state store proves the live object this instance's own: a row records `ask.instanceId`
|
|
5
|
+
* with its whole declaration (whole.ts) AND `settled` — the family's own "live matches these props"
|
|
6
|
+
* — says so. The one proof ownership/ accepts for a live object state holds no attributes for; the
|
|
7
|
+
* recovery read (`ownedRead`) and an adoption check (adopting.ts) both ask it.
|
|
8
|
+
* ⛔ `settled` NEVER FAILS THE CALLER. The recovery read runs with the INTERRUPTED deploy's props, so
|
|
8
9
|
* a declaration that cannot be evaluated any more (a fragments directory since moved, a group
|
|
9
10
|
* name since renamed) would fail every later plan, the fix included. It reads as "not proven
|
|
10
|
-
* ours" instead
|
|
11
|
+
* ours" instead, with the reason logged.
|
|
12
|
+
*/
|
|
13
|
+
export declare const provenOurs: <E, R>(ask: {
|
|
14
|
+
readonly fqn: string;
|
|
15
|
+
readonly instanceId: string;
|
|
16
|
+
}, settled: Effect.Effect<boolean, E, R>) => Effect.Effect<boolean, never, R>;
|
|
17
|
+
/**
|
|
18
|
+
* A family's `read` answer. With attributes in state it is `found` as is. Without, it is the probe
|
|
19
|
+
* or the recovery read (header): `found` is ours only when `provenOurs` says so, else `Unowned`,
|
|
20
|
+
* which `--adopt` resolves.
|
|
11
21
|
*/
|
|
12
22
|
export declare const ownedRead: <A extends object, E, R>(ask: Owner, found: A | undefined, settled: Effect.Effect<boolean, E, R>) => Effect.Effect<A | undefined, never, R>;
|
|
13
23
|
//# sourceMappingURL=probe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/ownership/probe.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC
|
|
1
|
+
{"version":3,"file":"probe.d.ts","sourceRoot":"","sources":["../../src/ownership/probe.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,OACxB;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,WACjD,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KACpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAoB9B,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,OACzC,KAAK,SACH,CAAC,GAAG,SAAS,WACX,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KACpC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,CAAC,CAIpC,CAAC"}
|
package/dist/ownership/rows.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import * as Effect from 'effect/Effect';
|
|
2
|
+
/** One generation of a persisted row, as far as ownership/ reads it. */
|
|
3
|
+
export type Generation = {
|
|
4
|
+
readonly instanceId?: unknown;
|
|
5
|
+
readonly status?: unknown;
|
|
6
|
+
readonly props?: unknown;
|
|
7
|
+
readonly attr?: unknown;
|
|
8
|
+
readonly old?: unknown;
|
|
9
|
+
/** Set by Apply on the `updating` row of an adoption, until that update commits (Apply.ts). */
|
|
10
|
+
readonly adopting?: unknown;
|
|
11
|
+
};
|
|
12
|
+
/** The generation `instanceId` names — at the FQN, or at a former FQN `renamedFrom` names. */
|
|
13
|
+
export declare const recordedGeneration: (fqn: string, instanceId: string) => Effect.Effect<Generation | undefined>;
|
|
2
14
|
/**
|
|
3
15
|
* What the state store says of `instanceId` for this resource (at its FQN, or at a former FQN its
|
|
4
16
|
* `renamedFrom` names — the planner migrates that row before it recovers it):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/ownership/rows.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/ownership/rows.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAmBxC,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAaF,8FAA8F;AAC9F,eAAO,MAAM,kBAAkB,QACxB,MAAM,cACC,MAAM,KACjB,MAAM,CAAC,MAAM,CAAC,UAAU,GAAG,SAAS,CASnC,CAAC;AAEL;;;;;;;;;GASG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtD,eAAO,MAAM,gBAAgB,QAAS,MAAM,cAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAKrF,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,QAAS,MAAM,cAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAI7E,CAAC;AAEJ;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,QAAS,MAAM,cAAc,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAUpF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ResourceState } from 'alchemy/State/ResourceState';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
/** A row with no attributes: `creating`, or `replacing` when it carries an `old` generation. */
|
|
4
|
+
export declare const row: (fqn: string, instanceId: string, old?: unknown, props?: unknown) => ResourceState;
|
|
5
|
+
/**
|
|
6
|
+
* A stack `s` at stage `test` whose store holds `rows`: `A` and `B` declared with a `name`, and `Z`
|
|
7
|
+
* declared `renamedFrom('X')`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const withStore: <A>(rows: Record<string, ResourceState>, effect: Effect.Effect<A>) => Promise<A>;
|
|
10
|
+
//# sourceMappingURL=store-fixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-fixture.d.ts","sourceRoot":"","sources":["../../src/ownership/store-fixture.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,gGAAgG;AAChG,eAAO,MAAM,GAAG,QACT,MAAM,cACC,MAAM,QACZ,OAAO,UACN,OAAO,KACb,aAO6B,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,QACnB,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,UAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KACvB,OAAO,CAAC,CAAC,CAgBT,CAAC"}
|
package/dist/proxmox/acl.d.ts
CHANGED
|
@@ -36,14 +36,14 @@ import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
|
36
36
|
export type AclSubjectType = 'user' | 'group' | 'token';
|
|
37
37
|
export interface AclProps extends WithTarget {
|
|
38
38
|
/**
|
|
39
|
-
* The PVE object path the grant is ON — `/`, `/pool/
|
|
39
|
+
* The PVE object path the grant is ON — `/`, `/pool/lab`, `/vms/101`, `/storage/local-zfs`.
|
|
40
40
|
* ⚠️ NOT THE API PATH: `spec.path` below is the endpoint (`access/acl`). PVE's API overloads the
|
|
41
41
|
* word, so this file does too rather than renaming a field the cluster calls `path`.
|
|
42
42
|
*/
|
|
43
43
|
path: string;
|
|
44
44
|
/** Which kind of subject `ugid` names. Identity: changing it is a different grant. */
|
|
45
45
|
type: AclSubjectType;
|
|
46
|
-
/** `
|
|
46
|
+
/** `alice@pve`, `admins`, `hf-provision@pve!hf-provision-…`. Identity. */
|
|
47
47
|
ugid: string;
|
|
48
48
|
/** The role bound here, e.g. `PVEAuditor`. ⚠️ It must exist — PVE refuses an unknown roleid. */
|
|
49
49
|
roleid: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAiB,MAAM,eAAe,CAAC;AAErF,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,IAAI,EAAE,cAAc,CAAC;IACrB,
|
|
1
|
+
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAiB,MAAM,eAAe,CAAC;AAErF,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,IAAI,EAAE,cAAc,CAAC;IACrB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,QAAQ,EACR,aAAa,EACb,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,UAAU,6CAAsC,CAAC;AAmH9D,eAAO,MAAM,kBAAkB,gIAqE5B,CAAC"}
|
|
@@ -12,7 +12,7 @@ import type { ApiTokenAttributes, ApiTokenProps } from './api-token.ts';
|
|
|
12
12
|
*
|
|
13
13
|
* ⛔ THE WRITE SEMANTICS BELOW ARE READ FROM THE CLUSTER'S OWN PERL RATHER THAN INFERRED FROM THE
|
|
14
14
|
* API SCHEMA — `/usr/share/perl5/PVE/API2/User.pm`, subs `generate_token` and
|
|
15
|
-
* `update_token_info`, read over SSH on
|
|
15
|
+
* `update_token_info`, read over SSH on node-b on 2026-09-13. The two disagree in exactly the place
|
|
16
16
|
* that costs a forever-diff: the schema gives `expire` the default "same as user", and the code
|
|
17
17
|
* never implements it. api-token.ts records what that means.
|
|
18
18
|
*/
|
|
@@ -24,7 +24,7 @@ import type { PveSpec } from './resource.ts';
|
|
|
24
24
|
* `ugid`, so reporting it as an attribute is what lets a privilege-separated token be granted
|
|
25
25
|
* anything at all without somebody retyping the join by hand.
|
|
26
26
|
*
|
|
27
|
-
* ⚠️ DERIVED, NOT READ. MEASURED: `GET /access/users/
|
|
27
|
+
* ⚠️ DERIVED, NOT READ. MEASURED: `GET /access/users/metrics@pve/token/exporter` answers
|
|
28
28
|
* `{"expire":0,"privsep":0}` and echoes back neither half of its own identity. So this is built
|
|
29
29
|
* from props, cannot disagree with the path the read used, and is therefore true by
|
|
30
30
|
* construction — which is why it is reported and never compared.
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* `Proxmox.ApiToken` — a PVE API token: the thing a service actually authenticates as.
|
|
3
3
|
*
|
|
4
4
|
* ⛔ THE SECRET EXISTS FOR ONE HTTP RESPONSE AND THEN NOWHERE. MEASURED from the cluster's own
|
|
5
|
-
* schema (`/usr/share/pve-docs/api-viewer/apidoc.js` on
|
|
5
|
+
* schema (`/usr/share/pve-docs/api-viewer/apidoc.js` on node-b, 2026-09-13): POST returns
|
|
6
6
|
* `["full-tokenid","info","value"]`; PUT returns `["comment","expire","full-tokenid","privsep",
|
|
7
7
|
* "value"]` with `value` present ONLY when `regenerate` was set; GET returns
|
|
8
8
|
* `["comment","expire","privsep"]` and never the secret. A live token agrees —
|
|
9
|
-
* `GET /access/users/
|
|
9
|
+
* `GET /access/users/metrics@pve/token/exporter` answers `{"expire":0,"privsep":0}`. PVE says
|
|
10
10
|
* it plainly on the POST: the value "needs to be stored as it cannot be retrieved afterwards".
|
|
11
11
|
*
|
|
12
12
|
* ⛔ SO A TOKEN THIS RESOURCE CREATES IS UNUSABLE, AND SAYING SO IS THE POINT OF THIS BLOCK. The
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* ★ SO POINT THIS FAMILY AT TOKENS THAT ALREADY EXIST. `comment`, `expire` and `privsep` are the
|
|
20
20
|
* whole of a token's policy, and declaring them is real work: it is how `expire` stops being
|
|
21
21
|
* whatever somebody typed in 2024. Mint NEW tokens where the secret can be caught — OpenBao's
|
|
22
|
-
* `proxmox-
|
|
22
|
+
* `proxmox-c1` mount, or a human at `pveum user token add`.
|
|
23
23
|
* ⚠️ THE CREATE PATH IS LEFT REACHABLE RATHER THAN STUBBED. A create that silently did nothing
|
|
24
24
|
* would be the same lie as the stubbed delete resource.ts's ★ refuses; the honest arrangement
|
|
25
25
|
* is a create that works and a header that says what it produces.
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
* that owns the lease, or to a human who is watching.
|
|
32
32
|
*
|
|
33
33
|
* ⚠️ THERE IS A SECOND WRITER TO THESE OBJECTS AND IT IS THE ONE THIS PROVIDER RUNS ON. OpenBao's
|
|
34
|
-
* `proxmox-
|
|
35
|
-
*
|
|
34
|
+
* `proxmox-c1` engine mints PVE tokens under `hf-read@pve` and `hf-provision@pve` — read from
|
|
35
|
+
* `<estate>/platform/secrets/vault/plugin-proxmox/proxmox/`: `privsep=0` (client.go), `expire` set
|
|
36
36
|
* to the lease deadline, and an id the plugin CHOOSES,
|
|
37
37
|
* `hf-<role>-<actor>-<entity6>-<stamp>-<nonce>` (tokenname.go). Revocation and WAL rollback look
|
|
38
38
|
* up that one exact id (wal.go); neither sweeps a prefix. So the two systems cannot collide on
|
|
@@ -59,7 +59,7 @@ import * as Provider from 'alchemy/Provider';
|
|
|
59
59
|
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
60
60
|
export interface ApiTokenProps extends WithTarget {
|
|
61
61
|
/**
|
|
62
|
-
* The account that owns the token, realm-qualified: `
|
|
62
|
+
* The account that owns the token, realm-qualified: `iac@pve`, `root@pam`. Identity.
|
|
63
63
|
*
|
|
64
64
|
* ⚠️ THE ACCOUNT MUST ALREADY EXIST. Every method here runs `check_user_exist` first, so a
|
|
65
65
|
* token named under a missing user fails the READ (folded to "absent") and then fails the
|
|
@@ -117,7 +117,7 @@ export interface ApiTokenProps extends WithTarget {
|
|
|
117
117
|
* — `$privsep = $privsep ? 1 : 0` (AccessControl.pm:1622), so there is no unset state to
|
|
118
118
|
* preserve. Were this optional, adopting a live token without mentioning `privsep` would plan
|
|
119
119
|
* an update to 1 and STRIP a working credential of every privilege it has, silently: PVE
|
|
120
|
-
* returns 401/403 to the service, nothing errors here, and `
|
|
120
|
+
* returns 401/403 to the service, nothing errors here, and `iac@pve!apply` simply stops
|
|
121
121
|
* working. Required, so adopting a token is a sentence that states what it is.
|
|
122
122
|
* ⚠️ AND `false` IS NOT A SHRUG. It is a token with its owner's whole privilege set; under a
|
|
123
123
|
* provisioning account that is the account's full authority with a separate secret.
|
|
@@ -139,9 +139,9 @@ export interface ProxmoxApiToken extends Resource<'Proxmox.ApiToken', ApiTokenPr
|
|
|
139
139
|
* ★ `retain` BY DEFAULT, AND THIS IS THE PLAINEST CASE FOR IT IN THE PACKAGE. A token's contents
|
|
140
140
|
* are one irreplaceable secret: delete it and the value is gone, a replacement is a DIFFERENT
|
|
141
141
|
* value, and every holder loses access the instant `cfs_write_file` returns — with no error
|
|
142
|
-
* raised anywhere near them. The live cluster's tokens are `
|
|
143
|
-
* exporter feeding VictoriaMetrics), `
|
|
144
|
-
* `
|
|
142
|
+
* raised anywhere near them. The live cluster's tokens are `metrics@pve!exporter` (the PVE
|
|
143
|
+
* exporter feeding VictoriaMetrics), `agent@pve!executor`, `iac@pve!apply`, `iac@pve!ro`,
|
|
144
|
+
* `app@pve!app` and `mint@pve!engine` — the last being the parent credential the
|
|
145
145
|
* OpenBao mount itself authenticates with, so orphaning that one would stop every plan in this
|
|
146
146
|
* package, this resource included. `delete` is FULLY IMPLEMENTED (DELETE is a real method on
|
|
147
147
|
* this path, measured) and runs the moment a caller opts in with `.pipe(RemovalPolicy.destroy())`.
|
|
@@ -150,7 +150,7 @@ export interface ProxmoxApiToken extends Resource<'Proxmox.ApiToken', ApiTokenPr
|
|
|
150
150
|
export declare const ProxmoxApiToken: import("alchemy").ResourceClass<ProxmoxApiToken>;
|
|
151
151
|
/**
|
|
152
152
|
* ⛔ `list` IS EMPTY, AND FOR THIS FAMILY THAT MATTERS MORE THAN THE GENERIC ARGUMENT IN
|
|
153
|
-
* resource.ts. A token index handed to Alchemy would offer up `
|
|
153
|
+
* resource.ts. A token index handed to Alchemy would offer up `mint@pve!engine` and every
|
|
154
154
|
* live OpenBao lease for adoption — and adoption is what makes a later plan willing to delete.
|
|
155
155
|
* The mount's leases in particular appear and vanish on their own; anything that adopted one
|
|
156
156
|
* would report drift against a credential that was never its to hold.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* here decides a diff; `findRow` picks a row out of an answer its caller already fetched.
|
|
10
10
|
*
|
|
11
11
|
* ⛔ THE TWO PATHS ARE DIFFERENT AND THAT IS THE WHOLE REASON THIS FILE HAS TWO OF THEM. MEASURED
|
|
12
|
-
* on
|
|
12
|
+
* on node-b, 2026-09-13: there is no GET on the id path — `pvesh get /nodes/node-b/ceph/mon/node-b` answers
|
|
13
13
|
* "No 'get' handler defined" — while POST and DELETE are registered ONLY there. So the read uses
|
|
14
14
|
* `collectionPath` and the two writes use `daemonPath`, and swapping either one is a silent
|
|
15
15
|
* 501: a create that never lands, or a destroy that reports failure while the daemon runs on.
|
|
@@ -38,8 +38,8 @@ import type { CephDaemonProps } from './ceph-daemon.ts';
|
|
|
38
38
|
export declare const daemonId: (props: CephDaemonProps) => string;
|
|
39
39
|
/**
|
|
40
40
|
* The read path. ⚠️ `{node}` HERE IS ONLY THE NODE BEING ASKED, not a filter. MEASURED:
|
|
41
|
-
* `GET /nodes/
|
|
42
|
-
* three IN A DIFFERENT ORDER — [
|
|
41
|
+
* `GET /nodes/node-b/ceph/mon` returns node-b, node-c AND node-d, and `GET /nodes/node-c/ceph/mon` returns the same
|
|
42
|
+
* three IN A DIFFERENT ORDER — [node-d,node-b,node-c] from node-b against [node-c,node-b,node-d] from node-c. A caller that took
|
|
43
43
|
* row zero, or assumed the list was this node's daemons, would be wrong on both counts.
|
|
44
44
|
*/
|
|
45
45
|
export declare const collectionPath: (props: CephDaemonProps) => string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `Proxmox.CephDaemon` — a Ceph monitor, manager or metadata server on one node.
|
|
3
3
|
*
|
|
4
4
|
* ★ ONE RESOURCE FOR THREE KINDS, AND THE SCHEMAS WERE CHECKED BEFORE THAT WAS DECIDED. Parsed
|
|
5
|
-
* from `/usr/share/pve-docs/api-viewer/apidoc.js` on
|
|
5
|
+
* from `/usr/share/pve-docs/api-viewer/apidoc.js` on node-b, 2026-09-13: mon, mgr and mds each
|
|
6
6
|
* expose GET on the collection and POST + DELETE on the id below it, each returns
|
|
7
7
|
* name/host/state/addr/ceph_version*, and the only divergence is the create parameter
|
|
8
8
|
* (`mon-address` / none / `hotstandby`) and the read-only extras (mon: quorum, rank; mds: rank,
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
* times over and let them drift — the argument resource.ts already makes for the factory itself.
|
|
11
11
|
*
|
|
12
12
|
* ⛔ THE ONLY GET IS THE COLLECTION'S, AND IT ANSWERS FOR THE WHOLE CLUSTER. MEASURED:
|
|
13
|
-
* `GET /nodes/
|
|
14
|
-
* three IN A DIFFERENT ORDER — [
|
|
13
|
+
* `GET /nodes/node-b/ceph/mon` returns node-b, node-c AND node-d, and `GET /nodes/node-c/ceph/mon` returns the same
|
|
14
|
+
* three IN A DIFFERENT ORDER — [node-d,node-b,node-c] from node-b, [node-c,node-b,node-d] from node-c — so a row is found by
|
|
15
15
|
* `name` and NEVER by position, and the `{node}` in the path is only the node being ASKED.
|
|
16
|
-
* MEASURED too: there is no GET on the id path at all; `pvesh get /nodes/
|
|
17
|
-
* "No 'get' handler defined for '/nodes/
|
|
16
|
+
* MEASURED too: there is no GET on the id path at all; `pvesh get /nodes/node-b/ceph/mon/node-b` answers
|
|
17
|
+
* "No 'get' handler defined for '/nodes/node-b/ceph/mon/node-b'", and mgr and mds answer the same.
|
|
18
18
|
* That is why `path()` below is the COLLECTION, and that in turn is why `delete` is the one
|
|
19
19
|
* handler this file writes by hand: `pveOperations.destroy` sends its DELETE to `spec.path`,
|
|
20
20
|
* which here would be the collection — a 501 reported as a failed destroy, with the daemon still
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
* strips its section from ceph.conf. THREE MONS TOLERATE ONE LOSS; TWO TOLERATE NONE. Removing
|
|
28
28
|
* two in one deploy — or removing the mon on the node whose API is serving the call — loses
|
|
29
29
|
* quorum, and a cluster without mon quorum BLOCKS every RBD and CephFS I/O rather than erroring
|
|
30
|
-
* it, so on this cluster every guest on `
|
|
30
|
+
* it, so on this cluster every guest on `rbd-c1` hangs.
|
|
31
31
|
* `mds`: destroying a STANDBY is a non-event; destroying the ACTIVE one fails CephFS over,
|
|
32
|
-
* and with no standby left `cephfs-
|
|
32
|
+
* and with no standby left `cephfs-c1` goes unavailable. `mgr`: no guest I/O depends on it, but
|
|
33
33
|
* the last one takes the PG autoscaler, the dashboard and PVE's own Ceph status with it.
|
|
34
|
-
* Live on 2026-09-13: mon, mgr AND mds on each of
|
|
34
|
+
* Live on 2026-09-13: mon, mgr AND mds on each of node-b, node-c, node-d.
|
|
35
35
|
*
|
|
36
36
|
* ★ AND THE ONLY WAY TO REACH THAT DESTROY IS TO DELETE THE DECLARATION. `matches` is deliberately
|
|
37
37
|
* total (see its ⛔), so this resource CANNOT plan a `replace` — which is the one action that
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* `Sys.Audit`/`Datastore.Audit` on `/`, and PVEAuditor holds both (MEASURED:
|
|
42
42
|
* `GET /access/roles/PVEAuditor`), so the mount's `read` role can see this family — unlike
|
|
43
43
|
* `Proxmox.Storage`, which 403s under that lease. POST and DELETE both check `Sys.Modify` on
|
|
44
|
-
* `/`, and MEASURED on this cluster
|
|
44
|
+
* `/`, and MEASURED on this cluster the provision role already held it (it is in `PROVISION_PRIVILEGES`), so the provision lane
|
|
45
45
|
* needs no widening for once. ⚠️ `Sys.Modify` on the ROOT path is broad — it also buys
|
|
46
46
|
* datacenter options and every other cluster-wide config write — so it is worth knowing that
|
|
47
47
|
* this credential already has it rather than discovering it the next time something is scoped.
|
|
@@ -80,9 +80,9 @@ export interface CephDaemonProps extends WithTarget {
|
|
|
80
80
|
name?: string;
|
|
81
81
|
/**
|
|
82
82
|
* mon only. Overrides the autodetected monitor IP; must sit in Ceph's public network — on this
|
|
83
|
-
* cluster that is
|
|
83
|
+
* cluster that is 198.51.100.0/24, carried by `vmbr1.42`.
|
|
84
84
|
* ⛔ CREATE-ONLY AND NEVER COMPARED. PVE takes a bare ip-list on write and hands back `addr` as
|
|
85
|
-
* `
|
|
85
|
+
* `198.51.100.12:6789/0` — an address, a port and a nonce. The two are not the same string and
|
|
86
86
|
* never will be, so diffing them would report a mismatch on every plan; with no PUT on this
|
|
87
87
|
* family that mismatch becomes a REPLACE, and a replace of a mon is the quorum loss in the
|
|
88
88
|
* header. Left out of `matches` on purpose.
|
|
@@ -92,7 +92,7 @@ export interface CephDaemonProps extends WithTarget {
|
|
|
92
92
|
* mds only. Makes this standby replay the active MDS's journal for a faster failover.
|
|
93
93
|
* ⛔ CREATE-ONLY AND NEVER COMPARED, AND THE FIELD THAT LOOKS LIKE ITS READBACK IS NOT ONE.
|
|
94
94
|
* `standby_replay` in the list is the daemon's CURRENT state, not its configuration: MEASURED
|
|
95
|
-
* 2026-09-13, all three mds report `standby_replay: false`,
|
|
95
|
+
* 2026-09-13, all three mds report `standby_replay: false`, node-c included — and node-c is the ACTIVE
|
|
96
96
|
* mds, which is not a standby at all and so can never report true however it was created.
|
|
97
97
|
* Comparing `hotstandby` to it would plan a replace against whichever mds Ceph happens to have
|
|
98
98
|
* elected, i.e. against a value no declaration controls.
|
|
@@ -121,22 +121,22 @@ export interface CephDaemonAttributes {
|
|
|
121
121
|
host: string;
|
|
122
122
|
/**
|
|
123
123
|
* mon `running`/`stopped`/`unknown`, mgr `active`/`standby`, mds `up:active`/`up:standby`/…
|
|
124
|
-
* ⛔ CEPH ELECTS THIS AND REWRITES IT WITHOUT ANYONE DECLARING ANYTHING. MEASURED: mgr
|
|
125
|
-
* `active` while
|
|
124
|
+
* ⛔ CEPH ELECTS THIS AND REWRITES IT WITHOUT ANYONE DECLARING ANYTHING. MEASURED: mgr node-b is
|
|
125
|
+
* `active` while node-c and node-d are `standby`; mds node-c is `up:active` while node-b and node-d are
|
|
126
126
|
* `up:standby`. Restart a daemon and the roles move. It is reported so a plan can show what is
|
|
127
127
|
* live and kept out of `matches` so that an election is never a diff.
|
|
128
128
|
* ⚠️ IT ALSO MEANS THIS RESOURCE DOES NOT MANAGE WHETHER THE DAEMON IS RUNNING. A configured but
|
|
129
129
|
* stopped daemon is still a row in the list, so it reads as present and plans `noop`.
|
|
130
130
|
*/
|
|
131
131
|
state: string;
|
|
132
|
-
/** Ceph-formatted, e.g. `
|
|
132
|
+
/** Ceph-formatted, e.g. `198.51.100.12:6789/0`. Reported; see `mon-address` for why never compared. */
|
|
133
133
|
addr: string;
|
|
134
134
|
/** `ceph_version_short`, e.g. `20.2.2`. Reported: a straggler after an upgrade is worth seeing. */
|
|
135
135
|
version: string;
|
|
136
136
|
/**
|
|
137
137
|
* mon rank in the monmap, mds rank in the filesystem, `-1` when the daemon has neither.
|
|
138
|
-
* ⛔ CEPH ASSIGNS IT AND NOTHING DECLARES IT. MEASURED: the mons are ranked
|
|
139
|
-
* monmap order, not declaration order — and the mds ranks are 0 for the active
|
|
138
|
+
* ⛔ CEPH ASSIGNS IT AND NOTHING DECLARES IT. MEASURED: the mons are ranked node-d=0, node-c=1, node-b=2 —
|
|
139
|
+
* monmap order, not declaration order — and the mds ranks are 0 for the active node-c and -1 for
|
|
140
140
|
* the two standbys. Rank moves when a daemon is added, removed or restarted.
|
|
141
141
|
*/
|
|
142
142
|
rank: number;
|
|
@@ -145,8 +145,8 @@ export interface CephDaemonAttributes {
|
|
|
145
145
|
/** mds only, and NOT the readback of `hotstandby` — see the ⛔ on that prop. */
|
|
146
146
|
standbyReplay: boolean;
|
|
147
147
|
/**
|
|
148
|
-
* mds only: the CephFS this daemon currently serves — `cephfs-
|
|
149
|
-
* ⛔ PRESENT ONLY ON THE ACTIVE MDS. MEASURED:
|
|
148
|
+
* mds only: the CephFS this daemon currently serves — `cephfs-c1` here.
|
|
149
|
+
* ⛔ PRESENT ONLY ON THE ACTIVE MDS. MEASURED: node-c carries `fs_name`, node-b and node-d have no such key
|
|
150
150
|
* at all. So it is empty for two of three identical declarations, and comparing it would
|
|
151
151
|
* report work on exactly the daemons that are healthy standbys.
|
|
152
152
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceph-daemon.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEnD,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,
|
|
1
|
+
{"version":3,"file":"ceph-daemon.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEnD,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,uGAAuG;IACvG,IAAI,EAAE,MAAM,CAAC;IACb,mGAAmG;IACnG,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,MAAM,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,aAAa,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AA+CH,eAAO,MAAM,yBAAyB,uIAoBnC,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `Proxmox.CephFlag` — one cluster-wide Ceph OSD flag, declared: `noout`, `pause`, `norebalance`.
|
|
3
3
|
*
|
|
4
4
|
* ★ ONE RESOURCE PER FLAG, NOT ONE CARRYING ALL ELEVEN, AND THE CLUSTER'S OWN API DECIDED IT.
|
|
5
|
-
* MEASURED on
|
|
5
|
+
* MEASURED on C1 2026-09-13 (pve-manager 9.2.11, ceph tentacle 20.2.2) by reading
|
|
6
6
|
* `/usr/share/perl5/PVE/API2/Cluster/Ceph.pm` and the published schema in
|
|
7
7
|
* `/usr/share/pve-docs/api-viewer/apidoc.js`:
|
|
8
8
|
* · `PUT /cluster/ceph/flags` ends in `fork_worker('cephsetflags', ...)` and returns a STRING
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* defaulting to false would make `CephFlag('noout', { flag: 'noout', target })` mean "clear
|
|
31
31
|
* it" — the dangerous direction, chosen by OMISSION. Here the dangerous sentence has to be
|
|
32
32
|
* typed out by a person. Do not add a default, and do not declare a flag merely to document
|
|
33
|
-
* that it is off: MEASURED 2026-09-13, all eleven flags on
|
|
33
|
+
* that it is off: MEASURED 2026-09-13, all eleven flags on C1 read 0, so eleven
|
|
34
34
|
* `value: false` lines would plan green forever and do nothing except take the brake off
|
|
35
35
|
* whenever somebody happens to apply them.
|
|
36
36
|
*
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
* and plans `noop` straight past it. It cannot express a half-pause, so never use it to prove
|
|
59
59
|
* one is gone; `ceph osd dump | head -1` can.
|
|
60
60
|
* ⛔ `pause` AND `noup` ARE ALSO THE TWO THAT TAKE THE CLUSTER DOWN FROM A TYPO. `pause: true`
|
|
61
|
-
* stops all reads and writes, so every guest on `
|
|
62
|
-
* MEASURED 2026-09-13 — freezes on its root disk, and so does `cephfs-
|
|
61
|
+
* stops all reads and writes, so every guest on `rbd-c1` — the cluster's only rbd pool,
|
|
62
|
+
* MEASURED 2026-09-13 — freezes on its root disk, and so does `cephfs-c1`. `noup: true`
|
|
63
63
|
* stops a rebooted OSD from ever rejoining. Neither is undone by deleting the line.
|
|
64
64
|
*
|
|
65
65
|
* ⚠️ PRIVILEGES, AND THE WRITE ONE IS A BIG ASK. Both GETs check `Sys.Audit` on `/`; both PUTs
|
|
@@ -80,7 +80,7 @@ import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
|
80
80
|
*
|
|
81
81
|
* ⚠️ THE ENUM IS CLOSED AND A TYPO IS A 400, WHICH IS WHY THIS IS A UNION AND NOT `string`. Both
|
|
82
82
|
* the GET and the PUT declare `additionalProperties => 0` over exactly this list.
|
|
83
|
-
* ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on
|
|
83
|
+
* ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on C1:
|
|
84
84
|
* `ceph osd dump` reports `flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit`.
|
|
85
85
|
* PVE reads that same string and answers only about its own eleven, so the others can neither
|
|
86
86
|
* leak in here nor be set from here — `noautoscale` and `nosnaptrim` included.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* ⛔ POST AND DELETE ANSWER WITH A UPID, NOT A RESULT — THE WRITE IS ASYNCHRONOUS AND A FAILED ONE
|
|
18
18
|
* IS STILL HTTP 200. Read from the cluster's own source, /usr/share/perl5/PVE/API2/Ceph/FS.pm on
|
|
19
|
-
*
|
|
19
|
+
* node-b: `createfs` and `destroyfs` both end `return $rpcenv->fork_worker(...)`. Two different lies
|
|
20
20
|
* come out of trusting that status code. A read-back straight after the POST reports "still
|
|
21
21
|
* absent" about a filesystem that is being built as it says so — which is precisely the message
|
|
22
22
|
* `pveOperations.reconcile` would print, pointing at the wrong cause. And a worker that dies
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* has not been observed; only the `fork_worker` that guarantees it is there.
|
|
27
27
|
*
|
|
28
28
|
* ⛔ A DELETE BODY IS SILENTLY DISCARDED BY PVE, SO `remove-pools` GOES IN THE QUERY STRING.
|
|
29
|
-
* MEASURED in /usr/share/perl5/PVE/APIServer/AnyEvent.pm on
|
|
29
|
+
* MEASURED in /usr/share/perl5/PVE/APIServer/AnyEvent.pm on node-b: line 928 reads the request
|
|
30
30
|
* content into params only `if ($method eq 'PUT' || $method eq 'POST')`, and line 1653 sends
|
|
31
31
|
* every other method down a branch that parses `$request->url->query()` and nothing else. So
|
|
32
32
|
* `pve(target, role, 'DELETE', path, form)` would send a body PVE never looks at: the flags would
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* ⛔ THE POLLER IS NEVER THE TASK'S OWNER, WHICH COSTS A PRIVILEGE, AND THIS ONE IS MEASURED
|
|
42
42
|
* RATHER THAN FEARED. client.ts mints a fresh credential per call and the mount vends a NEW
|
|
43
43
|
* token id each time, so the token reading the status is never the token recorded in the UPID —
|
|
44
|
-
* and PVE compares them exactly. /usr/share/perl5/PVE/API2/Tasks.pm on
|
|
44
|
+
* and PVE compares them exactly. /usr/share/perl5/PVE/API2/Tasks.pm on node-b, `$check_task_user`:
|
|
45
45
|
* `return $user eq $fulltoken || $user eq $task->{user};`, above it the comment "token only sees
|
|
46
46
|
* token tasks, user sees user + token tasks". Token B of the same user matches neither branch.
|
|
47
47
|
* So the fallback is the schema's other clause for `GET /nodes/{node}/tasks/{upid}/status`: "The
|
|
@@ -75,8 +75,8 @@ export declare const createForm: (props: CephFsProps) => Record<string, string>;
|
|
|
75
75
|
*
|
|
76
76
|
* ⚠️ A CREATE NEEDS A RUNNING *AND* A STANDBY MDS, and refuses BEFORE it forks -- "no running
|
|
77
77
|
* Metadata Server (MDS) found!" / "no standby Metadata Server (MDS) found!" are synchronous, so
|
|
78
|
-
* they surface as a failed POST rather than as a silent worker.
|
|
79
|
-
* `up:active` for cephfs-
|
|
78
|
+
* they surface as a failed POST rather than as a silent worker. C1 has three (MEASURED: node-c
|
|
79
|
+
* `up:active` for cephfs-c1, node-b and node-d `up:standby`), so this is a note for a smaller cluster.
|
|
80
80
|
* ⚠️ BOTH WRITES CHECK `Sys.Modify` ON `/` -- the root, with the breadth metric-server.ts warns
|
|
81
81
|
* about: granting it buys datacenter options and every other cluster-wide config write too.
|
|
82
82
|
*/
|