@homeflare/alchemy 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +54 -10
  2. package/dist/caddy/admin-calls.d.ts +48 -0
  3. package/dist/caddy/admin-calls.d.ts.map +1 -0
  4. package/dist/caddy/admin-guard.d.ts +40 -0
  5. package/dist/caddy/admin-guard.d.ts.map +1 -0
  6. package/dist/caddy/admin.d.ts +73 -0
  7. package/dist/caddy/admin.d.ts.map +1 -0
  8. package/dist/caddy/caddyfile-tripwire.d.ts +5 -0
  9. package/dist/caddy/caddyfile-tripwire.d.ts.map +1 -0
  10. package/dist/caddy/config-form.d.ts +27 -0
  11. package/dist/caddy/config-form.d.ts.map +1 -0
  12. package/dist/caddy/config-lifecycle.d.ts +45 -0
  13. package/dist/caddy/config-lifecycle.d.ts.map +1 -0
  14. package/dist/caddy/config.d.ts +30 -0
  15. package/dist/caddy/config.d.ts.map +1 -0
  16. package/dist/caddy/digest.d.ts +7 -0
  17. package/dist/caddy/digest.d.ts.map +1 -0
  18. package/dist/caddy/fake-caddy.d.ts +43 -0
  19. package/dist/caddy/fake-caddy.d.ts.map +1 -0
  20. package/dist/caddy/index.d.ts +21 -0
  21. package/dist/caddy/index.d.ts.map +1 -0
  22. package/dist/caddy/index.js +912 -0
  23. package/dist/caddy/index.js.map +28 -0
  24. package/dist/caddy/local-admin.d.ts +45 -0
  25. package/dist/caddy/local-admin.d.ts.map +1 -0
  26. package/dist/caddy/providers.d.ts +14 -0
  27. package/dist/caddy/providers.d.ts.map +1 -0
  28. package/dist/caddy/with-file.d.ts +47 -0
  29. package/dist/caddy/with-file.d.ts.map +1 -0
  30. package/dist/cloudflare/fake-mesh.d.ts +58 -0
  31. package/dist/cloudflare/fake-mesh.d.ts.map +1 -0
  32. package/dist/cloudflare/index.d.ts +9 -3
  33. package/dist/cloudflare/index.d.ts.map +1 -1
  34. package/dist/cloudflare/index.js +218 -7
  35. package/dist/cloudflare/index.js.map +9 -4
  36. package/dist/cloudflare/mesh-node-api.d.ts +75 -0
  37. package/dist/cloudflare/mesh-node-api.d.ts.map +1 -0
  38. package/dist/cloudflare/mesh-node-form.d.ts +82 -0
  39. package/dist/cloudflare/mesh-node-form.d.ts.map +1 -0
  40. package/dist/cloudflare/mesh-node-harness.d.ts +46 -0
  41. package/dist/cloudflare/mesh-node-harness.d.ts.map +1 -0
  42. package/dist/cloudflare/mesh-node-lifecycle.d.ts +22 -0
  43. package/dist/cloudflare/mesh-node-lifecycle.d.ts.map +1 -0
  44. package/dist/cloudflare/mesh-node-token.d.ts +23 -0
  45. package/dist/cloudflare/mesh-node-token.d.ts.map +1 -0
  46. package/dist/cloudflare/mesh-node.d.ts +48 -0
  47. package/dist/cloudflare/mesh-node.d.ts.map +1 -0
  48. package/dist/cloudflare/providers.d.ts +1 -12
  49. package/dist/cloudflare/providers.d.ts.map +1 -1
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/launchd/index.js.map +2 -2
  53. package/dist/launchd/secret-tripwire.d.ts +1 -0
  54. package/dist/launchd/secret-tripwire.d.ts.map +1 -1
  55. package/dist/openbao/cloudflare-role.d.ts.map +1 -1
  56. package/dist/openbao/fake-engines.d.ts +30 -0
  57. package/dist/openbao/fake-engines.d.ts.map +1 -0
  58. package/dist/openbao/fake-stack.d.ts +29 -0
  59. package/dist/openbao/fake-stack.d.ts.map +1 -0
  60. package/dist/openbao/index.js +231 -151
  61. package/dist/openbao/index.js.map +9 -7
  62. package/dist/openbao/policy.d.ts +2 -0
  63. package/dist/openbao/policy.d.ts.map +1 -1
  64. package/dist/openbao/proxmox-role-form.d.ts +18 -8
  65. package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
  66. package/dist/openbao/proxmox-role-wire.d.ts +18 -0
  67. package/dist/openbao/proxmox-role-wire.d.ts.map +1 -0
  68. package/dist/openbao/proxmox-role.d.ts.map +1 -1
  69. package/dist/openbao/rename.d.ts +106 -0
  70. package/dist/openbao/rename.d.ts.map +1 -0
  71. package/docs/caddy.md +180 -0
  72. package/docs/mesh-node.md +188 -0
  73. package/package.json +7 -2
  74. package/src/caddy/admin-calls.ts +128 -0
  75. package/src/caddy/admin-guard.ts +170 -0
  76. package/src/caddy/admin.ts +74 -0
  77. package/src/caddy/caddyfile-tripwire.ts +133 -0
  78. package/src/caddy/config-form.ts +64 -0
  79. package/src/caddy/config-lifecycle.test.ts +218 -0
  80. package/src/caddy/config-lifecycle.ts +148 -0
  81. package/src/caddy/config.ts +116 -0
  82. package/src/caddy/digest.ts +44 -0
  83. package/src/caddy/fake-caddy.ts +160 -0
  84. package/src/caddy/guards.test.ts +156 -0
  85. package/src/caddy/index.ts +25 -0
  86. package/src/caddy/local-admin.test.ts +160 -0
  87. package/src/caddy/local-admin.ts +189 -0
  88. package/src/caddy/providers.test.ts +244 -0
  89. package/src/caddy/providers.ts +17 -0
  90. package/src/caddy/with-file.ts +62 -0
  91. package/src/cloudflare/fake-mesh.ts +181 -0
  92. package/src/cloudflare/index.ts +9 -3
  93. package/src/cloudflare/mesh-node-api.ts +127 -0
  94. package/src/cloudflare/mesh-node-edges.test.ts +103 -0
  95. package/src/cloudflare/mesh-node-form.test.ts +80 -0
  96. package/src/cloudflare/mesh-node-form.ts +115 -0
  97. package/src/cloudflare/mesh-node-harness.ts +124 -0
  98. package/src/cloudflare/mesh-node-lifecycle.ts +187 -0
  99. package/src/cloudflare/mesh-node-policy.test.ts +151 -0
  100. package/src/cloudflare/mesh-node-token.test.ts +117 -0
  101. package/src/cloudflare/mesh-node-token.ts +125 -0
  102. package/src/cloudflare/mesh-node.test.ts +172 -0
  103. package/src/cloudflare/mesh-node.ts +120 -0
  104. package/src/cloudflare/providers.ts +20 -2
  105. package/src/index.ts +1 -0
  106. package/src/launchd/secret-tripwire.ts +1 -1
  107. package/src/openbao/REPLACE.md +97 -21
  108. package/src/openbao/cloudflare-role-wire.test.ts +8 -34
  109. package/src/openbao/cloudflare-role.ts +30 -8
  110. package/src/openbao/fake-engines.ts +119 -0
  111. package/src/openbao/fake-stack.ts +112 -0
  112. package/src/openbao/policy-rename.test.ts +112 -0
  113. package/src/openbao/policy-wire.test.ts +7 -26
  114. package/src/openbao/policy.ts +27 -4
  115. package/src/openbao/proxmox-role-form.ts +27 -8
  116. package/src/openbao/proxmox-role-wire.ts +24 -0
  117. package/src/openbao/proxmox-role.ts +62 -37
  118. package/src/openbao/rename-occupied.test.ts +166 -0
  119. package/src/openbao/rename.ts +162 -0
  120. package/src/openbao/role-rename.test.ts +205 -0
@@ -804,7 +804,7 @@ var assertBaoIdentity = (input) => {
804
804
  import { Resource as Resource3 } from "alchemy";
805
805
  import { isResolved as isResolved3 } from "alchemy/Diff";
806
806
  import * as Provider3 from "alchemy/Provider";
807
- import * as Effect12 from "effect/Effect";
807
+ import * as Effect13 from "effect/Effect";
808
808
 
809
809
  // src/openbao/cloudflare-group-scope.ts
810
810
  var zoneResource = (zoneId) => `com.cloudflare.api.account.zone.${zoneId}`;
@@ -1184,13 +1184,46 @@ var permissionGroupsFromLiveRoles = (declared) => Effect11.gen(function* () {
1184
1184
  });
1185
1185
  });
1186
1186
 
1187
+ // src/openbao/rename.ts
1188
+ import * as Effect12 from "effect/Effect";
1189
+ var exact = (identity) => identity;
1190
+ var declaredString = (news, key) => {
1191
+ if (typeof news !== "object" || news === null)
1192
+ return;
1193
+ const value = news[key];
1194
+ return typeof value === "string" ? value : undefined;
1195
+ };
1196
+ var isPendingProp = (news, key) => {
1197
+ if (typeof news !== "object" || news === null)
1198
+ return false;
1199
+ const value = news[key];
1200
+ return value !== undefined && typeof value !== "string";
1201
+ };
1202
+ var declaredRolePath = (news, rolePath2) => {
1203
+ const mount = declaredString(news, "mount");
1204
+ const name = declaredString(news, "name");
1205
+ return mount === undefined || name === undefined ? undefined : rolePath2(mount, name);
1206
+ };
1207
+ var policyKey = (name) => name.trim().toLowerCase();
1208
+ var isMoved = (stored, declared, key = exact) => declared === undefined ? undefined : key(stored) !== key(declared);
1209
+ var triedRolePath = (output, olds, rolePath2) => output === undefined ? declaredRolePath(olds, rolePath2) : rolePath2(output.mount, output.name);
1210
+ var judgeMove = (family, tried, declared, pathOf, key = exact) => Effect12.gen(function* () {
1211
+ if (tried === undefined || declared === undefined || key(tried) === key(declared)) {
1212
+ return;
1213
+ }
1214
+ if ((yield* baoRead(pathOf(declared))) === undefined)
1215
+ return { from: tried, to: declared };
1216
+ return yield* Effect12.die(new Error(`${family}: ${tried} → ${declared} would land on an object that already exists at ` + `${declared}. Under RemovalPolicy.destroy() a resource moving off that name (a swap, a ` + "shift, or a reverted move that did not finish) deletes it after this one writes it. " + "Nothing was written. Move through a name nothing holds, in two deploys, or remove " + `${declared} by hand first.`));
1217
+ });
1218
+ var refuseMovedUpdate = (family, from, to) => Effect12.die(new Error(`${family}: planned as an update, but its identity moved from ${from} to ${to}. Writing ` + `${to} now would leave ${from} live with no state record. Nothing was written. Run the ` + "deploy again: once the value resolves at plan time it plans as a replace."));
1219
+
1187
1220
  // src/openbao/cloudflare-role.ts
1188
1221
  var BaoCloudflareRole = Resource3("Bao.CloudflareRole", {
1189
1222
  defaultRemovalPolicy: "retain"
1190
1223
  });
1191
- var BaoCloudflareRoleProvider = () => Provider3.effect(BaoCloudflareRole, Effect12.gen(function* () {
1224
+ var BaoCloudflareRoleProvider = () => Provider3.effect(BaoCloudflareRole, Effect13.gen(function* () {
1192
1225
  const groups = yield* CloudflarePermissionGroups;
1193
- const resolved = (props) => Effect12.gen(function* () {
1226
+ const resolved = (props) => Effect13.gen(function* () {
1194
1227
  const refs = new Map;
1195
1228
  for (const policy of props.policies) {
1196
1229
  const scope = policy.scope ?? scopeOfResource(policy.resource);
@@ -1200,18 +1233,25 @@ var BaoCloudflareRoleProvider = () => Provider3.effect(BaoCloudflareRole, Effect
1200
1233
  const ids = yield* groups.resolve(props.mount, [...refs.values()]);
1201
1234
  const { missing, policies } = resolvePolicies(props.policies, ids);
1202
1235
  if (missing.length > 0) {
1203
- return yield* Effect12.die(new Error(`Bao.CloudflareRole ${rolePath(props.mount, props.name)}: groups ` + `${missing.join(", ")} came back unresolved from a lookup that reported success.`));
1236
+ return yield* Effect13.die(new Error(`Bao.CloudflareRole ${rolePath(props.mount, props.name)}: groups ` + `${missing.join(", ")} came back unresolved from a lookup that reported success.`));
1204
1237
  }
1205
1238
  return policies;
1206
1239
  });
1207
1240
  return BaoCloudflareRole.Provider.of({
1208
- list: () => Effect12.succeed([]),
1209
- read: Effect12.fn(function* ({ olds }) {
1241
+ list: () => Effect13.succeed([]),
1242
+ read: Effect13.fn(function* ({ olds }) {
1210
1243
  const live = yield* readCloudflareRole(olds.mount, olds.name);
1211
1244
  return live === undefined ? undefined : attributesOf4(olds, live);
1212
1245
  }),
1213
- diff: Effect12.fn(function* ({ news, output }) {
1214
- if (output === undefined || !isResolved3(news))
1246
+ diff: Effect13.fn(function* ({ news, olds, output }) {
1247
+ const tried = triedRolePath(output, olds, rolePath);
1248
+ const declared = declaredRolePath(news, rolePath);
1249
+ const move = yield* judgeMove("Bao.CloudflareRole", tried, declared, (path) => path);
1250
+ if (output === undefined)
1251
+ return;
1252
+ if (move !== undefined)
1253
+ return { action: "replace" };
1254
+ if (!isResolved3(news))
1215
1255
  return;
1216
1256
  if (refusalOf(news) !== undefined)
1217
1257
  return { action: "update" };
@@ -1221,11 +1261,15 @@ var BaoCloudflareRoleProvider = () => Provider3.effect(BaoCloudflareRole, Effect
1221
1261
  return { action: "update" };
1222
1262
  return differences(news, policies, live).length === 0 ? { action: "noop" } : { action: "update" };
1223
1263
  }),
1224
- reconcile: Effect12.fn(function* ({ news }) {
1264
+ reconcile: Effect13.fn(function* ({ news, output }) {
1225
1265
  const path = rolePath(news.mount, news.name);
1266
+ const before = output === undefined ? path : rolePath(output.mount, output.name);
1267
+ if (isMoved(before, path) === true) {
1268
+ return yield* refuseMovedUpdate("Bao.CloudflareRole", before, path);
1269
+ }
1226
1270
  const refusal2 = refusalOf(news);
1227
1271
  if (refusal2 !== undefined) {
1228
- return yield* Effect12.die(new Error(`Bao.CloudflareRole ${path}: ${refusal2}.`));
1272
+ return yield* Effect13.die(new Error(`Bao.CloudflareRole ${path}: ${refusal2}.`));
1229
1273
  }
1230
1274
  const policies = yield* resolved(news);
1231
1275
  const live = yield* readCloudflareRole(news.mount, news.name);
@@ -1234,15 +1278,15 @@ var BaoCloudflareRoleProvider = () => Provider3.effect(BaoCloudflareRole, Effect
1234
1278
  }
1235
1279
  const after = yield* readCloudflareRole(news.mount, news.name);
1236
1280
  if (after === undefined) {
1237
- return yield* Effect12.die(new Error(`Bao.CloudflareRole ${path}: the write succeeded but the role is absent.`));
1281
+ return yield* Effect13.die(new Error(`Bao.CloudflareRole ${path}: the write succeeded but the role is absent.`));
1238
1282
  }
1239
1283
  const left = differences(news, policies, after);
1240
1284
  if (left.length > 0) {
1241
- return yield* Effect12.die(new Error(`Bao.CloudflareRole ${path}: wrote cleanly but reads back different in ` + `${left.map((d) => d.field).join(", ")}. Inspect the role before retrying.`));
1285
+ return yield* Effect13.die(new Error(`Bao.CloudflareRole ${path}: wrote cleanly but reads back different in ` + `${left.map((d) => d.field).join(", ")}. Inspect the role before retrying.`));
1242
1286
  }
1243
1287
  return attributesOf4(news, after);
1244
1288
  }),
1245
- delete: Effect12.fn(function* ({ output }) {
1289
+ delete: Effect13.fn(function* ({ output }) {
1246
1290
  yield* deleteCloudflareRole(output.mount, output.name);
1247
1291
  return;
1248
1292
  })
@@ -1492,7 +1536,7 @@ var hostAppRoles = (input) => {
1492
1536
  import { Resource as Resource4 } from "alchemy";
1493
1537
  import { isResolved as isResolved4 } from "alchemy/Diff";
1494
1538
  import * as Provider4 from "alchemy/Provider";
1495
- import * as Effect14 from "effect/Effect";
1539
+ import * as Effect15 from "effect/Effect";
1496
1540
 
1497
1541
  // src/openbao/jwt-config-form.ts
1498
1542
  var text5 = (value) => typeof value === "string" ? value.trim() : "";
@@ -1582,16 +1626,16 @@ var problems = (props) => {
1582
1626
  };
1583
1627
 
1584
1628
  // src/openbao/role-reconcile.ts
1585
- import * as Effect13 from "effect/Effect";
1586
- var readRoleAt = (spec) => Effect13.map(baoRead(spec.path), (live) => live === undefined ? undefined : { attributes: spec.attributesOf(live), live });
1587
- var planRole = (spec) => Effect13.gen(function* () {
1629
+ import * as Effect14 from "effect/Effect";
1630
+ var readRoleAt = (spec) => Effect14.map(baoRead(spec.path), (live) => live === undefined ? undefined : { attributes: spec.attributesOf(live), live });
1631
+ var planRole = (spec) => Effect14.gen(function* () {
1588
1632
  if (spec.problems.length > 0)
1589
1633
  return "update";
1590
1634
  const found = yield* readRoleAt(spec);
1591
1635
  return found !== undefined && spec.matches(found.attributes) ? "noop" : "update";
1592
1636
  });
1593
- var refuse2 = (spec, message) => Effect13.die(new Error(`${spec.family} ${spec.path}: ${message}`));
1594
- var reconcileRole = (spec) => Effect13.gen(function* () {
1637
+ var refuse2 = (spec, message) => Effect14.die(new Error(`${spec.family} ${spec.path}: ${message}`));
1638
+ var reconcileRole = (spec) => Effect14.gen(function* () {
1595
1639
  if (spec.problems.length > 0)
1596
1640
  return yield* refuse2(spec, spec.problems.join("; "));
1597
1641
  const found = yield* readRoleAt(spec);
@@ -1624,29 +1668,29 @@ var jwtConfigSpec = (props) => ({
1624
1668
  problems: problems(props),
1625
1669
  wouldErase
1626
1670
  });
1627
- var BaoJwtAuthConfigProvider = () => Provider4.effect(BaoJwtAuthConfig, Effect14.succeed(BaoJwtAuthConfig.Provider.of({
1628
- list: () => Effect14.succeed([]),
1629
- read: Effect14.fn(function* ({ olds }) {
1671
+ var BaoJwtAuthConfigProvider = () => Provider4.effect(BaoJwtAuthConfig, Effect15.succeed(BaoJwtAuthConfig.Provider.of({
1672
+ list: () => Effect15.succeed([]),
1673
+ read: Effect15.fn(function* ({ olds }) {
1630
1674
  const found = yield* readRoleAt(jwtConfigSpec(olds));
1631
1675
  return found?.attributes;
1632
1676
  }),
1633
- diff: Effect14.fn(function* ({ news, output }) {
1677
+ diff: Effect15.fn(function* ({ news, output }) {
1634
1678
  if (output === undefined || !isResolved4(news))
1635
1679
  return;
1636
1680
  if (mountOf(news) !== output.mount)
1637
1681
  return { action: "replace" };
1638
1682
  return { action: yield* planRole(jwtConfigSpec(news)) };
1639
1683
  }),
1640
- reconcile: Effect14.fn(function* ({ news }) {
1684
+ reconcile: Effect15.fn(function* ({ news }) {
1641
1685
  return yield* reconcileRole(jwtConfigSpec(news));
1642
1686
  }),
1643
- delete: () => Effect14.void
1687
+ delete: () => Effect15.void
1644
1688
  })));
1645
1689
  // src/openbao/jwt-role.ts
1646
1690
  import { Resource as Resource5 } from "alchemy";
1647
1691
  import { isResolved as isResolved5 } from "alchemy/Diff";
1648
1692
  import * as Provider5 from "alchemy/Provider";
1649
- import * as Effect15 from "effect/Effect";
1693
+ import * as Effect16 from "effect/Effect";
1650
1694
 
1651
1695
  // src/openbao/auth-token-form.ts
1652
1696
  var sortedSet2 = (values) => [...new Set(values)].sort();
@@ -1856,13 +1900,13 @@ var jwtRoleSpec = (props) => ({
1856
1900
  path: rolePath2(props),
1857
1901
  problems: problems2(props)
1858
1902
  });
1859
- var BaoJwtRoleProvider = () => Provider5.effect(BaoJwtRole, Effect15.succeed(BaoJwtRole.Provider.of({
1860
- list: () => Effect15.succeed([]),
1861
- read: Effect15.fn(function* ({ olds }) {
1903
+ var BaoJwtRoleProvider = () => Provider5.effect(BaoJwtRole, Effect16.succeed(BaoJwtRole.Provider.of({
1904
+ list: () => Effect16.succeed([]),
1905
+ read: Effect16.fn(function* ({ olds }) {
1862
1906
  const found = yield* readRoleAt(jwtRoleSpec(olds));
1863
1907
  return found?.attributes;
1864
1908
  }),
1865
- diff: Effect15.fn(function* ({ news, output }) {
1909
+ diff: Effect16.fn(function* ({ news, output }) {
1866
1910
  if (output === undefined || !isResolved5(news))
1867
1911
  return;
1868
1912
  if (mountOf2(news) !== output.mount || news.name !== output.name) {
@@ -1870,10 +1914,10 @@ var BaoJwtRoleProvider = () => Provider5.effect(BaoJwtRole, Effect15.succeed(Bao
1870
1914
  }
1871
1915
  return { action: yield* planRole(jwtRoleSpec(news)) };
1872
1916
  }),
1873
- reconcile: Effect15.fn(function* ({ news }) {
1917
+ reconcile: Effect16.fn(function* ({ news }) {
1874
1918
  return yield* reconcileRole(jwtRoleSpec(news));
1875
1919
  }),
1876
- delete: Effect15.fn(function* ({ output }) {
1920
+ delete: Effect16.fn(function* ({ output }) {
1877
1921
  yield* baoDelete(rolePath2(output));
1878
1922
  return;
1879
1923
  })
@@ -1882,7 +1926,7 @@ var BaoJwtRoleProvider = () => Provider5.effect(BaoJwtRole, Effect15.succeed(Bao
1882
1926
  import { Resource as Resource6 } from "alchemy";
1883
1927
  import { isResolved as isResolved6 } from "alchemy/Diff";
1884
1928
  import * as Provider6 from "alchemy/Provider";
1885
- import * as Effect16 from "effect/Effect";
1929
+ import * as Effect17 from "effect/Effect";
1886
1930
 
1887
1931
  // src/openbao/kubernetes-role-form.ts
1888
1932
  var sortedSet4 = (values) => [...new Set(values)].sort();
@@ -1959,13 +2003,13 @@ var kubernetesRoleSpec = (props) => ({
1959
2003
  path: rolePath3(props),
1960
2004
  problems: problems3(props)
1961
2005
  });
1962
- var BaoKubernetesRoleProvider = () => Provider6.effect(BaoKubernetesRole, Effect16.succeed(BaoKubernetesRole.Provider.of({
1963
- list: () => Effect16.succeed([]),
1964
- read: Effect16.fn(function* ({ olds }) {
2006
+ var BaoKubernetesRoleProvider = () => Provider6.effect(BaoKubernetesRole, Effect17.succeed(BaoKubernetesRole.Provider.of({
2007
+ list: () => Effect17.succeed([]),
2008
+ read: Effect17.fn(function* ({ olds }) {
1965
2009
  const found = yield* readRoleAt(kubernetesRoleSpec(olds));
1966
2010
  return found?.attributes;
1967
2011
  }),
1968
- diff: Effect16.fn(function* ({ news, output }) {
2012
+ diff: Effect17.fn(function* ({ news, output }) {
1969
2013
  if (output === undefined || !isResolved6(news))
1970
2014
  return;
1971
2015
  if (mountOf3(news) !== output.mount || news.name !== output.name) {
@@ -1973,10 +2017,10 @@ var BaoKubernetesRoleProvider = () => Provider6.effect(BaoKubernetesRole, Effect
1973
2017
  }
1974
2018
  return { action: yield* planRole(kubernetesRoleSpec(news)) };
1975
2019
  }),
1976
- reconcile: Effect16.fn(function* ({ news }) {
2020
+ reconcile: Effect17.fn(function* ({ news }) {
1977
2021
  return yield* reconcileRole(kubernetesRoleSpec(news));
1978
2022
  }),
1979
- delete: Effect16.fn(function* ({ output }) {
2023
+ delete: Effect17.fn(function* ({ output }) {
1980
2024
  yield* baoDelete(rolePath3(output));
1981
2025
  return;
1982
2026
  })
@@ -1985,7 +2029,7 @@ var BaoKubernetesRoleProvider = () => Provider6.effect(BaoKubernetesRole, Effect
1985
2029
  import { Resource as Resource7 } from "alchemy";
1986
2030
  import { isResolved as isResolved7 } from "alchemy/Diff";
1987
2031
  import * as Provider7 from "alchemy/Provider";
1988
- import * as Effect18 from "effect/Effect";
2032
+ import * as Effect19 from "effect/Effect";
1989
2033
 
1990
2034
  // src/openbao/mfa-enforcement-form.ts
1991
2035
  var sortedSet5 = (values) => [...new Set(values.map((value) => value.trim()))].filter((value) => value !== "").sort();
@@ -2053,16 +2097,16 @@ var problems4 = (props, missingPaths) => {
2053
2097
  };
2054
2098
 
2055
2099
  // src/openbao/mfa-wire.ts
2056
- import * as Effect17 from "effect/Effect";
2100
+ import * as Effect18 from "effect/Effect";
2057
2101
  var TOTP_PATH = "identity/mfa/method/totp";
2058
2102
  var METHODS_PATH = "identity/mfa/method";
2059
2103
  var enforcementPath = (name) => `identity/mfa/login-enforcement/${name}`;
2060
2104
  var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
2061
- var ownNamespacePath = Effect17.gen(function* () {
2105
+ var ownNamespacePath = Effect18.gen(function* () {
2062
2106
  const env = yield* BaoEnv;
2063
2107
  return namespacePath(envNamespace(env));
2064
2108
  });
2065
- var findTotp = (name) => Effect17.gen(function* () {
2109
+ var findTotp = (name) => Effect18.gen(function* () {
2066
2110
  const listing = yield* baoRead(`${METHODS_PATH}?list=true`);
2067
2111
  const info = record4(listing?.["key_info"]) ?? {};
2068
2112
  const own = yield* ownNamespacePath;
@@ -2071,17 +2115,17 @@ var findTotp = (name) => Effect17.gen(function* () {
2071
2115
  if (live === undefined || live["name"] !== name || live["namespace_path"] !== own)
2072
2116
  continue;
2073
2117
  if (live["type"] !== "totp") {
2074
- return yield* Effect17.die(new Error(`Bao.MfaTotpMethod ${name}: that name belongs to a ${String(live["type"])} method ` + `(${id}) in this namespace, and a TOTP write would convert it. Pick another name.`));
2118
+ return yield* Effect18.die(new Error(`Bao.MfaTotpMethod ${name}: that name belongs to a ${String(live["type"])} method ` + `(${id}) in this namespace, and a TOTP write would convert it. Pick another name.`));
2075
2119
  }
2076
2120
  return { id, live };
2077
2121
  }
2078
2122
  return;
2079
2123
  });
2080
- var writeTotp = (body) => Effect17.asVoid(baoCall("write", "POST", TOTP_PATH, body));
2124
+ var writeTotp = (body) => Effect18.asVoid(baoCall("write", "POST", TOTP_PATH, body));
2081
2125
  var deleteTotp = (id) => baoDelete(`${TOTP_PATH}/${id}`);
2082
2126
  var readEnforcement = (name) => baoRead(enforcementPath(name));
2083
- var writeEnforcement = (name, body) => Effect17.asVoid(baoCall("write", "POST", enforcementPath(name), body));
2084
- var authAccessors = (paths) => Effect17.gen(function* () {
2127
+ var writeEnforcement = (name, body) => Effect18.asVoid(baoCall("write", "POST", enforcementPath(name), body));
2128
+ var authAccessors = (paths) => Effect18.gen(function* () {
2085
2129
  if (paths.length === 0)
2086
2130
  return { accessors: [], missing: [] };
2087
2131
  const table = (yield* baoRead("sys/auth")) ?? {};
@@ -2101,20 +2145,20 @@ var authAccessors = (paths) => Effect17.gen(function* () {
2101
2145
  var BaoMfaLoginEnforcement = Resource7("Bao.MfaLoginEnforcement", {
2102
2146
  defaultRemovalPolicy: "retain"
2103
2147
  });
2104
- var refuse3 = (name, message) => Effect18.die(new Error(`Bao.MfaLoginEnforcement ${name}: ${message}`));
2105
- var resolveTargets = (props) => Effect18.map(authAccessors(props.authMethodPaths ?? []), ({ accessors, missing }) => ({
2148
+ var refuse3 = (name, message) => Effect19.die(new Error(`Bao.MfaLoginEnforcement ${name}: ${message}`));
2149
+ var resolveTargets = (props) => Effect19.map(authAccessors(props.authMethodPaths ?? []), ({ accessors, missing }) => ({
2106
2150
  bad: problems4(props, missing),
2107
2151
  resolved: accessors
2108
2152
  }));
2109
- var readLive = (name) => Effect18.map(readEnforcement(name), (live) => live === undefined ? undefined : attributesOf8(name, live));
2110
- var planEnforcement = (props) => Effect18.gen(function* () {
2153
+ var readLive = (name) => Effect19.map(readEnforcement(name), (live) => live === undefined ? undefined : attributesOf8(name, live));
2154
+ var planEnforcement = (props) => Effect19.gen(function* () {
2111
2155
  const { bad, resolved } = yield* resolveTargets(props);
2112
2156
  if (bad.length > 0)
2113
2157
  return "update";
2114
2158
  const live = yield* readLive(props.name);
2115
2159
  return live !== undefined && matches7(live, props, resolved) ? "noop" : "update";
2116
2160
  });
2117
- var reconcileEnforcement = (props) => Effect18.gen(function* () {
2161
+ var reconcileEnforcement = (props) => Effect19.gen(function* () {
2118
2162
  const { bad, resolved } = yield* resolveTargets(props);
2119
2163
  if (bad.length > 0)
2120
2164
  return yield* refuse3(props.name, bad.join("; "));
@@ -2130,22 +2174,22 @@ var reconcileEnforcement = (props) => Effect18.gen(function* () {
2130
2174
  }
2131
2175
  return after;
2132
2176
  });
2133
- var BaoMfaLoginEnforcementProvider = () => Provider7.effect(BaoMfaLoginEnforcement, Effect18.succeed(BaoMfaLoginEnforcement.Provider.of({
2134
- list: () => Effect18.succeed([]),
2135
- read: Effect18.fn(function* ({ olds }) {
2177
+ var BaoMfaLoginEnforcementProvider = () => Provider7.effect(BaoMfaLoginEnforcement, Effect19.succeed(BaoMfaLoginEnforcement.Provider.of({
2178
+ list: () => Effect19.succeed([]),
2179
+ read: Effect19.fn(function* ({ olds }) {
2136
2180
  return yield* readLive(olds.name);
2137
2181
  }),
2138
- diff: Effect18.fn(function* ({ news, output }) {
2182
+ diff: Effect19.fn(function* ({ news, output }) {
2139
2183
  if (output === undefined || !isResolved7(news))
2140
2184
  return;
2141
2185
  if (news.name !== output.name)
2142
2186
  return { action: "replace" };
2143
2187
  return { action: yield* planEnforcement(news) };
2144
2188
  }),
2145
- reconcile: Effect18.fn(function* ({ news }) {
2189
+ reconcile: Effect19.fn(function* ({ news }) {
2146
2190
  return yield* reconcileEnforcement(news);
2147
2191
  }),
2148
- delete: Effect18.fn(function* ({ output }) {
2192
+ delete: Effect19.fn(function* ({ output }) {
2149
2193
  return yield* refuse3(output.name, "REFUSING to delete: OpenBao 2.6.2 drops a login enforcement from memory only, and it " + "returns on the next unseal (openbao/openbao#4030). Set this resource back to " + "retain to drop it from state; remove it for good only once the server is fixed.");
2150
2194
  })
2151
2195
  })));
@@ -2153,7 +2197,7 @@ var BaoMfaLoginEnforcementProvider = () => Provider7.effect(BaoMfaLoginEnforceme
2153
2197
  import { Resource as Resource8 } from "alchemy";
2154
2198
  import { isResolved as isResolved8 } from "alchemy/Diff";
2155
2199
  import * as Provider8 from "alchemy/Provider";
2156
- import * as Effect19 from "effect/Effect";
2200
+ import * as Effect20 from "effect/Effect";
2157
2201
 
2158
2202
  // src/openbao/mfa-totp-form.ts
2159
2203
  var canonical6 = (input) => ({
@@ -2234,15 +2278,15 @@ var problems5 = (props) => {
2234
2278
  var BaoMfaTotpMethod = Resource8("Bao.MfaTotpMethod", {
2235
2279
  defaultRemovalPolicy: "retain"
2236
2280
  });
2237
- var refuse4 = (props, message) => Effect19.die(new Error(`Bao.MfaTotpMethod ${props.name}: ${message}`));
2238
- var readTotp = (props) => Effect19.map(findTotp(props.name), (found) => found === undefined ? undefined : attributesOf9(found.id, found.live));
2239
- var planTotp = (props) => Effect19.gen(function* () {
2281
+ var refuse4 = (props, message) => Effect20.die(new Error(`Bao.MfaTotpMethod ${props.name}: ${message}`));
2282
+ var readTotp = (props) => Effect20.map(findTotp(props.name), (found) => found === undefined ? undefined : attributesOf9(found.id, found.live));
2283
+ var planTotp = (props) => Effect20.gen(function* () {
2240
2284
  if (problems5(props).length > 0)
2241
2285
  return "update";
2242
2286
  const live = yield* readTotp(props);
2243
2287
  return live !== undefined && matches8(live, props) ? "noop" : "update";
2244
2288
  });
2245
- var reconcileTotp = (props) => Effect19.gen(function* () {
2289
+ var reconcileTotp = (props) => Effect20.gen(function* () {
2246
2290
  const bad = problems5(props);
2247
2291
  if (bad.length > 0)
2248
2292
  return yield* refuse4(props, bad.join("; "));
@@ -2256,12 +2300,12 @@ var reconcileTotp = (props) => Effect19.gen(function* () {
2256
2300
  return yield* refuse4(props, "the method read back different.");
2257
2301
  return after;
2258
2302
  });
2259
- var BaoMfaTotpMethodProvider = () => Provider8.effect(BaoMfaTotpMethod, Effect19.succeed(BaoMfaTotpMethod.Provider.of({
2260
- list: () => Effect19.succeed([]),
2261
- read: Effect19.fn(function* ({ olds }) {
2303
+ var BaoMfaTotpMethodProvider = () => Provider8.effect(BaoMfaTotpMethod, Effect20.succeed(BaoMfaTotpMethod.Provider.of({
2304
+ list: () => Effect20.succeed([]),
2305
+ read: Effect20.fn(function* ({ olds }) {
2262
2306
  return yield* readTotp(olds);
2263
2307
  }),
2264
- diff: Effect19.fn(function* ({ news, output }) {
2308
+ diff: Effect20.fn(function* ({ news, output }) {
2265
2309
  if (output === undefined || !isResolved8(news))
2266
2310
  return;
2267
2311
  const rename = renameProblem(output.name, news.name);
@@ -2269,10 +2313,10 @@ var BaoMfaTotpMethodProvider = () => Provider8.effect(BaoMfaTotpMethod, Effect19
2269
2313
  return yield* refuse4(news, rename);
2270
2314
  return { action: yield* planTotp(news) };
2271
2315
  }),
2272
- reconcile: Effect19.fn(function* ({ news }) {
2316
+ reconcile: Effect20.fn(function* ({ news }) {
2273
2317
  return yield* reconcileTotp(news);
2274
2318
  }),
2275
- delete: Effect19.fn(function* ({ output }) {
2319
+ delete: Effect20.fn(function* ({ output }) {
2276
2320
  yield* deleteTotp(output.methodId);
2277
2321
  return;
2278
2322
  })
@@ -2281,24 +2325,24 @@ var BaoMfaTotpMethodProvider = () => Provider8.effect(BaoMfaTotpMethod, Effect19
2281
2325
  import { Resource as Resource9 } from "alchemy";
2282
2326
  import { isResolved as isResolved9 } from "alchemy/Diff";
2283
2327
  import * as Provider9 from "alchemy/Provider";
2284
- import * as Effect22 from "effect/Effect";
2328
+ import * as Effect23 from "effect/Effect";
2285
2329
 
2286
2330
  // src/openbao/mount-reconcile.ts
2287
- import * as Effect21 from "effect/Effect";
2331
+ import * as Effect22 from "effect/Effect";
2288
2332
 
2289
2333
  // src/openbao/mount-wire.ts
2290
- import * as Effect20 from "effect/Effect";
2291
- var absentFromTable2 = (path, asked) => baoRead("sys/mounts").pipe(Effect20.mapError((listing) => new BaoError(asked.status, asked.method, asked.path, [
2334
+ import * as Effect21 from "effect/Effect";
2335
+ var absentFromTable2 = (path, asked) => baoRead("sys/mounts").pipe(Effect21.mapError((listing) => new BaoError(asked.status, asked.method, asked.path, [
2292
2336
  ...asked.errors,
2293
2337
  `sys/mounts could not confirm the mount is absent: ${listing.message}`
2294
- ])), Effect20.flatMap((table) => table !== undefined && !Object.hasOwn(table, `${mountPath(path)}/`) ? Effect20.succeed(undefined) : Effect20.fail(asked)));
2295
- var readMountData = (path) => baoRead(readPath(path)).pipe(Effect20.catchIf((error) => error.status === 400, (error) => absentFromTable2(path, error)));
2338
+ ])), Effect21.flatMap((table) => table !== undefined && !Object.hasOwn(table, `${mountPath(path)}/`) ? Effect21.succeed(undefined) : Effect21.fail(asked)));
2339
+ var readMountData = (path) => baoRead(readPath(path)).pipe(Effect21.catchIf((error) => error.status === 400, (error) => absentFromTable2(path, error)));
2296
2340
  var enableMount = (props) => baoWrite("POST", readPath(props.path), enableBody(props));
2297
2341
  var tuneMount = (props) => baoWrite("POST", tunePath(props.path), tuneBody(props));
2298
2342
  var disableMount = (path) => baoDelete(readPath(path));
2299
2343
 
2300
2344
  // src/openbao/mount-reconcile.ts
2301
- var readMount = (props) => Effect21.gen(function* () {
2345
+ var readMount = (props) => Effect22.gen(function* () {
2302
2346
  const live = yield* readMountData(props.path);
2303
2347
  if (live === undefined)
2304
2348
  return;
@@ -2311,10 +2355,10 @@ var moveTarget2 = (props) => ({
2311
2355
  type: props.type,
2312
2356
  wire: mountPath
2313
2357
  });
2314
- var reconcileMount = (news, stated, options) => Effect21.gen(function* () {
2358
+ var reconcileMount = (news, stated, options) => Effect22.gen(function* () {
2315
2359
  const move = planMove("Bao.Mount", stated, news.path, news.remountFrom);
2316
2360
  if (move.kind === "refuse")
2317
- return yield* Effect21.die(new Error(move.message));
2361
+ return yield* Effect22.die(new Error(move.message));
2318
2362
  if (move.kind === "move")
2319
2363
  yield* performMove(moveTarget2(news), move.from, options);
2320
2364
  let live = yield* readMount(news);
@@ -2324,13 +2368,13 @@ var reconcileMount = (news, stated, options) => Effect21.gen(function* () {
2324
2368
  yield* tuneMount(news);
2325
2369
  } else if (!matches(live, news)) {
2326
2370
  if (live.type !== news.type) {
2327
- return yield* Effect21.die(new Error(`Bao.Mount ${mountPath(news.path)}: live type ${live.type} != ${news.type}. ` + "Secrets engine type is immutable — replace manually."));
2371
+ return yield* Effect22.die(new Error(`Bao.Mount ${mountPath(news.path)}: live type ${live.type} != ${news.type}. ` + "Secrets engine type is immutable — replace manually."));
2328
2372
  }
2329
2373
  yield* tuneMount(news);
2330
2374
  }
2331
2375
  live = yield* readMount(news);
2332
2376
  if (live === undefined) {
2333
- return yield* Effect21.die(new Error(`Bao.Mount ${mountPath(news.path)}: write returned no error but the mount is still absent.`));
2377
+ return yield* Effect22.die(new Error(`Bao.Mount ${mountPath(news.path)}: write returned no error but the mount is still absent.`));
2334
2378
  }
2335
2379
  return live;
2336
2380
  });
@@ -2339,17 +2383,17 @@ var reconcileMount = (news, stated, options) => Effect21.gen(function* () {
2339
2383
  var BaoMount = Resource9("Bao.Mount", {
2340
2384
  defaultRemovalPolicy: "retain"
2341
2385
  });
2342
- var BaoMountProvider = () => Provider9.effect(BaoMount, Effect22.succeed(BaoMount.Provider.of({
2343
- list: () => Effect22.succeed([]),
2344
- read: Effect22.fn(function* ({ olds }) {
2386
+ var BaoMountProvider = () => Provider9.effect(BaoMount, Effect23.succeed(BaoMount.Provider.of({
2387
+ list: () => Effect23.succeed([]),
2388
+ read: Effect23.fn(function* ({ olds }) {
2345
2389
  return yield* readMount(olds);
2346
2390
  }),
2347
- diff: Effect22.fn(function* ({ news, output }) {
2391
+ diff: Effect23.fn(function* ({ news, output }) {
2348
2392
  if (output === undefined || !isResolved9(news))
2349
2393
  return;
2350
2394
  const move = planMove("Bao.Mount", output.path, news.path, news.remountFrom);
2351
2395
  if (move.kind === "refuse")
2352
- return yield* Effect22.die(new Error(move.message));
2396
+ return yield* Effect23.die(new Error(move.message));
2353
2397
  if (move.kind === "move")
2354
2398
  return { action: "update" };
2355
2399
  const live = yield* readMount(news);
@@ -2361,10 +2405,10 @@ var BaoMountProvider = () => Provider9.effect(BaoMount, Effect22.succeed(BaoMoun
2361
2405
  return { action: "noop" };
2362
2406
  return { action: "update" };
2363
2407
  }),
2364
- reconcile: Effect22.fn(function* ({ news, output }) {
2408
+ reconcile: Effect23.fn(function* ({ news, output }) {
2365
2409
  return yield* reconcileMount(news, output?.path);
2366
2410
  }),
2367
- delete: Effect22.fn(function* ({ output }) {
2411
+ delete: Effect23.fn(function* ({ output }) {
2368
2412
  yield* disableMount(output.path);
2369
2413
  return;
2370
2414
  })
@@ -2373,7 +2417,7 @@ var BaoMountProvider = () => Provider9.effect(BaoMount, Effect22.succeed(BaoMoun
2373
2417
  import { Resource as Resource10 } from "alchemy";
2374
2418
  import { isResolved as isResolved10 } from "alchemy/Diff";
2375
2419
  import * as Provider10 from "alchemy/Provider";
2376
- import * as Effect23 from "effect/Effect";
2420
+ import * as Effect24 from "effect/Effect";
2377
2421
 
2378
2422
  // src/openbao/pki-role-canonical.ts
2379
2423
  var DEFAULT_KEY_USAGE = [
@@ -2539,18 +2583,18 @@ var problems6 = (props) => {
2539
2583
  var BaoPkiRole = Resource10("Bao.PkiRole", {
2540
2584
  defaultRemovalPolicy: "retain"
2541
2585
  });
2542
- var readRole2 = (props) => Effect23.gen(function* () {
2586
+ var readRole2 = (props) => Effect24.gen(function* () {
2543
2587
  const live = yield* baoRead(rolePath4(props));
2544
2588
  if (live === undefined)
2545
2589
  return;
2546
2590
  return attributesOf10(props, live);
2547
2591
  });
2548
- var BaoPkiRoleProvider = () => Provider10.effect(BaoPkiRole, Effect23.succeed(BaoPkiRole.Provider.of({
2549
- list: () => Effect23.succeed([]),
2550
- read: Effect23.fn(function* ({ olds }) {
2592
+ var BaoPkiRoleProvider = () => Provider10.effect(BaoPkiRole, Effect24.succeed(BaoPkiRole.Provider.of({
2593
+ list: () => Effect24.succeed([]),
2594
+ read: Effect24.fn(function* ({ olds }) {
2551
2595
  return yield* readRole2(olds);
2552
2596
  }),
2553
- diff: Effect23.fn(function* ({ news, output }) {
2597
+ diff: Effect24.fn(function* ({ news, output }) {
2554
2598
  if (output === undefined || !isResolved10(news))
2555
2599
  return;
2556
2600
  if (mountPath(news.mount ?? "pki") !== output.mount || news.name !== output.name)
@@ -2562,11 +2606,11 @@ var BaoPkiRoleProvider = () => Provider10.effect(BaoPkiRole, Effect23.succeed(Ba
2562
2606
  return { action: "update" };
2563
2607
  return matches9(live, news) ? { action: "noop" } : { action: "update" };
2564
2608
  }),
2565
- reconcile: Effect23.fn(function* ({ news }) {
2609
+ reconcile: Effect24.fn(function* ({ news }) {
2566
2610
  const path = rolePath4(news);
2567
2611
  const bad = problems6(news);
2568
2612
  if (bad.length > 0) {
2569
- return yield* Effect23.die(new Error(`Bao.PkiRole ${path}: ${bad.join(" ")}`));
2613
+ return yield* Effect24.die(new Error(`Bao.PkiRole ${path}: ${bad.join(" ")}`));
2570
2614
  }
2571
2615
  const live = yield* readRole2(news);
2572
2616
  if (live === undefined || !matches9(live, news)) {
@@ -2574,14 +2618,14 @@ var BaoPkiRoleProvider = () => Provider10.effect(BaoPkiRole, Effect23.succeed(Ba
2574
2618
  }
2575
2619
  const after = yield* readRole2(news);
2576
2620
  if (after === undefined) {
2577
- return yield* Effect23.die(new Error(`Bao.PkiRole ${path}: write reported success but the role is absent.`));
2621
+ return yield* Effect24.die(new Error(`Bao.PkiRole ${path}: write reported success but the role is absent.`));
2578
2622
  }
2579
2623
  if (!matches9(after, news)) {
2580
- return yield* Effect23.die(new Error(`Bao.PkiRole ${path}: wrote cleanly but the role read back different. ` + "OpenBao stored something other than what was declared — inspect with " + `\`bao read ${path}\` before retrying.`));
2624
+ return yield* Effect24.die(new Error(`Bao.PkiRole ${path}: wrote cleanly but the role read back different. ` + "OpenBao stored something other than what was declared — inspect with " + `\`bao read ${path}\` before retrying.`));
2581
2625
  }
2582
2626
  return after;
2583
2627
  }),
2584
- delete: Effect23.fn(function* ({ output }) {
2628
+ delete: Effect24.fn(function* ({ output }) {
2585
2629
  yield* baoDelete(rolePath4(output));
2586
2630
  return;
2587
2631
  })
@@ -2590,7 +2634,7 @@ var BaoPkiRoleProvider = () => Provider10.effect(BaoPkiRole, Effect23.succeed(Ba
2590
2634
  import { Resource as Resource11 } from "alchemy";
2591
2635
  import { isResolved as isResolved11 } from "alchemy/Diff";
2592
2636
  import * as Provider11 from "alchemy/Provider";
2593
- import * as Effect25 from "effect/Effect";
2637
+ import * as Effect26 from "effect/Effect";
2594
2638
 
2595
2639
  // src/openbao/plugin-form.ts
2596
2640
  var resolve = (props) => ({
@@ -2646,7 +2690,7 @@ var sameArgs = (want, have) => want.length === have.length && want.every((entry,
2646
2690
  var matches10 = (attributes, form) => !attributes.builtin && attributes.version === form.version && attributes.sha256 === form.sha256 && attributes.command === form.command && sameArgs(form.args, attributes.args);
2647
2691
 
2648
2692
  // src/openbao/plugin-reconcile.ts
2649
- import * as Effect24 from "effect/Effect";
2693
+ import * as Effect25 from "effect/Effect";
2650
2694
 
2651
2695
  // src/openbao/plugin-wire.ts
2652
2696
  var readPluginData = (form) => baoRead(versionedPath(form.type, form.name, form.version));
@@ -2654,10 +2698,10 @@ var registerPlugin = (form) => baoWrite("PUT", catalogPath(form.type, form.name)
2654
2698
  var deregisterPlugin = (type, name, version) => baoDelete(versionedPath(type, name, version));
2655
2699
 
2656
2700
  // src/openbao/plugin-reconcile.ts
2657
- var readEntry = (form) => Effect24.map(readPluginData(form), (live) => live === undefined ? undefined : { attributes: attributesOf11(form, live), live });
2658
- var refuse5 = (form, message) => Effect24.die(new Error(`Bao.Plugin ${versionedPath(form.type, form.name, form.version)}: ${message}`));
2701
+ var readEntry = (form) => Effect25.map(readPluginData(form), (live) => live === undefined ? undefined : { attributes: attributesOf11(form, live), live });
2702
+ var refuse5 = (form, message) => Effect25.die(new Error(`Bao.Plugin ${versionedPath(form.type, form.name, form.version)}: ${message}`));
2659
2703
  var SELF_REPORTED = "the write succeeded but the read-back does not match. If the binary reports its own version, " + "OpenBao filed the registration under it (plugin_catalog.go setInternal) — declare `version` " + "as exactly the version the binary reports; that adopts the entry this write just made.";
2660
- var reconcilePlugin = (props) => Effect24.gen(function* () {
2704
+ var reconcilePlugin = (props) => Effect25.gen(function* () {
2661
2705
  const form = resolve(props);
2662
2706
  const bad = problems7(form);
2663
2707
  if (bad.length > 0)
@@ -2684,13 +2728,13 @@ var reconcilePlugin = (props) => Effect24.gen(function* () {
2684
2728
  var BaoPlugin = Resource11("Bao.Plugin", {
2685
2729
  defaultRemovalPolicy: "retain"
2686
2730
  });
2687
- var BaoPluginProvider = () => Provider11.effect(BaoPlugin, Effect25.succeed(BaoPlugin.Provider.of({
2688
- list: () => Effect25.succeed([]),
2689
- read: Effect25.fn(function* ({ olds }) {
2731
+ var BaoPluginProvider = () => Provider11.effect(BaoPlugin, Effect26.succeed(BaoPlugin.Provider.of({
2732
+ list: () => Effect26.succeed([]),
2733
+ read: Effect26.fn(function* ({ olds }) {
2690
2734
  const found = yield* readEntry(resolve(olds));
2691
2735
  return found?.attributes;
2692
2736
  }),
2693
- diff: Effect25.fn(function* ({ news, output }) {
2737
+ diff: Effect26.fn(function* ({ news, output }) {
2694
2738
  if (output === undefined || !isResolved11(news))
2695
2739
  return;
2696
2740
  const form = resolve(news);
@@ -2702,10 +2746,10 @@ var BaoPluginProvider = () => Provider11.effect(BaoPlugin, Effect25.succeed(BaoP
2702
2746
  return { action: "update" };
2703
2747
  return matches10(found.attributes, form) ? { action: "noop" } : { action: "update" };
2704
2748
  }),
2705
- reconcile: Effect25.fn(function* ({ news }) {
2749
+ reconcile: Effect26.fn(function* ({ news }) {
2706
2750
  return yield* reconcilePlugin(news);
2707
2751
  }),
2708
- delete: Effect25.fn(function* ({ output }) {
2752
+ delete: Effect26.fn(function* ({ output }) {
2709
2753
  yield* deregisterPlugin(output.type, output.name, output.version);
2710
2754
  return;
2711
2755
  })
@@ -2714,7 +2758,7 @@ var BaoPluginProvider = () => Provider11.effect(BaoPlugin, Effect25.succeed(BaoP
2714
2758
  import { Resource as Resource12 } from "alchemy";
2715
2759
  import { isResolved as isResolved12 } from "alchemy/Diff";
2716
2760
  import * as Provider12 from "alchemy/Provider";
2717
- import * as Effect27 from "effect/Effect";
2761
+ import * as Effect28 from "effect/Effect";
2718
2762
  import * as FileSystem from "effect/FileSystem";
2719
2763
  import * as Path from "effect/Path";
2720
2764
 
@@ -2722,13 +2766,13 @@ import * as Path from "effect/Path";
2722
2766
  var isEmptyAssembly = (parts, joined) => parts === 0 || !/^\s*path\s+"/m.test(joined);
2723
2767
 
2724
2768
  // src/openbao/policy-wire.ts
2725
- import * as Effect26 from "effect/Effect";
2769
+ import * as Effect27 from "effect/Effect";
2726
2770
  var policyPath = (name) => `sys/policies/acl/${name}`;
2727
- var readPolicy = (name) => Effect26.flatMap(baoRead(policyPath(name)), (data) => {
2771
+ var readPolicy = (name) => Effect27.flatMap(baoRead(policyPath(name)), (data) => {
2728
2772
  if (data === undefined)
2729
- return Effect26.succeed("");
2773
+ return Effect27.succeed("");
2730
2774
  const text11 = data["policy"];
2731
- return typeof text11 === "string" ? Effect26.succeed(text11) : Effect26.fail(new BaoError(200, "GET", policyPath(name), ["no data.policy in the response"]));
2775
+ return typeof text11 === "string" ? Effect27.succeed(text11) : Effect27.fail(new BaoError(200, "GET", policyPath(name), ["no data.policy in the response"]));
2732
2776
  });
2733
2777
  var writePolicy = (name, hcl) => baoWrite("PUT", policyPath(name), { policy: hcl });
2734
2778
  var deletePolicy = (name) => baoDelete(policyPath(name));
@@ -2744,10 +2788,10 @@ var attributesOf12 = (name, hcl, parts) => ({
2744
2788
  grants: grantsOf(hcl),
2745
2789
  parts
2746
2790
  });
2747
- var BaoPolicyProvider = () => Provider12.effect(BaoPolicy, Effect27.gen(function* () {
2791
+ var BaoPolicyProvider = () => Provider12.effect(BaoPolicy, Effect28.gen(function* () {
2748
2792
  const fs = yield* FileSystem.FileSystem;
2749
2793
  const path = yield* Path.Path;
2750
- const assemble = (dir) => Effect27.gen(function* () {
2794
+ const assemble = (dir) => Effect28.gen(function* () {
2751
2795
  const entries2 = (yield* fs.readDirectory(dir)).filter((f) => f.endsWith(".hcl")).sort();
2752
2796
  let joined = "";
2753
2797
  for (const f of entries2)
@@ -2756,23 +2800,33 @@ var BaoPolicyProvider = () => Provider12.effect(BaoPolicy, Effect27.gen(function
2756
2800
  return { joined, parts: entries2.length };
2757
2801
  });
2758
2802
  return BaoPolicy.Provider.of({
2759
- list: () => Effect27.succeed([]),
2760
- read: Effect27.fn(function* ({ olds }) {
2803
+ list: () => Effect28.succeed([]),
2804
+ read: Effect28.fn(function* ({ olds }) {
2761
2805
  const live = yield* readPolicy(olds.name);
2762
2806
  return live.length === 0 ? undefined : attributesOf12(olds.name, live, 0);
2763
2807
  }),
2764
- diff: Effect27.fn(function* ({ news, output }) {
2765
- if (output === undefined || !isResolved12(news))
2808
+ diff: Effect28.fn(function* ({ news, olds, output }) {
2809
+ const tried = output?.name ?? declaredString(olds, "name");
2810
+ const declared = declaredString(news, "name");
2811
+ const move = yield* judgeMove("Bao.Policy", tried, declared, policyPath, policyKey);
2812
+ if (output === undefined)
2813
+ return;
2814
+ if (move !== undefined)
2815
+ return { action: "replace" };
2816
+ if (!isResolved12(news))
2766
2817
  return;
2767
2818
  const { joined } = yield* assemble(news.fragments);
2768
2819
  const live = yield* readPolicy(news.name);
2769
2820
  return live.length > 0 && sha256(live) === sha256(joined) ? { action: "noop" } : { action: "update" };
2770
2821
  }),
2771
- reconcile: Effect27.fn(function* ({ news }) {
2822
+ reconcile: Effect28.fn(function* ({ news, output }) {
2772
2823
  const name = news.name;
2824
+ if (output !== undefined && isMoved(output.name, name, policyKey) === true) {
2825
+ return yield* refuseMovedUpdate(`Bao.Policy ${name}`, output.name, name);
2826
+ }
2773
2827
  const { joined, parts } = yield* assemble(news.fragments);
2774
2828
  if (isEmptyAssembly(parts, joined))
2775
- return yield* Effect27.die(new Error(`Bao.Policy ${name}: the fragments under ${news.fragments} carry no path grant ` + `(${String(parts)} file(s)). Writing it would revoke every grant the policy carries.`));
2829
+ return yield* Effect28.die(new Error(`Bao.Policy ${name}: the fragments under ${news.fragments} carry no path grant ` + `(${String(parts)} file(s)). Writing it would revoke every grant the policy carries.`));
2776
2830
  const current = yield* readPolicy(name);
2777
2831
  if (current.length > 0 && sha256(current) === sha256(joined)) {
2778
2832
  return attributesOf12(name, joined, parts);
@@ -2780,7 +2834,7 @@ var BaoPolicyProvider = () => Provider12.effect(BaoPolicy, Effect27.gen(function
2780
2834
  yield* writePolicy(name, joined);
2781
2835
  return attributesOf12(name, joined, parts);
2782
2836
  }),
2783
- delete: Effect27.fn(function* ({ output }) {
2837
+ delete: Effect28.fn(function* ({ output }) {
2784
2838
  yield* deletePolicy(output.name);
2785
2839
  return;
2786
2840
  })
@@ -2790,7 +2844,7 @@ var BaoPolicyProvider = () => Provider12.effect(BaoPolicy, Effect27.gen(function
2790
2844
  import { Resource as Resource13 } from "alchemy";
2791
2845
  import { isResolved as isResolved13 } from "alchemy/Diff";
2792
2846
  import * as Provider13 from "alchemy/Provider";
2793
- import * as Effect28 from "effect/Effect";
2847
+ import * as Effect30 from "effect/Effect";
2794
2848
 
2795
2849
  // src/openbao/proxmox-role-form.ts
2796
2850
  var text11 = (value) => typeof value === "string" ? value : "";
@@ -2836,25 +2890,45 @@ var sameTtl5 = (want, have) => {
2836
2890
  const haveSeconds = ttlSeconds(have);
2837
2891
  return wantSeconds !== undefined && wantSeconds === haveSeconds;
2838
2892
  };
2893
+ var rescopeRefusal = (before, mintUser, allowMintUserChange) => before === mintUser || allowMintUserChange === before ? undefined : `re-scopes mint_user ${before} → ${mintUser}. Every token this role mints inherits ` + `the ACL of that PVE user. To re-scope deliberately, declare allowMintUserChange: '${before}'.`;
2839
2894
  var matches11 = (attributes, props) => attributes.mintUser === props.mintUser && sameTtl5(props.ttl, attributes.ttl) && sameTtl5(props.maxTtl, attributes.maxTtl);
2840
2895
 
2841
- // src/openbao/proxmox-role.ts
2842
- var BaoProxmoxRole = Resource13("Bao.ProxmoxRole", {
2843
- defaultRemovalPolicy: "retain"
2844
- });
2845
- var readRole3 = (props) => Effect28.gen(function* () {
2896
+ // src/openbao/proxmox-role-wire.ts
2897
+ import * as Effect29 from "effect/Effect";
2898
+ var readRole3 = (props) => Effect29.gen(function* () {
2846
2899
  const live = yield* baoRead(rolePath5(props.mount, props.name));
2847
2900
  if (live === undefined)
2848
2901
  return;
2849
2902
  return attributesOf13(props, live);
2850
2903
  });
2851
- var BaoProxmoxRoleProvider = () => Provider13.effect(BaoProxmoxRole, Effect28.succeed(BaoProxmoxRole.Provider.of({
2852
- list: () => Effect28.succeed([]),
2853
- read: Effect28.fn(function* ({ olds }) {
2904
+
2905
+ // src/openbao/proxmox-role.ts
2906
+ var BaoProxmoxRole = Resource13("Bao.ProxmoxRole", {
2907
+ defaultRemovalPolicy: "retain"
2908
+ });
2909
+ var BaoProxmoxRoleProvider = () => Provider13.effect(BaoProxmoxRole, Effect30.succeed(BaoProxmoxRole.Provider.of({
2910
+ list: () => Effect30.succeed([]),
2911
+ read: Effect30.fn(function* ({ olds }) {
2854
2912
  return yield* readRole3(olds);
2855
2913
  }),
2856
- diff: Effect28.fn(function* ({ news, output }) {
2857
- if (output === undefined || !isResolved13(news))
2914
+ diff: Effect30.fn(function* ({ news, olds, output }) {
2915
+ const tried = triedRolePath(output, olds, rolePath5);
2916
+ const declared = declaredRolePath(news, rolePath5);
2917
+ const move = yield* judgeMove("Bao.ProxmoxRole", tried, declared, (path) => path);
2918
+ if (output === undefined)
2919
+ return;
2920
+ if (move !== undefined) {
2921
+ const mintUser = declaredString(news, "mintUser");
2922
+ if (mintUser === undefined || isPendingProp(news, "allowMintUserChange"))
2923
+ return;
2924
+ const from = (yield* readRole3(output))?.mintUser ?? output.mintUser;
2925
+ const allow = declaredString(news, "allowMintUserChange");
2926
+ const refusal2 = rescopeRefusal(from, mintUser, allow);
2927
+ if (refusal2 === undefined)
2928
+ return { action: "replace" };
2929
+ return yield* Effect30.die(new Error(`Bao.ProxmoxRole ${move.from} → ${move.to}: the rename ${refusal2}`));
2930
+ }
2931
+ if (!isResolved13(news))
2858
2932
  return;
2859
2933
  const live = yield* readRole3(news);
2860
2934
  if (live === undefined)
@@ -2863,25 +2937,31 @@ var BaoProxmoxRoleProvider = () => Provider13.effect(BaoProxmoxRole, Effect28.su
2863
2937
  return { action: "noop" };
2864
2938
  return { action: "update" };
2865
2939
  }),
2866
- reconcile: Effect28.fn(function* ({ news }) {
2940
+ reconcile: Effect30.fn(function* ({ news, output }) {
2941
+ const path = rolePath5(news.mount, news.name);
2942
+ const before = output === undefined ? path : rolePath5(output.mount, output.name);
2943
+ if (isMoved(before, path) === true) {
2944
+ return yield* refuseMovedUpdate("Bao.ProxmoxRole", before, path);
2945
+ }
2867
2946
  const live = yield* readRole3(news);
2868
- if (live !== undefined && live.mintUser !== news.mintUser && news.allowMintUserChange !== live.mintUser) {
2869
- return yield* Effect28.die(new Error(`Bao.ProxmoxRole ${rolePath5(news.mount, news.name)}: live mint_user ` + `${live.mintUser} != declared ${news.mintUser}. Every token this role mints ` + "inherits the ACL of that PVE user. To re-scope deliberately, declare " + `allowMintUserChange: '${live.mintUser}'.`));
2947
+ const rescope = live === undefined ? undefined : rescopeRefusal(live.mintUser, news.mintUser, news.allowMintUserChange);
2948
+ if (rescope !== undefined) {
2949
+ return yield* Effect30.die(new Error(`Bao.ProxmoxRole ${path}: ${rescope}`));
2870
2950
  }
2871
2951
  if (live === undefined || !matches11(live, news)) {
2872
2952
  const write = writeBody9(news);
2873
2953
  if (!write.ok) {
2874
- return yield* Effect28.die(new Error(`Bao.ProxmoxRole ${rolePath5(news.mount, news.name)}: unparseable duration ` + `${write.bad.join(", ")}. Use 30s / 5m / 1h / 1d, or 0.`));
2954
+ return yield* Effect30.die(new Error(`Bao.ProxmoxRole ${path}: unparseable duration ` + `${write.bad.join(", ")}. Use 30s / 5m / 1h / 1d, or 0.`));
2875
2955
  }
2876
- yield* baoWrite("PUT", rolePath5(news.mount, news.name), write.body);
2956
+ yield* baoWrite("PUT", path, write.body);
2877
2957
  }
2878
2958
  const after = yield* readRole3(news);
2879
2959
  if (after === undefined) {
2880
- return yield* Effect28.die(new Error(`Bao.ProxmoxRole ${rolePath5(news.mount, news.name)}: write returned no error ` + "but the role is still absent."));
2960
+ return yield* Effect30.die(new Error(`Bao.ProxmoxRole ${path}: write returned no error ` + "but the role is still absent."));
2881
2961
  }
2882
2962
  return after;
2883
2963
  }),
2884
- delete: Effect28.fn(function* ({ output }) {
2964
+ delete: Effect30.fn(function* ({ output }) {
2885
2965
  yield* baoDelete(rolePath5(output.mount, output.name));
2886
2966
  return;
2887
2967
  })
@@ -2890,7 +2970,7 @@ var BaoProxmoxRoleProvider = () => Provider13.effect(BaoProxmoxRole, Effect28.su
2890
2970
  import { Resource as Resource14 } from "alchemy";
2891
2971
  import { isResolved as isResolved14 } from "alchemy/Diff";
2892
2972
  import * as Provider14 from "alchemy/Provider";
2893
- import * as Effect29 from "effect/Effect";
2973
+ import * as Effect31 from "effect/Effect";
2894
2974
 
2895
2975
  // src/openbao/ssh-role-form.ts
2896
2976
  var readPath4 = (mount, name) => `${sshMountOf(mount)}/roles/${name}`;
@@ -2982,7 +3062,7 @@ var matches12 = (attributes, form) => attributes.keyType === "ca" && sameList2(f
2982
3062
  var BaoSshRole = Resource14("Bao.SshRole", {
2983
3063
  defaultRemovalPolicy: "retain"
2984
3064
  });
2985
- var readRole4 = (form) => Effect29.gen(function* () {
3065
+ var readRole4 = (form) => Effect31.gen(function* () {
2986
3066
  const live = yield* baoRead(readPath4(form.mount, form.name));
2987
3067
  if (live === undefined)
2988
3068
  return;
@@ -3008,14 +3088,14 @@ var wouldErase2 = (live) => {
3008
3088
  }
3009
3089
  return lost.sort();
3010
3090
  };
3011
- var refuse6 = (message) => Effect29.die(new Error(message));
3012
- var BaoSshRoleProvider = () => Provider14.effect(BaoSshRole, Effect29.succeed(BaoSshRole.Provider.of({
3013
- list: () => Effect29.succeed([]),
3014
- read: Effect29.fn(function* ({ olds }) {
3091
+ var refuse6 = (message) => Effect31.die(new Error(message));
3092
+ var BaoSshRoleProvider = () => Provider14.effect(BaoSshRole, Effect31.succeed(BaoSshRole.Provider.of({
3093
+ list: () => Effect31.succeed([]),
3094
+ read: Effect31.fn(function* ({ olds }) {
3015
3095
  const found = yield* readRole4(resolve2(olds));
3016
3096
  return found?.attributes;
3017
3097
  }),
3018
- diff: Effect29.fn(function* ({ news, output }) {
3098
+ diff: Effect31.fn(function* ({ news, output }) {
3019
3099
  if (output === undefined || !isResolved14(news))
3020
3100
  return;
3021
3101
  const form = resolve2(news);
@@ -3027,7 +3107,7 @@ var BaoSshRoleProvider = () => Provider14.effect(BaoSshRole, Effect29.succeed(Ba
3027
3107
  return { action: "update" };
3028
3108
  return matches12(found.attributes, form) ? { action: "noop" } : { action: "update" };
3029
3109
  }),
3030
- reconcile: Effect29.fn(function* ({ news }) {
3110
+ reconcile: Effect31.fn(function* ({ news }) {
3031
3111
  const form = resolve2(news);
3032
3112
  const path = readPath4(form.mount, form.name);
3033
3113
  const bad = badDurations(form);
@@ -3053,7 +3133,7 @@ var BaoSshRoleProvider = () => Provider14.effect(BaoSshRole, Effect29.succeed(Ba
3053
3133
  }
3054
3134
  return after.attributes;
3055
3135
  }),
3056
- delete: Effect29.fn(function* ({ output }) {
3136
+ delete: Effect31.fn(function* ({ output }) {
3057
3137
  yield* baoDelete(readPath4(output.mount, output.name));
3058
3138
  return;
3059
3139
  })
@@ -3107,5 +3187,5 @@ export {
3107
3187
  revokeSelfEffect
3108
3188
  };
3109
3189
 
3110
- //# debugId=7E1DFE85CC398C5464756E2164756E21
3190
+ //# debugId=81D869587FC0B7AF64756E2164756E21
3111
3191
  //# sourceMappingURL=index.js.map