empeira 0.1.0.pre.alpha.1

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 (274) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +661 -0
  3. data/README.md +150 -0
  4. data/bin/empeira +18 -0
  5. data/config/defaults.yaml +229 -0
  6. data/docs/addons.md +331 -0
  7. data/docs/architecture.md +277 -0
  8. data/docs/configuration.md +1204 -0
  9. data/docs/development.md +472 -0
  10. data/docs/hiera-and-eyaml.md +361 -0
  11. data/docs/installation.md +280 -0
  12. data/docs/networking.md +362 -0
  13. data/docs/nodes.md +642 -0
  14. data/docs/proxy.md +136 -0
  15. data/docs/troubleshooting.md +361 -0
  16. data/lib/empeira/addons/environment.rb +82 -0
  17. data/lib/empeira/addons/error.rb +14 -0
  18. data/lib/empeira/addons/installation.rb +119 -0
  19. data/lib/empeira/addons/manifest.rb +112 -0
  20. data/lib/empeira/addons/service.rb +50 -0
  21. data/lib/empeira/addons/storage.rb +94 -0
  22. data/lib/empeira/agent/acquisition.rb +120 -0
  23. data/lib/empeira/agent/artifact.rb +41 -0
  24. data/lib/empeira/agent/authentication.rb +122 -0
  25. data/lib/empeira/agent/cache.rb +122 -0
  26. data/lib/empeira/agent/cache_storage.rb +82 -0
  27. data/lib/empeira/agent/download.rb +84 -0
  28. data/lib/empeira/agent/native_commands.rb +84 -0
  29. data/lib/empeira/agent/release_sources.rb +64 -0
  30. data/lib/empeira/agent/target.rb +66 -0
  31. data/lib/empeira/agent/versions.rb +29 -0
  32. data/lib/empeira/application.rb +86 -0
  33. data/lib/empeira/build.json +5 -0
  34. data/lib/empeira/build_info.rb +46 -0
  35. data/lib/empeira/cli/addons.rb +45 -0
  36. data/lib/empeira/cli/base.rb +73 -0
  37. data/lib/empeira/cli/completion.rb +42 -0
  38. data/lib/empeira/cli/config.rb +22 -0
  39. data/lib/empeira/cli/main.rb +114 -0
  40. data/lib/empeira/cli/node.rb +87 -0
  41. data/lib/empeira/cli/progress.rb +169 -0
  42. data/lib/empeira/cli/proxy.rb +36 -0
  43. data/lib/empeira/cli/update.rb +23 -0
  44. data/lib/empeira/completion/addons.rb +93 -0
  45. data/lib/empeira/completion/bash.rb +62 -0
  46. data/lib/empeira/completion/catalog.rb +170 -0
  47. data/lib/empeira/configuration/additional_services.rb +74 -0
  48. data/lib/empeira/configuration/addons.rb +85 -0
  49. data/lib/empeira/configuration/agent_schema.rb +163 -0
  50. data/lib/empeira/configuration/agent_values.rb +54 -0
  51. data/lib/empeira/configuration/bootstrap_guests.rb +42 -0
  52. data/lib/empeira/configuration/command_mocks.rb +65 -0
  53. data/lib/empeira/configuration/display.rb +20 -0
  54. data/lib/empeira/configuration/dns_rewrites.rb +58 -0
  55. data/lib/empeira/configuration/document.rb +67 -0
  56. data/lib/empeira/configuration/eyaml.rb +46 -0
  57. data/lib/empeira/configuration/hiera_modulepath.rb +40 -0
  58. data/lib/empeira/configuration/hiera_mount_schema.rb +62 -0
  59. data/lib/empeira/configuration/hiera_mounts.rb +83 -0
  60. data/lib/empeira/configuration/image_schema.rb +68 -0
  61. data/lib/empeira/configuration/loader.rb +70 -0
  62. data/lib/empeira/configuration/merge.rb +48 -0
  63. data/lib/empeira/configuration/network_redirects.rb +73 -0
  64. data/lib/empeira/configuration/node_images.rb +64 -0
  65. data/lib/empeira/configuration/schema.rb +215 -0
  66. data/lib/empeira/configuration/server_mounts.rb +106 -0
  67. data/lib/empeira/configuration/service_configuration.rb +40 -0
  68. data/lib/empeira/configuration/ssh_preferences.rb +83 -0
  69. data/lib/empeira/configuration/user.rb +46 -0
  70. data/lib/empeira/configuration/vm_interfaces.rb +147 -0
  71. data/lib/empeira/control_plane/additional_configurations.rb +106 -0
  72. data/lib/empeira/control_plane/browser.rb +73 -0
  73. data/lib/empeira/control_plane/controller.rb +763 -0
  74. data/lib/empeira/control_plane/discovery.rb +69 -0
  75. data/lib/empeira/control_plane/environment.rb +101 -0
  76. data/lib/empeira/control_plane/files.rb +263 -0
  77. data/lib/empeira/control_plane/health.rb +104 -0
  78. data/lib/empeira/control_plane/plan.rb +356 -0
  79. data/lib/empeira/control_plane/storage.rb +48 -0
  80. data/lib/empeira/errors.rb +9 -0
  81. data/lib/empeira/execution/console.rb +45 -0
  82. data/lib/empeira/execution/diagnostics.rb +210 -0
  83. data/lib/empeira/execution/launch_error.rb +14 -0
  84. data/lib/empeira/execution/result.rb +11 -0
  85. data/lib/empeira/execution/runner.rb +235 -0
  86. data/lib/empeira/execution/terminal.rb +35 -0
  87. data/lib/empeira/hostname_pattern.rb +14 -0
  88. data/lib/empeira/images/configuration.rb +44 -0
  89. data/lib/empeira/images/identity.rb +30 -0
  90. data/lib/empeira/images/recipe.rb +22 -0
  91. data/lib/empeira/images/reference.rb +43 -0
  92. data/lib/empeira/images/source.rb +19 -0
  93. data/lib/empeira/immutable.rb +19 -0
  94. data/lib/empeira/infrastructure/cleanup.rb +328 -0
  95. data/lib/empeira/infrastructure/definition.rb +59 -0
  96. data/lib/empeira/infrastructure/owned_files.rb +63 -0
  97. data/lib/empeira/infrastructure/recovery_inventory.rb +53 -0
  98. data/lib/empeira/infrastructure/service.rb +237 -0
  99. data/lib/empeira/infrastructure/status.rb +73 -0
  100. data/lib/empeira/infrastructure/store.rb +274 -0
  101. data/lib/empeira/modules/events.rb +67 -0
  102. data/lib/empeira/modules/git_safety.rb +60 -0
  103. data/lib/empeira/modules/host_git.rb +91 -0
  104. data/lib/empeira/modules/image.rb +27 -0
  105. data/lib/empeira/modules/installer.rb +67 -0
  106. data/lib/empeira/modules/request.rb +68 -0
  107. data/lib/empeira/modules/state.rb +87 -0
  108. data/lib/empeira/modules/storage.rb +35 -0
  109. data/lib/empeira/network/bootstrap_proxy.rb +157 -0
  110. data/lib/empeira/network/definition.rb +61 -0
  111. data/lib/empeira/network/direct_egress.rb +133 -0
  112. data/lib/empeira/network/egress.rb +27 -0
  113. data/lib/empeira/network/gateway.rb +79 -0
  114. data/lib/empeira/network/host_policy.rb +53 -0
  115. data/lib/empeira/network/naming.rb +28 -0
  116. data/lib/empeira/network/peer/adapter_image.rb +32 -0
  117. data/lib/empeira/network/peer/allocation.rb +45 -0
  118. data/lib/empeira/network/peer/attachment.rb +27 -0
  119. data/lib/empeira/network/peer/backend.rb +108 -0
  120. data/lib/empeira/network/peer/channel.rb +133 -0
  121. data/lib/empeira/network/peer/channel_server.rb +71 -0
  122. data/lib/empeira/network/peer/docker_adapter.rb +165 -0
  123. data/lib/empeira/network/peer/helper_security.rb +40 -0
  124. data/lib/empeira/network/peer/layout.rb +55 -0
  125. data/lib/empeira/network/peer/linux_podman.rb +114 -0
  126. data/lib/empeira/network/peer/podman_machine.rb +112 -0
  127. data/lib/empeira/network/policy.rb +39 -0
  128. data/lib/empeira/network/proxy_bindings.rb +52 -0
  129. data/lib/empeira/network/proxy_policy.rb +64 -0
  130. data/lib/empeira/network/redirects.rb +89 -0
  131. data/lib/empeira/network/resource.rb +50 -0
  132. data/lib/empeira/node/agent_installer.rb +31 -0
  133. data/lib/empeira/node/agent_package.rb +125 -0
  134. data/lib/empeira/node/agent_repository.rb +160 -0
  135. data/lib/empeira/node/apt_configuration.rb +85 -0
  136. data/lib/empeira/node/bootstrap.rb +17 -0
  137. data/lib/empeira/node/certificates.rb +98 -0
  138. data/lib/empeira/node/command_mock.rb +38 -0
  139. data/lib/empeira/node/command_mocks.rb +114 -0
  140. data/lib/empeira/node/container.rb +134 -0
  141. data/lib/empeira/node/container_agent_bootstrap.rb +43 -0
  142. data/lib/empeira/node/container_bootstrap.rb +52 -0
  143. data/lib/empeira/node/container_guest.rb +42 -0
  144. data/lib/empeira/node/container_lifecycle.rb +145 -0
  145. data/lib/empeira/node/container_preparation.rb +144 -0
  146. data/lib/empeira/node/container_state.rb +129 -0
  147. data/lib/empeira/node/definition.rb +20 -0
  148. data/lib/empeira/node/diagnostic_command.rb +19 -0
  149. data/lib/empeira/node/dnf_agent_repository.rb +202 -0
  150. data/lib/empeira/node/external_fact.rb +21 -0
  151. data/lib/empeira/node/image.rb +38 -0
  152. data/lib/empeira/node/interactive_tools.rb +33 -0
  153. data/lib/empeira/node/interface.rb +18 -0
  154. data/lib/empeira/node/inventory.rb +143 -0
  155. data/lib/empeira/node/package_bootstrap.rb +129 -0
  156. data/lib/empeira/node/package_proxy.rb +102 -0
  157. data/lib/empeira/node/package_sources.rb +19 -0
  158. data/lib/empeira/node/puppet_command.rb +12 -0
  159. data/lib/empeira/node/rpm_configuration.rb +62 -0
  160. data/lib/empeira/node/run_request.rb +52 -0
  161. data/lib/empeira/node/runtime_proxy.rb +86 -0
  162. data/lib/empeira/node/service.rb +72 -0
  163. data/lib/empeira/node/ssh_client.rb +25 -0
  164. data/lib/empeira/node/ssh_credentials.rb +78 -0
  165. data/lib/empeira/node/ssh_endpoint.rb +58 -0
  166. data/lib/empeira/node/ssh_identity.rb +37 -0
  167. data/lib/empeira/node/user_ssh.rb +60 -0
  168. data/lib/empeira/node/vm.rb +89 -0
  169. data/lib/empeira/node/vm_bootstrap.rb +53 -0
  170. data/lib/empeira/node/vm_lifecycle.rb +212 -0
  171. data/lib/empeira/node/vm_preflight.rb +37 -0
  172. data/lib/empeira/node/vm_preparation.rb +161 -0
  173. data/lib/empeira/node/vm_state.rb +161 -0
  174. data/lib/empeira/platform/facts.rb +66 -0
  175. data/lib/empeira/platform/kvm.rb +91 -0
  176. data/lib/empeira/platform/locations.rb +51 -0
  177. data/lib/empeira/platform/resolvers.rb +85 -0
  178. data/lib/empeira/platform/routes.rb +50 -0
  179. data/lib/empeira/progress.rb +70 -0
  180. data/lib/empeira/project_root.rb +47 -0
  181. data/lib/empeira/providers/lifecycle.rb +96 -0
  182. data/lib/empeira/providers/registry.rb +21 -0
  183. data/lib/empeira/providers/resource.rb +32 -0
  184. data/lib/empeira/providers.rb +55 -0
  185. data/lib/empeira/runtime/agent_helper.rb +34 -0
  186. data/lib/empeira/runtime/container.rb +184 -0
  187. data/lib/empeira/runtime/diagnostic_execution.rb +52 -0
  188. data/lib/empeira/runtime/docker.rb +87 -0
  189. data/lib/empeira/runtime/image_metadata.rb +83 -0
  190. data/lib/empeira/runtime/interface.rb +8 -0
  191. data/lib/empeira/runtime/mount_probe.rb +57 -0
  192. data/lib/empeira/runtime/podman.rb +122 -0
  193. data/lib/empeira/runtime/recipe_build.rb +103 -0
  194. data/lib/empeira/runtime/registry_diagnostics.rb +23 -0
  195. data/lib/empeira/runtime/service_arguments.rb +55 -0
  196. data/lib/empeira/runtime/service_images.rb +118 -0
  197. data/lib/empeira/runtime/service_inspection.rb +159 -0
  198. data/lib/empeira/runtime/services.rb +120 -0
  199. data/lib/empeira/runtime/update_helper.rb +97 -0
  200. data/lib/empeira/runtime/workspace_network.rb +69 -0
  201. data/lib/empeira/server/environment_configuration.rb +53 -0
  202. data/lib/empeira/server/http.rb +38 -0
  203. data/lib/empeira/server/puppetdb_runtime.rb +74 -0
  204. data/lib/empeira/server/relay_certificate.rb +76 -0
  205. data/lib/empeira/server/runtime.rb +90 -0
  206. data/lib/empeira/services/definition.rb +42 -0
  207. data/lib/empeira/updates/images.rb +104 -0
  208. data/lib/empeira/updates/modules.rb +45 -0
  209. data/lib/empeira/updates/service.rb +45 -0
  210. data/lib/empeira/version_requirement.rb +23 -0
  211. data/lib/empeira/vm/agent.rb +66 -0
  212. data/lib/empeira/vm/bootstrap_requirements.rb +69 -0
  213. data/lib/empeira/vm/certificate_runtime.rb +30 -0
  214. data/lib/empeira/vm/cloud_init.rb +171 -0
  215. data/lib/empeira/vm/disk.rb +136 -0
  216. data/lib/empeira/vm/guest.rb +123 -0
  217. data/lib/empeira/vm/guest_channel.rb +110 -0
  218. data/lib/empeira/vm/guest_network.rb +94 -0
  219. data/lib/empeira/vm/guest_output.rb +52 -0
  220. data/lib/empeira/vm/guest_shell.rb +79 -0
  221. data/lib/empeira/vm/guest_upload.rb +43 -0
  222. data/lib/empeira/vm/image_cache.rb +77 -0
  223. data/lib/empeira/vm/image_source.rb +91 -0
  224. data/lib/empeira/vm/interface.rb +8 -0
  225. data/lib/empeira/vm/interface_ownership.rb +124 -0
  226. data/lib/empeira/vm/interface_routes.rb +101 -0
  227. data/lib/empeira/vm/interfaces.rb +240 -0
  228. data/lib/empeira/vm/management.rb +73 -0
  229. data/lib/empeira/vm/prerequisites.rb +42 -0
  230. data/lib/empeira/vm/qemu.rb +83 -0
  231. data/lib/empeira/vm/qemu_runtime.rb +404 -0
  232. data/lib/empeira/vm/removal.rb +30 -0
  233. data/lib/empeira/vm/root_disk.rb +92 -0
  234. data/lib/empeira/vm/session_lock.rb +56 -0
  235. data/lib/empeira/vm/system_ssh.rb +29 -0
  236. data/lib/empeira/workspace.rb +42 -0
  237. data/lib/empeira.rb +209 -0
  238. data/resources/browser/relay.rb +22 -0
  239. data/resources/gateway/Containerfile +8 -0
  240. data/resources/gateway/bridge.rb +66 -0
  241. data/resources/gateway/configure.rb +323 -0
  242. data/resources/modules/Containerfile +15 -0
  243. data/resources/modules/Gemfile +5 -0
  244. data/resources/modules/Gemfile.lock +105 -0
  245. data/resources/modules/worker.rb +161 -0
  246. data/resources/network/adapter/Containerfile +13 -0
  247. data/resources/network/adapter/bridge_linux.go +51 -0
  248. data/resources/network/adapter/framing.go +45 -0
  249. data/resources/network/adapter/framing_test.go +53 -0
  250. data/resources/network/adapter/go.mod +3 -0
  251. data/resources/network/adapter/main_linux.go +273 -0
  252. data/resources/network/channel.rb +18 -0
  253. data/resources/network/ssh_connect.rb +32 -0
  254. data/resources/nodes/apt_sources.rb +66 -0
  255. data/resources/nodes/debian/Containerfile +10 -0
  256. data/resources/nodes/diagnostic_exec.rb +109 -0
  257. data/resources/nodes/interactive_tools.rb +139 -0
  258. data/resources/nodes/managed_file.rb +91 -0
  259. data/resources/nodes/management/guest_agent.py +415 -0
  260. data/resources/nodes/management/setup.sh +20 -0
  261. data/resources/nodes/management/shell_rc.sh +12 -0
  262. data/resources/nodes/management/system_account.sh +24 -0
  263. data/resources/nodes/management/system_home.sh +15 -0
  264. data/resources/nodes/rpm/Containerfile +10 -0
  265. data/resources/nodes/runtime_proxy.rb +244 -0
  266. data/resources/nodes/ssh/proxy.rb +16 -0
  267. data/resources/nodes/ssh/sshd_config +15 -0
  268. data/resources/nodes/ssh/start +6 -0
  269. data/resources/proxy/Containerfile +10 -0
  270. data/resources/relay/Containerfile +5 -0
  271. data/resources/relay/puppetdb.rb +81 -0
  272. data/resources/relay/server_environment.rb +10 -0
  273. data/resources/relay/server_start.sh +41 -0
  274. metadata +326 -0
@@ -0,0 +1,361 @@
1
+ # Hiera and EYAML
2
+
3
+ The canonical selected control repository is mounted read-only at
4
+ `/etc/puppetlabs/code/environments/production`. Change `server.environment` to use
5
+ another ordinary Puppet environment name. Environment caching is disabled so local,
6
+ uncommitted manifests, modules and data are visible without image rebuilds. The
7
+ repository supplies its normal `environment.conf`, `hiera.yaml`, manifests and
8
+ vendored modules. Puppetfile modules are synchronized explicitly with `update modules` into `modules.path`.
9
+ Empeira installs the structured `facts.empeira` fact before the first catalog:
10
+
11
+ ```yaml
12
+ empeira:
13
+ provider: vm # container on container nodes
14
+ hierarchy: empeira
15
+ ```
16
+
17
+ The hierarchy selector is always the literal string `empeira`. Empeira does not edit
18
+ the repository's `hiera.yaml`.
19
+
20
+ Repositories that need disposable-node overrides can optionally add this hierarchy:
21
+
22
+ ```yaml
23
+ hierarchy:
24
+ - name: "Empeira provider"
25
+ path: "virtual/empeira-%{facts.empeira.provider}.yaml"
26
+ - name: "Empeira common"
27
+ path: "virtual/%{facts.empeira.hierarchy}.yaml"
28
+ - name: "Virtualization"
29
+ path: "virtual/%{facts.virtual}.yaml"
30
+ ```
31
+
32
+ Priority lookups select the first matching value: provider-specific overrides in
33
+ `virtual/empeira-vm.yaml` or `virtual/empeira-container.yaml`, then shared overrides
34
+ in `virtual/empeira.yaml`, then the normal virtualization data. Merge lookups retain
35
+ ordinary Hiera merge semantics. For example:
36
+
37
+ `virtual/empeira-vm.yaml`:
38
+
39
+ ```yaml
40
+ example::profile: vm
41
+ ```
42
+
43
+ `virtual/empeira-container.yaml`:
44
+
45
+ ```yaml
46
+ example::profile: container
47
+ ```
48
+
49
+ `virtual/empeira.yaml`:
50
+
51
+ ```yaml
52
+ example::dns_servers: [198.18.0.53]
53
+ ```
54
+
55
+ A production node without `facts.empeira` cannot select either Empeira file and uses
56
+ only `virtual/<facts.virtual>.yaml`. The native lookup integration gate verifies
57
+ this negative case with the configured server image, including an existing common
58
+ file. Do not add a static `path: virtual/empeira.yaml`: that would apply the shared
59
+ overrides to production nodes too. `facts.virtual` remains Facter's own platform fact.
60
+
61
+ Existing provisioned nodes receive the selector on their next `up`, `node start`
62
+ or `node puppet`; the owned fact file is verified before recording its revision.
63
+ Unchanged revisions reuse the inventory stamp without extra guest probes. The fact
64
+ file is reserved for Empeira; Puppet should not manage it. Empeira never edits the
65
+ user's hierarchy or adds server-side selection logic. The files can live in the
66
+ normal control repository or existing Hiera mounts; generic repositories need no
67
+ Empeira-specific hierarchy.
68
+
69
+ ## External Hiera repositories
70
+
71
+ Configure Puppet-aware mounts in `.empeira.yaml`:
72
+
73
+ ```yaml
74
+ hiera:
75
+ mounts:
76
+ - source: ../hieradata-register
77
+ type: module
78
+ name: hieradata_register
79
+ - source: ../hieradata-secure
80
+ type: module
81
+ name: hieradata_secure
82
+ - source: ../environment-data
83
+ type: environment
84
+ target: data/external
85
+ required: true
86
+ ```
87
+
88
+ `module` mounts appear read-only at
89
+ `/etc/puppetlabs/code/environments/<server.environment>/modules/<name>`.
90
+ Names must start with a lowercase letter and contain lowercase letters, digits or
91
+ underscores. `environment` mounts appear at the configured relative `target` below
92
+ the same environment root. Absolute targets, `.`/`..` segments, unsafe mount syntax,
93
+ duplicate targets and overlapping parent/child targets are rejected, including
94
+ collisions across mount types. Existing destination symlinks must stay inside the
95
+ environment. Generic absolute container destinations are not supported.
96
+
97
+ Relative sources resolve against the canonical control-repository root; absolute
98
+ local sources are supported. Existing paths are canonicalized, including macOS
99
+ `/var` symlinks. Sources may live outside the control repository. `required` defaults
100
+ to `false`: missing, inaccessible or dangling optional repositories produce a visible
101
+ warning during `up` and are skipped. `required: true` makes unavailability fatal.
102
+ Existing non-directories, looping symlinks and malformed definitions remain errors.
103
+ An optional mount is not permission to ignore unsafe configuration.
104
+
105
+ Puppet's [default modulepath](https://help.puppet.com/core/current/Content/PuppetCore/config_file_environment.htm)
106
+ includes the environment's `modules` directory. Empeira checks explicit
107
+ `environment.conf` modulepaths and reports a known exclusion before startup. It
108
+ never rewrites that file. Expressions containing unresolved Puppet variables cannot
109
+ be verified completely from the CLI host; ensure the effective server modulepath
110
+ includes the mount destination. Environment mounts can be referenced by ordinary
111
+ relative paths in `hiera.yaml`, such as `data/external` or `external` when the datadir
112
+ is already `data`.
113
+
114
+ All repositories stay live and read-only. Changes inside mounted Hiera repositories
115
+ are visible to the next catalog request with `environment_timeout = 0`, without
116
+ an image rebuild, `up`, or a service restart.
117
+ Empeira neither copies their data nor writes probe files into them. It uses a managed
118
+ environment directory containing links to the read-only control repository so nested
119
+ mount targets can exist without modifying control code. Existing control files and
120
+ linked directories remain live. `up` refreshes links for new control entries under
121
+ projected ancestor directories. EYAML keys remain configured separately in `eyaml`,
122
+ and decryption runs on the server.
123
+
124
+ `config show` includes `effective_hiera_mounts` with resolved destinations and
125
+ `available`/`skipped` states. These are desired mount observations, not a container
126
+ readiness claim. Mount definitions, required flags and source availability affect
127
+ only the server fingerprint; Hiera file contents are not hashed. Run `up` after
128
+ changing mount definitions or making a skipped source available to reconcile the
129
+ server.
130
+
131
+ ## Puppetfile modules
132
+
133
+ `empeira update modules` resolves Hiera mounts and synchronizes the root `Puppetfile`.
134
+ Run it explicitly before `up` when required modules have not been installed. `up`
135
+ consumes existing modules without r10k, Forge or Git access and never repairs them.
136
+ An obviously missing module tree fails early with:
137
+
138
+ ```text
139
+ Puppetfile modules are not available.
140
+ Run: empeira update modules
141
+ ```
142
+
143
+ Without a Puppetfile, the repository's existing module layout continues to work unchanged.
144
+
145
+ ```yaml
146
+ hiera:
147
+ mounts:
148
+ - source: ../hieradata
149
+ type: module
150
+ name: hieradata
151
+ required: false
152
+ ```
153
+
154
+ When `../hieradata` is available, Empeira uses that local repository and does not
155
+ install the matching Puppetfile module. When the optional path is unavailable,
156
+ Empeira warns and skips its mount. `update modules` installs the Puppetfile version;
157
+ `up` uses that existing fallback or reports missing modules. With
158
+ `required: true`, an unavailable local repository aborts before synchronization;
159
+ there is no fallback. Only available `type: module` entries are overrides.
160
+ `owner-hieradata`, `owner/hieradata` and `hieradata` normalize to the module name
161
+ `hieradata`. Duplicate normalized Puppetfile names are errors, including overrides.
162
+
163
+ The installer uses the pinned [r10k Puppetfile loader](https://github.com/puppetlabs/r10k)
164
+ and its native `module_exclude_regex`, before module synchronization. The Puppetfile
165
+ is evaluated once in the disposable update helper. Its validated installation plan is
166
+ then used for host Git acquisition and r10k synchronization without reevaluation.
167
+ Empeira never rewrites the original Puppetfile. r10k synchronizes modules directly
168
+ into `modules.path`, reusing existing installations and native Git/r10k caches.
169
+ The existing managed environment projection exposes installed modules at the normal
170
+ `/etc/puppetlabs/code/environments/<environment>/modules` path. Parent environment
171
+ mounts precede local Hiera mounts, which stay live and read-only. An explicitly
172
+ excluded `modules` path in `environment.conf` remains an error. Git-tracked module
173
+ contents are protected before synchronization. Undeclared directories in the managed
174
+ path are retained; Empeira does not purge them. Environment mounts overlapping `modules` are
175
+ rejected when a Puppetfile exists; use module mounts for overrides.
176
+
177
+ Git entries support HTTPS/HTTP, `ssh://` URLs and normal SCP-style Git URLs such as
178
+ `git@private-alias:team/profile.git`, with `ref`, `branch`, `tag` or `commit`.
179
+ Without a selector, the acquired remote's HEAD is used. Prefer a full commit ID
180
+ for reproducibility. Forge entries support exact versions and r10k's `:latest`;
181
+ pin versions for reproducibility. Unpinned branches and `:latest` are resolved only
182
+ during synchronization, not on every `up`. Custom
183
+ Forge endpoints use the ordinary Puppetfile `forge` directive. `moduledir` and
184
+ `install_path` must resolve to `modules`; alternate installation trees, SVN, local
185
+ Puppetfile entries, `:control_branch`, embedded HTTP credentials and SSH URL passwords
186
+ are unsupported and fail clearly. An excluded override does not need a usable
187
+ remote or ref. Git source acquisition runs on the host as the invoking user, using
188
+ normal Git/OpenSSH behavior: SSH agent, identities, `~/.ssh/config`, aliases,
189
+ ProxyJump, known hosts and VPN routing. Empeira supplies no SSH flags or replacement
190
+ identity, copies no private keys, and manages no Git credentials. Interactive Git
191
+ retains terminal access for authentication; unattended execution needs an already usable identity/agent.
192
+ Persistent bare Git mirrors are kept in the normal external Empeira state and updated
193
+ incrementally using host Git. The helper reads these mirrors and reuses r10k's native
194
+ cache and working copies; no temporary bundles or fresh module trees are produced.
195
+ Neither SSH sockets nor private keys enter the helper. Empeira disables Git template
196
+ copying when creating mirrors, so user template hooks are not copied.
197
+
198
+ The installer receives a copy of the Puppetfile, not the control repository or host
199
+ SSH configuration: external Ruby includes and host-dependent Puppetfiles are unsupported.
200
+ Puppetfiles remain trusted executable Ruby input. The helper is configured through
201
+ `images.r10k.build: modules/Containerfile`; its reviewed recipe produces
202
+ `localhost/empeira-r10k:<recipe-hash>` and includes Ruby, r10k, Git and CA certificates.
203
+ The obsolete `images.modules` key is rejected.
204
+
205
+ The disposable installer belongs to the update plane. It uses the selected
206
+ runtime's normal bridge network (`bridge` on Docker, `podman` on Podman), with
207
+ normal runtime DNS and external connectivity. It never joins an Empeira workspace
208
+ network and needs neither CoreDNS nor Squid. Workspace `network.egress`, `proxy.enabled`,
209
+ `proxy.global` and `proxy.rules` do not affect
210
+ Forge or Git acquisition. Supplied `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` and their
211
+ lowercase equivalents are inherited unchanged; Empeira generates no proxy URL.
212
+ The host/runtime's normal network and corporate-proxy configuration must permit
213
+ Forge access, including redirects. Node and service isolation remains unchanged.
214
+
215
+ The helper bind-mounts readonly Puppetfile/request inputs and source mirrors, plus the
216
+ writable `modules.path` and native r10k cache. The caches stay outside the control
217
+ repository in the normal workspace state. It runs with the invoking user's UID/GID
218
+ (Podman uses `keep-id`) and dropped capabilities. Per-invocation ownership and observed
219
+ network membership are verified. Success, failure and interruption remove the owned
220
+ helper where the engine remains reachable; no persistent service or internal DNS
221
+ identity is created. Reviewed image builds contain no control code or credentials.
222
+
223
+ `up` performs a shallow availability check, without evaluating Puppetfile Ruby or
224
+ contacting any source. An existing small external list of module names helps detect
225
+ missing directories and unavailable optional-override fallbacks. Manually populated
226
+ module directories can also be used without this metadata. An explicit successful
227
+ zero-module update permits an empty directory. This is a plausibility check, not a
228
+ version/content check: Puppetfile changes never trigger automatic synchronization.
229
+ Module acquisition has no freshness fingerprints or implicit repair actions. The
230
+ server reloads live code for each catalog with `environment_timeout = 0`; it never
231
+ requests or enforces module synchronization.
232
+
233
+ `empeira update modules` always synchronizes, including moving refs, directly into the
234
+ configured directory. Existing Git repositories are fetched incrementally, and r10k
235
+ reuses installed modules where possible; unchanged Forge releases are not downloaded
236
+ again. Native r10k synchronization may reset local edits to managed modules. Use local
237
+ Hiera mounts for maintained overrides. Updates work before the first `up` and after
238
+ `down` or `destroy`; they do not start services or rewrite infrastructure inventory.
239
+ Both commands use the existing version gate and workspace mutation lock.
240
+
241
+ A running server can see changes immediately through its readonly module mount.
242
+ There is no deployment generation, activation pointer or subsequent `up` requirement
243
+ for module content changes. This is intentional for a disposable local test environment.
244
+ The next catalog request sees the edits, including from agents invoked outside Empeira.
245
+ Empeira does not automatically run Puppet agents. A failed or interrupted update may
246
+ leave partially updated modules visible; there is no rollback. Inspect the reported
247
+ module/ref/version or connectivity error and rerun `update modules`. The installed-name
248
+ list is invalidated before a sync. `up` may consume plausible partial contents;
249
+ it never retries the update. Recovery is an explicit `update modules`.
250
+
251
+ Both `down` and `destroy` retain the actual module directory, native source/r10k caches
252
+ and small installed-name list. The next `up` consumes these existing dependencies without downloading
253
+ again. Module and cache removal is a separate manual action.
254
+
255
+ During synchronization, the existing two-line progress display shows the current
256
+ module name and completed count, for example `profile (14/28)` at 50%.
257
+ The total excludes available local overrides. A module is counted only after its
258
+ complete r10k sync succeeds; Git acquisition displays its name without incrementing
259
+ the counter. An empty effective Puppetfile completes at 100% without division by zero. Local module mounts appear as
260
+ `name (local override)`. Redirected output and CI use the same messages as plain
261
+ lines. Successful Git, SSH, HTTP, Forge and r10k output is buffered and never
262
+ printed as a subprocess transcript. Authentication prompts can still use the
263
+ user's terminal; Empeira does not disable normal Git/SSH authentication.
264
+
265
+ On failure, Empeira reports the module and bounded, filtered diagnostic lines for
266
+ authentication, DNS, HTTP, versions and refs. Commands, authorization headers,
267
+ private key material, filesystem paths and credential-bearing URLs are suppressed
268
+ or redacted. A failed module is not counted, and failure never emits a successful
269
+ completion event. Check the reported
270
+ cause before retrying; raw successful subprocess logs are not part of normal output.
271
+
272
+ `modules.path` is the actual module directory, relative to the canonical control
273
+ repository root unless absolute. The default:
274
+
275
+ ```yaml
276
+ modules:
277
+ path: modules
278
+ ```
279
+
280
+ produces a flat installation:
281
+
282
+ ```text
283
+ <control-repo>/modules/
284
+ ├── stdlib/
285
+ ├── concat/
286
+ └── profile/
287
+ ```
288
+
289
+ `path: ../puppet-modules` instead installs directly into that sibling directory.
290
+ There are no `.empeira`, workspace or generation subdirectories beneath the module
291
+ path. Internal metadata and caches stay in the platform workspace state. The server
292
+ sees the configured directory at its normal environment `modules/<name>` path.
293
+ Available local Hiera overrides are mounted over destination directories in that
294
+ tree (created empty if missing); their source remains live and readonly. Changing `modules.path` requires
295
+ `up` to change the server mount, but does not delete the previous directory.
296
+ Old generation directories from development versions are neither adopted nor deleted
297
+ automatically; they can be removed manually after the old infrastructure is torn down.
298
+
299
+ If the path is inside the canonical control repository, Empeira checks it with Git
300
+ before synchronization. Git-tracked contents anywhere under that path are a hard
301
+ error before changing module output. For an untracked path
302
+ not covered by Git's ignore rules, Empeira prints one repository-relative
303
+ recommendation per command and continues. It never edits `.gitignore`. Nested
304
+ ignore files, `.git/info/exclude` and configured global excludes are respected.
305
+ An external module directory needs no Git-ignore recommendation.
306
+
307
+ With the default path, the recommendation is:
308
+
309
+ ```text
310
+ Recommendation: modules/ is not ignored by Git. Add /modules/ to .gitignore.
311
+ ```
312
+
313
+ If the repository already tracks vendored modules there, select a different
314
+ `modules.path`; tracked files are never overwritten or deleted by synchronization.
315
+
316
+ ## EYAML
317
+
318
+ Enable server-side PKCS7 keys explicitly in `.empeira.yaml`:
319
+
320
+ ```yaml
321
+ eyaml:
322
+ enabled: true
323
+ private_key: secrets/private_key.pkcs7.pem
324
+ public_key: secrets/public_key.pkcs7.pem
325
+ ```
326
+
327
+ Relative paths resolve against the canonical project root. Absolute paths are also
328
+ supported. Both must resolve to readable regular files. The private key is a PEM
329
+ RSA private key for PKCS7 and the public file is its PEM X.509 certificate, as generated
330
+ by `eyaml createkeys`. Keep keys outside version control. Empeira neither generates
331
+ nor copies these keys into the repository, inventory or images. It mounts the files
332
+ read-only, exclusively in the configuration server. OpenVox mounts them directly;
333
+ the Puppet Server image changes ownership recursively under `/etc/puppetlabs/puppet`
334
+ at startup, so Empeira mounts its sources outside that tree and stages the keys in
335
+ a container-local tmpfs. Both providers expose the keys to Hiera at:
336
+
337
+ - `/etc/puppetlabs/puppet/eyaml/private_key.pkcs7.pem`
338
+ - `/etc/puppetlabs/puppet/eyaml/public_key.pkcs7.pem`
339
+
340
+ Point the existing control repository's `hiera.yaml` at these paths, for example:
341
+
342
+ ```yaml
343
+ version: 5
344
+ hierarchy:
345
+ - name: Encrypted common data
346
+ lookup_key: eyaml_lookup_key
347
+ path: common.eyaml
348
+ options:
349
+ pkcs7_private_key: /etc/puppetlabs/puppet/eyaml/private_key.pkcs7.pem
350
+ pkcs7_public_key: /etc/puppetlabs/puppet/eyaml/public_key.pkcs7.pem
351
+ ```
352
+
353
+ The server's JRuby environment must include `hiera-eyaml` (the default OpenVox
354
+ image includes it). The runtime must expose the source files through its UID
355
+ mapping and shared filesystem. Empeira verifies engine visibility
356
+ without printing file contents. It does not detect EYAML usage automatically or
357
+ rewrite `hiera.yaml`. `config show` redacts the private-key path under the existing secret-redaction
358
+ model and never displays key contents.
359
+ For Puppet Server, run `empeira up` after changing a key file to refresh the
360
+ container-local tmpfs copy without recreating the server.
361
+ EYAML is disabled by default.
@@ -0,0 +1,280 @@
1
+ # Installation
2
+
3
+ Empeira runs natively on Linux and macOS. Windows uses **WSL2 → Linux → Empeira**;
4
+ native Windows Ruby is unsupported. The gemspec requires **Ruby >= 3.4**; the
5
+ supported Ruby versions are **3.4 and 4.0**. Normal CI tests both on Ubuntu;
6
+ release gems are built with Ruby 3.4. Use source installation until a release gem
7
+ is available. No RubyGems.org publication is assumed.
8
+
9
+ ## Linux and WSL2
10
+
11
+ These commands target **Ubuntu 24.04 LTS** and **Debian 13 trixie**. Their default
12
+ Ruby packages are below Empeira's minimum; Ruby 3.2 and 3.3 are unsupported.
13
+ Install Ruby 3.4+ with your established Ruby version manager before using Empeira.
14
+ Ubuntu 22.04 and Debian 12 also need a newer Ruby. Installing the distribution's
15
+ default `ruby` package does not satisfy the gemspec on these hosts.
16
+
17
+ Core/source development dependencies and SSH access:
18
+
19
+ ```bash
20
+ sudo apt-get update
21
+ sudo apt-get install -y git build-essential openssh-client iproute2
22
+ # Activate Ruby 3.4+ using your Ruby version manager before continuing.
23
+ gem install bundler
24
+ ruby --version
25
+ gem --version
26
+ bundle --version
27
+ git --version
28
+ ssh -V
29
+ ```
30
+
31
+ The selected Ruby installation must supply `gem` and matching development headers;
32
+ `gem install bundler` supplies `bundle`. `build-essential` builds native development
33
+ gems; additional Ruby build prerequisites depend on your version manager.
34
+ `openssh-client` supplies `ssh` and `ssh-keygen` for regular node SSH. VM management
35
+ uses private VirtIO-Serial and Python 3 already present in the selected cloud image.
36
+ `iproute2` supplies host route inspection for automatic workspace subnet allocation. Coreutils supplies
37
+ `tail` for VM logs; Bash supplies optional completion. Both are standard on these
38
+ hosts. Downloads use Ruby libraries; Empeira does not invoke host curl/wget.
39
+
40
+ Default runtime (Podman with Netavark):
41
+
42
+ ```bash
43
+ sudo apt-get install -y podman netavark
44
+ podman --version
45
+ podman info --format '{{.Host.NetworkBackend}}'
46
+ ```
47
+
48
+ The backend must be `netavark`. Run Empeira as your normal user with rootless
49
+ Podman; do not mix rootful and rootless inventories.
50
+
51
+ Docker alternative: use **Docker Engine >= 28** with the Linux bridge driver.
52
+ Ubuntu 24.04's and Debian 13's distribution `docker.io` packages are too old for isolated gateway mode.
53
+ The following uses Docker's official apt repository on Ubuntu or Debian; it
54
+ assumes conflicting distribution Docker packages have already been removed as
55
+ specified by the upstream guide:
56
+
57
+ ```bash
58
+ sudo apt-get install -y ca-certificates curl
59
+ sudo install -m 0755 -d /etc/apt/keyrings
60
+ . /etc/os-release
61
+ curl -fsSL "https://download.docker.com/linux/$ID/gpg" | sudo tee /etc/apt/keyrings/docker.asc >/dev/null
62
+ sudo chmod a+r /etc/apt/keyrings/docker.asc
63
+ printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/%s %s stable\n' \
64
+ "$(dpkg --print-architecture)" "$ID" "${UBUNTU_CODENAME:-$VERSION_CODENAME}" |
65
+ sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
66
+ sudo apt-get update
67
+ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin
68
+ sudo systemctl enable --now docker
69
+ sudo usermod -aG docker "$USER"
70
+ # Start a new login session before continuing. Docker group access controls the engine.
71
+ docker --version
72
+ ```
73
+
74
+ Set `runtime.container_engine: docker` in `.empeira.yaml`, or use
75
+ `--container-engine docker`. WSL2 can instead use Docker Desktop's WSL integration
76
+ with a Linux engine >= 28; do not install a competing daemon in that distribution.
77
+
78
+ ### VM prerequisites
79
+
80
+ Optional packages, only for VM users:
81
+
82
+ ```bash
83
+ # amd64 host:
84
+ sudo apt-get install -y qemu-system-x86 qemu-utils xorriso openssh-client
85
+ qemu-system-x86_64 --version
86
+
87
+ # arm64 host instead:
88
+ sudo apt-get install -y qemu-system-arm qemu-efi-aarch64 qemu-utils xorriso openssh-client
89
+ qemu-system-aarch64 --version
90
+
91
+ # Both architectures:
92
+ qemu-img --version
93
+ xorriso -version
94
+ ssh -V
95
+ ```
96
+
97
+ `qemu-utils` supplies `qemu-img`. AArch64 requires the `qemu-efi-aarch64` firmware;
98
+ x86_64 uses QEMU's default firmware. Linux VM creation requires usable KVM:
99
+
100
+ ```bash
101
+ ls -l /dev/kvm
102
+ id
103
+ # If /dev/kvm belongs to group kvm and your account lacks access:
104
+ sudo usermod -aG kvm "$USER"
105
+ ```
106
+
107
+ Start a new login session after a group change. The device must exist, permit
108
+ read/write access and support the KVM API; group membership alone is insufficient.
109
+ Empeira reports ioctl failures and never silently falls back to TCG.
110
+
111
+ ### WSL2 specifics
112
+
113
+ Use the Linux commands above inside an Ubuntu 24.04 or Debian 13 WSL2 distribution,
114
+ with source/control checkouts on its Linux filesystem. KVM is unnecessary for
115
+ container-only use. VM use requires `/dev/kvm` and functional nested virtualization;
116
+ check `ls -l /dev/kvm` and apply the group/access remediation above if appropriate.
117
+ After group changes, run this **from Windows PowerShell**, then reopen WSL:
118
+
119
+ ```powershell
120
+ wsl --shutdown
121
+ ```
122
+
123
+ A missing KVM device requires a compatible Windows/WSL kernel and virtualization
124
+ configuration; adding a group cannot create acceleration. WSL mirrored networking
125
+ is not a prerequisite. Windows-host QEMU/WHPX is not currently implemented.
126
+
127
+ ## macOS with Homebrew
128
+
129
+ Install Homebrew using its [official instructions](https://brew.sh/), then core:
130
+
131
+ ```bash
132
+ brew install git ruby@3.4 openssh
133
+ export PATH="$(brew --prefix ruby@3.4)/bin:$PATH"
134
+ gem install --user-install bundler
135
+ export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
136
+ ruby --version
137
+ gem --version
138
+ bundle --version
139
+ git --version
140
+ ssh -V
141
+ ```
142
+
143
+ Keep those PATH settings in your shell configuration if desired. Apple’s system
144
+ Ruby is not suitable. Ruby 3.4 is the recommended development version. macOS supplies `scutil` for host/split DNS discovery and `tail` for logs.
145
+
146
+ Default runtime:
147
+
148
+ ```bash
149
+ brew install podman
150
+ podman machine init
151
+ podman machine start
152
+ podman --version
153
+ ```
154
+
155
+ Podman runs Linux containers in its managed Linux VM. Run `machine init` only once.
156
+ The engine must use Netavark and see the control repository and Empeira state bind
157
+ mounts. Configure machine sharing for paths outside its normal shared directories.
158
+
159
+ Docker alternative:
160
+
161
+ ```bash
162
+ brew install --cask docker-desktop
163
+ open -a Docker
164
+ docker --version
165
+ ```
166
+
167
+ Wait for Docker Desktop to start, use its Linux engine >= 28, and select Docker in
168
+ Empeira configuration. Docker Desktop has its own distribution/license terms; it
169
+ is not bundled with Empeira. The CLI host and engine must agree on mount paths.
170
+
171
+ Optional VM tools:
172
+
173
+ ```bash
174
+ brew install qemu xorriso
175
+ qemu-img --version
176
+ xorriso -version
177
+ # Apple Silicon:
178
+ qemu-system-aarch64 --version
179
+ qemu-system-aarch64 -accel help
180
+ # Intel instead:
181
+ qemu-system-x86_64 --version
182
+ qemu-system-x86_64 -accel help
183
+ ```
184
+
185
+ Homebrew QEMU provides its firmware. **HVF** is the expected accelerator; Empeira
186
+ fails if unavailable. Real macOS/HVF VM E2E, macOS container engine integration,
187
+ and arm64 browser/runtime E2E remain separate manual gates. Lightweight CI does
188
+ not establish those results. See the [VM validation gates](development.md#vm-and-shared-network-prerequisites).
189
+
190
+ ## Source installation and project opt-in
191
+
192
+ ```bash
193
+ git clone https://github.com/c8m6/empeira.git
194
+ cd empeira
195
+ bundle config set --local path vendor/bundle
196
+ bundle install
197
+ export EMPEIRA_SOURCE="$PWD"
198
+ empeira() { BUNDLE_GEMFILE="$EMPEIRA_SOURCE/Gemfile" bundle exec ruby "$EMPEIRA_SOURCE/bin/empeira" "$@"; }
199
+ empeira --version
200
+ empeira help
201
+ cd ../control-repo
202
+ touch .empeira.yaml
203
+ empeira config validate
204
+ empeira update modules # only when the project uses a Puppetfile
205
+ empeira up
206
+ empeira browser
207
+ ```
208
+
209
+ The control repository must already be a Git repository. Project commands resolve
210
+ `git rev-parse --show-toplevel` through the execution runner and require an actual
211
+ `.empeira.yaml` at that canonical root. An empty file uses defaults. Subdirectory
212
+ invocation and paths with spaces work. No Puppet-file heuristic or `--project`
213
+ selector exists. The source shell function preserves the current directory.
214
+ Version/help/self-update work outside Git (self-update currently reports unavailable).
215
+
216
+ ## Release gem installation
217
+
218
+ Download the `.gem` asset from a [GitHub release](https://github.com/c8m6/empeira/releases)
219
+ when one is available, then install it with your existing Ruby environment:
220
+
221
+ ```bash
222
+ gem install --user-install ./empeira-VERSION.gem
223
+ export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
224
+ empeira --version
225
+ ```
226
+
227
+ Replace `VERSION` with the downloaded asset's filename. RubyGems installs the Thor
228
+ runtime dependency; no third-party agents, images or binaries are bundled. The
229
+ release version is embedded in BuildInfo. Standalone binaries and automatic
230
+ self-update are unavailable. Do not use `sudo gem install` for normal operation.
231
+
232
+ For other Linux distributions, install equivalent Ruby/Git/runtime packages through
233
+ your established host setup. Guest OS support does not imply a tested host recipe.
234
+ Report native Linux, WSL2, macOS and runtime/architecture validation separately.
235
+
236
+ ## Bash completion
237
+
238
+ For an installed executable or the source function above in Bash:
239
+
240
+ ```bash
241
+ source <(empeira completion bash)
242
+ ```
243
+
244
+ Completion includes browser, additional lifecycle commands, node names from
245
+ inventory, configured OS versions, providers and runtime values. Both spaced and
246
+ `--provider=con` forms work, including Bash's split `=` tokens. Project-dependent
247
+ completion uses the current Git repository and returns no project candidates outside
248
+ one. It never probes a runtime. No shell configuration is changed automatically.
249
+
250
+ ## Installation references
251
+
252
+ Upstream installation references:
253
+ [Ubuntu noble indexes](https://archive.ubuntu.com/ubuntu/dists/noble/),
254
+ [Debian trixie index](https://deb.debian.org/debian/dists/trixie/main/),
255
+ [Docker Ubuntu](https://docs.docker.com/engine/install/ubuntu/),
256
+ [Docker Debian](https://docs.docker.com/engine/install/debian/),
257
+ [Homebrew Ruby 3.4](https://formulae.brew.sh/formula/ruby@3.4),
258
+ [Podman](https://formulae.brew.sh/formula/podman),
259
+ [QEMU](https://formulae.brew.sh/formula/qemu),
260
+ [xorriso](https://formulae.brew.sh/formula/xorriso),
261
+ [OpenSSH](https://formulae.brew.sh/formula/openssh),
262
+ [Docker Desktop](https://formulae.brew.sh/cask/docker-desktop).
263
+
264
+ ### Peer networking prerequisites
265
+
266
+ Linux/WSL2 subnet allocation requires iproute2 even for container-only use.
267
+ Native rootless Podman VM networking also requires Netavark,
268
+ user namespaces, `/dev/net/tun`, and KVM access inside the rootless namespace:
269
+
270
+ ```bash
271
+ podman --remote=false unshare --rootless-netns test -w /dev/net/tun
272
+ podman --remote=false unshare --rootless-netns test -w /dev/kvm
273
+ ```
274
+
275
+ Normal operation does not use sudo. Device/ACL setup is an administrator's one-time
276
+ host task. Linux Docker uses the owned packet-helper model and requires its engine
277
+ to support `/dev/net/tun` and `NET_ADMIN` for that helper. macOS uses QEMU/HVF plus
278
+ rootless Podman Machine or Docker Desktop, with the canonical state directory
279
+ shared where needed. The runtime builds the packaged static adapter; **Go is not
280
+ a normal Empeira prerequisite**. IPv4 is the supported peer protocol initially.