├── .gitignore ├── LICENSE ├── README.md ├── TODOS ├── activation.md ├── flake.lock ├── flake.nix ├── garnix.yaml ├── ls-initrd.sh ├── modules ├── base.nix ├── image.nix └── nix-virtiofs.nix ├── overlays └── systemd.nix ├── plot.svg ├── runvf ├── .gitignore ├── Makefile ├── Package.resolved ├── Package.swift ├── Sources │ └── runvf │ │ └── Runvf.swift ├── default.nix ├── nix │ ├── default.nix │ └── workspace-state.json └── runvf.entitlements └── shell.nix /.gitignore: -------------------------------------------------------------------------------- 1 | result 2 | result-* 3 | efi-vars.fd 4 | mounted 5 | .build 6 | outputs 7 | image.raw 8 | .vscode 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003-2018 Eelco Dolstra and the Nixpkgs/NixOS contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Server-optimised NixOS 2 | 3 | I wanted a playground to play around ideas to 'modernise' NixOS. 4 | For now, that's easier with a from scratch approach where I can 5 | freely sketch out ideas. 6 | 7 | Server-optimised NixOS is a distribution inspired 8 | by NixOS, ChromeOS, Container Optimised Linux and Container Linux. 9 | 10 | It is an opinionated, server-first distribution. 11 | 12 | Note that most of the listed features are currently vaporware 13 | 14 | 15 | ## Running 16 | 17 | * You can build an EFI image 18 | 19 | ```bash 20 | nix build .#image 21 | ``` 22 | 23 | * Build a bootspec 24 | 25 | ```bash 26 | nix build .#toplevel 27 | ``` 28 | 29 | 30 | * Run with MacOS 'Virtualization.Framework': 31 | 32 | We can't provide a nix package until Apple SDK 12 is shipped. tracked here https://github.com/NixOS/nixpkgs/issues/242666 33 | 34 | ```bash 35 | nix build .#toplevel --eval-store auto --builder ssh://linux-builder 36 | nix run . ./result/boot.json 37 | ``` 38 | 39 | 40 | ## Features 41 | 42 | * Automatic updates 43 | * Measured boot (TPM) 44 | * [x] systemd-boot with Unified Kernel images 45 | * [ ] SecureBoot 46 | * [x] Using dm-verity for integrity of the system 47 | * Unattended reboots 48 | * Atomic upgrades and rollbacks through reboots or kexec\ 49 | * Automatic boot assessment through systemd-boot with automatic rollback when system is unhealthy 50 | * No GUI components 51 | * fwupd integration 52 | * Have to include modules on demand. 53 | * Heavily documented modules 54 | * [x] Systemd is used for both stage-1 and stage-2 init 55 | * [x] Systemd-networkd based networking 56 | * Systemd-nspawn based containers (and also docker containers) 57 | * Heavy use of systemd-generators 58 | * Uses systemd-tmpfiles to populate `/etc` 59 | * [x] Systemd resizes and formats disks on first boot using `systemd-repart` 60 | * Only use ``Requires and Wants. Not WantedBy and RequiresBy 61 | i.e. 62 | 63 | targets.multi-user.wants = [ "nginx.service" ]; 64 | 65 | instead of services.nginx.wantedBy = [ "multi-user.target" ]; 66 | 67 | 68 | Why? This makes me less confused about ordering. Arrows are hard 69 | 70 | * Initrd can reconfigure the system (by consulting cloud-metadata or matchbox-like system) 71 | * https://github.com/coreos/afterburn 72 | * https://coreos.com/matchbox/docs/latest/api.html 73 | * https://github.com/poseidon/matchbox 74 | * https://github.com/coreos/ignition 75 | 76 | ## Boot process 77 | 78 | ![Screenshot](plot.svg) 79 | 80 | systemConfig can either be a path to an evaluated thingy or a path to a nix expression 81 | 82 | systemd initrd is booted. 83 | 84 | Might need to patch `systemd/remount-fs/remount-fs.c`. /usr and / are treated 85 | special by systemd and will be remounted in stage-2 with the correct /etc/fstab options. 86 | 87 | Why /usr too is a mystery to me; as the only way to mount it is by 88 | 89 | 90 | ``` 91 | # FIXME when linux < 4.5 is EOL, switch to atomic bind mounts 92 | #mount /nix/store /nix/store -o bind,remount,ro 93 | mount --bind /nix/store /nix/store 94 | mount -o remount,ro,bind /nix/store 95 | ``` 96 | 97 | 98 | ## About my current gripes with NixOS "cloud" images 99 | 100 | NixOS ships a bunch of cloud images; Azure, AWS, Digitalocean, GCP. 101 | These support reconfiguring boxes through contacting metadata service and then nixos-rebuild'switching into 102 | the desired configuration. 103 | 104 | I guess this works 'fine'. 105 | 106 | ## TODOs: 107 | 108 | * Make kernel-install optional through mesonFlags instead of hacky patch 109 | * Something with modulesTree so that depMod actually works! 110 | * microcode 111 | * Make an initramfs optional? 112 | 113 | -------------------------------------------------------------------------------- /TODOS: -------------------------------------------------------------------------------- 1 | https://github.com/NixOS/nixpkgs/commit/244ed6ae710553cb1392b1a5c4538e9e47b44cd6 2 | 3 | ^ This needs to be reverted 4 | 5 | See https://github.com/systemd/systemd/blob/master/docs/UIDS-GIDS.md#notes-on-resolvability-of-user-and-group-names 6 | 7 | Instead: 8 | 9 | nscd.wants = [ "nss-lookup.target" "nss-user-lookup.target" ]; 10 | nscd.before = [ "nss-lookup.target" "nss-user-lookup.target" ]; 11 | 12 | 13 | is there a big difference? Not really; because nscd in our case is _always_ needed anyway 14 | -------------------------------------------------------------------------------- /activation.md: -------------------------------------------------------------------------------- 1 | # Activation pseudocode 2 | 3 | Server-optimised linux supports activating new configurations in place. 4 | However; by design it is limited in scope. NixOS' configuration activation 5 | script is a bit of a behemoth that has grown over time and has special cases 6 | and quirks to iron it out. Instead; server-optimised-linux has very strict 7 | rules what _can_ be activated at runtime and what not. It will also tell you 8 | when it can not fulfill an activation without bugs, and asks you to reboot 9 | instead. 10 | 11 | The main rule is: 12 | * Support starting new units 13 | * Support stopping obselete units 14 | * NOT restarting changed units. 15 | 16 | The choice of restarting a unit 17 | 18 | We start of by calling. which tells us all the units that are known to systemd, and whether they're potentially 19 | masked or not 20 | ``` 21 | ListUnitFiles 22 | ``` 23 | We also call `realpath` on the unit file to get its absolute path in the nix store. If it's not in the nix 24 | store we ignore it for activation and emit a warning "Not managed by nix" 25 | 26 | We want to refuse activation if any files that were started before `sysinit.target are changed. 27 | This is a safety precaution; 28 | This is programatically queried using: 29 | ``` 30 | systemctl show sysinit.target -p After --no-pager 31 | ``` 32 | 33 | the daemon is reloaded to parse all the new config files potentially 34 | 35 | Then it will refuse to activate any unit with `DefaultDependencies=no` 36 | and specifically units that are scheduled `Before=sysinit.target` 37 | 38 | 39 | 40 | Next we call `ListUnitFiles` again. 41 | Any unit that disappeared from the list or is now `masked` is stopped. 42 | 43 | 44 | 45 | Next; we go on to starting and restarting. For this we have some more special rules 46 | 47 | 48 | 49 | For all other files; if `realpath` != original realpath; we restart the unit. IF `realpath` didn't point to 50 | a nix store path; we issue a `warning` and don't do anything 51 | 52 | 53 | Activation is now complete 54 | 55 | 56 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1722221733, 6 | "narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "12bf09802d77264e441f48e25459c10c93eada2e", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixos-24.05", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Server Optimised NixOS"; 3 | 4 | inputs.nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-24.05"; 5 | 6 | nixConfig = { 7 | extra-trusted-substituters = "https://cache.garnix.io"; 8 | extra-substituters = "https://cache.garnix.io"; 9 | extra-trusted-public-keys = "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="; 10 | }; 11 | 12 | outputs = { self, nixpkgs }: { 13 | formatter.aarch64-linux = nixpkgs.legacyPackages.aarch64-linux.nixpkgs-fmt; 14 | formatter.aarch64-darwin = nixpkgs.legacyPackages.aarch64-darwin.nixpkgs-fmt; 15 | 16 | packages.aarch64-darwin.runvf = nixpkgs.legacyPackages.aarch64-darwin.swiftPackages.callPackage ./runvf { }; 17 | 18 | apps.aarch64-darwin.default = { 19 | type = "app"; 20 | program = "${self.packages.aarch64-darwin.runvf}/bin/runvf"; 21 | }; 22 | 23 | nixosModules = { 24 | base = ./modules/base.nix; 25 | image = ./modules/image.nix; 26 | nix-virtiofs = ./modules/nix-virtiofs.nix; 27 | }; 28 | 29 | overlays.systemd = import ./overlays/systemd.nix; 30 | 31 | nixosConfigurations.default = nixpkgs.lib.nixosSystem { 32 | system = "aarch64-linux"; 33 | modules = [ 34 | self.nixosModules.nix-virtiofs 35 | self.nixosModules.base 36 | # self.nixosModules.image 37 | ]; 38 | }; 39 | 40 | packages.aarch64-linux = rec { 41 | inherit 42 | (self.nixosConfigurations.default.config.system.build) 43 | toplevel 44 | image; 45 | # nspawn; 46 | default = toplevel; 47 | }; 48 | 49 | 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /garnix.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | include: 3 | - '*.x86_64-linux.*' 4 | - '*.aarch64-linux.*' 5 | - 'nixosConfigurations.*' -------------------------------------------------------------------------------- /ls-initrd.sh: -------------------------------------------------------------------------------- 1 | # !/bin/sh 2 | # Helper to show contents of initrd 3 | zcat $1 | cpio -t | less 4 | -------------------------------------------------------------------------------- /modules/base.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, lib, ... }: 2 | { 3 | # boot.loader.systemd-boot.enable = true; 4 | boot.bootspec.enable = true; 5 | boot.loader.external.enable = true; 6 | boot.loader.external.installHook = pkgs.writeScript "install" '' 7 | echo would install 8 | ''; 9 | boot.initrd.systemd.enable = true; 10 | boot.initrd.systemd.emergencyAccess = true; 11 | boot.initrd.systemd.repart.enable = true; 12 | # we shouldn't disable this for non-containers. Systemd ships the unit anyway and already 13 | # has a ConditionVirtualization=container clause 14 | 15 | networking.useNetworkd = true; 16 | systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false; 17 | 18 | 19 | documentation.nixos.enable = false; 20 | system.disableInstallerTools = true; 21 | 22 | nix.enable = true; 23 | nix.channel.enable = false; 24 | nix.settings.experimental-features = [ "nix-command" "flakes" ]; 25 | 26 | systemd.additionalUpstreamSystemUnits = [ 27 | ]; 28 | 29 | users.users.arian = { 30 | isNormalUser = true; 31 | extraGroups = [ "wheel" ]; 32 | initialPassword = "arian"; 33 | }; 34 | 35 | systemd.services.debug-things = { 36 | wantedBy = [ "multi-user.target" ]; 37 | serviceConfig = { 38 | Type = "oneshot"; 39 | RemainAfterExit = true; 40 | StandardOutput = "journal+console"; 41 | }; 42 | script = '' 43 | ls /etc/pam.d 44 | cat /etc/passwd 45 | cat /etc/shadow 46 | ''; 47 | 48 | 49 | }; 50 | 51 | # systemd.services.console-getty.enable = true; 52 | # services.getty.autologinUser = "arian"; 53 | 54 | fileSystems."/" = { 55 | fsType = "tmpfs"; 56 | device = "tmpfs"; 57 | }; 58 | 59 | boot.initrd.availableKernelModules = [ 60 | "virtio_balloon" 61 | "virtio_console" 62 | "virtio_net" 63 | "virtio_pci" 64 | "virtio_rng" 65 | "virtio_blk" 66 | "virtio_scsi" 67 | "virtiofs" 68 | ]; 69 | 70 | boot.kernelParams = [ 71 | "console=hvc0" 72 | "rescue" 73 | "systemd.setenv=SYSTEMD_SULOGIN_FORCE=1" 74 | # "systemd.journald.forward_to_console" 75 | "systemd.log_level=debug" 76 | # "mount.usr=PARTLABEL=usr" 77 | ]; 78 | 79 | # TODO(arianvp): Why are we loading these explicitly again? 80 | boot.initrd.kernelModules = [ "virtio_balloon" "virtio_console" "virtio_rng" "dm-verity" ]; 81 | 82 | system.stateVersion = "24.05"; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /modules/image.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, modulesPath, ... }: 2 | let 3 | cfg = config.image; 4 | 5 | loaderConf = pkgs.writeTextFile { 6 | name = "loader.conf"; 7 | text = '' 8 | timeout ${toString config.boot.loader.timeout} 9 | ''; 10 | }; 11 | 12 | inherit (pkgs.stdenv.hostPlatform) linuxArch efiArch; 13 | EFIARCH = lib.toUpper efiArch; 14 | 15 | uki = pkgs.stdenv.mkDerivation { 16 | name = "${config.system.build.kernel.name}.efi"; 17 | # usrhash=${pkgs.jq}/bin/jq -r '.[]|select(.type=="usr-${arch}") | .roothash' ${config.system.build.imageWithoutESP}/repart-output.json 18 | buildCommand = '' 19 | 20 | ''; 21 | }; 22 | 23 | closure = pkgs.closureInfo { rootPaths = [ config.system.build.toplevel ]; }; 24 | seed = "d03836b2-8e14-46e6-9524-d3e3d0b363dd"; 25 | in 26 | { 27 | boot.initrd.availableKernelModules = [ 28 | "virtio_balloon" 29 | "virtio_console" 30 | "virtio_net" 31 | "virtio_pci" 32 | "virtio_rng" 33 | "virtio_blk" 34 | "virtio_scsi" 35 | "virtiofs" 36 | ]; 37 | 38 | boot.kernelParams = [ 39 | "console=hvc0" 40 | # "systemd.journald.forward_to_console" 41 | # "systemd.log_level=debug" 42 | # "mount.usr=PARTLABEL=usr" 43 | ]; 44 | boot.initrd.kernelModules = [ "virtio_balloon" "virtio_console" "virtio_rng" "dm-verity" ]; 45 | 46 | # We do this because we need the udev rules from the package 47 | boot.initrd.services.lvm.enable = true; 48 | 49 | boot.initrd.systemd = { 50 | additionalUpstreamUnits = [ 51 | "veritysetup-pre.target" 52 | "veritysetup.target" 53 | "remote-veritysetup.target" 54 | ]; 55 | storePaths = [ 56 | "${config.boot.initrd.systemd.package}/lib/systemd/systemd-veritysetup" 57 | "${config.boot.initrd.systemd.package}/lib/systemd/system-generators/systemd-veritysetup-generator" 58 | ]; 59 | # systemd doesn't deserialize state when init= is passed. 60 | # https://github.com/systemd/systemd/blob/85fe60b9e8cdf3f14a725fd65fe3003621d49baf/src/core/main.c#L1852-L1853 61 | # contents."sbin/init" = "${config.system.build.toplevel}/init"; 62 | }; 63 | 64 | boot.initrd.supportedFilesystems = [ "erofs" ]; 65 | 66 | # This improves boot time by ~1s 67 | systemd.mounts = [{ 68 | enable = false; 69 | where = "/sys/kernel/config"; 70 | }]; 71 | 72 | boot.initrd.systemd.mounts = [{ 73 | enable = false; 74 | where = "/sys/kernel/config"; 75 | }]; 76 | 77 | system.build.image = pkgs.runCommand "image" 78 | { 79 | nativeBuildInputs = [ 80 | pkgs.fakeroot 81 | pkgs.systemd-tools 82 | 83 | pkgs.dosfstools 84 | pkgs.mtools 85 | pkgs.erofs-utils 86 | 87 | pkgs.jq 88 | ]; 89 | 90 | } '' 91 | mkdir -p $out 92 | mkdir -p repart.d 93 | 94 | cat < repart.d/00-esp.conf 95 | [Partition] 96 | Type=esp 97 | Format=vfat 98 | SizeMinBytes=128M 99 | SizeMaxBytes=128M 100 | CopyFiles=${config.systemd.package}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi:/EFI/BOOT/BOOT${EFIARCH}.EFI 101 | CopyFiles=$out/Image.unsigned.efi:/EFI/Linux/Image.efi 102 | EOA 103 | 104 | cat < repart.d/10-usr.conf 105 | [Partition] 106 | Type=usr 107 | Label=usr 108 | Format=erofs 109 | Minimize=yes 110 | Verity=data 111 | VerityMatchKey=usr 112 | CopyFiles=${closure}/registration:/store/.nix-path-registration 113 | EOB 114 | for path in $(cat ${closure}/store-paths); do 115 | echo "CopyFiles=$path:''${path#/nix}" >> repart.d/10-usr.conf 116 | done 117 | 118 | cat < repart.d/20-usr-verity.conf 119 | [Partition] 120 | Type=usr-verity 121 | Verity=hash 122 | VerityMatchKey=usr 123 | SizeMinBytes=64M 124 | SizeMaxBytes=64M 125 | EOC 126 | 127 | fakeroot systemd-repart \ 128 | --dry-run=no \ 129 | --defer-partitions esp \ 130 | --empty=create \ 131 | --seed=${seed} \ 132 | --size=auto \ 133 | --definitions=./repart.d \ 134 | --json=pretty \ 135 | $out/image.raw \ 136 | | tee $out/repart-output.json 137 | 138 | usrhash=$(jq -r '.[]|select(.type=="usr-${linuxArch}") | .roothash' $out/repart-output.json) 139 | 140 | ${pkgs.systemd-tools}/lib/systemd/ukify \ 141 | ${config.system.build.kernel}/Image \ 142 | ${config.system.build.toplevel}/initrd \ 143 | --cmdline "${builtins.concatStringsSep " " config.boot.kernelParams} usrhash=$usrhash init=${config.system.build.toplevel}/init" \ 144 | --os-release @${config.environment.etc."os-release".source} \ 145 | --stub "${pkgs.systemd}/lib/systemd/boot/efi/linux${efiArch}.efi.stub"\ 146 | --no-sign-kernel 147 | 148 | mv Image.unsigned.efi $out/Image.unsigned.efi 149 | 150 | 151 | 152 | fakeroot systemd-repart \ 153 | --dry-run=no \ 154 | --seed=${seed} \ 155 | --definitions=./repart.d \ 156 | --json=pretty \ 157 | $out/image.raw 158 | ''; 159 | 160 | } 161 | -------------------------------------------------------------------------------- /modules/nix-virtiofs.nix: -------------------------------------------------------------------------------- 1 | { 2 | fileSystems = { 3 | "/nix/.ro-store" = { 4 | device = "nix-store"; 5 | fsType = "virtiofs"; 6 | options = [ "ro" ]; 7 | neededForBoot = true; 8 | }; 9 | "/nix/store" = { 10 | overlay = { 11 | lowerdir = [ "/nix/.ro-store" ]; 12 | upperdir = "/nix/.rw-store/upper"; 13 | workdir = "/nix/.rw-store/work"; 14 | }; 15 | neededForBoot = true; 16 | }; 17 | }; 18 | 19 | /*systemd.services.nix-path-registration = lib.mkIf config.nix.enable { 20 | requiredBy = [ "multi-user.target" ]; 21 | script = '' 22 | ${config.nix.package}/bin/nix-store --load-db < /nix/store/.nix-path-registration 23 | ''; 24 | };*/ 25 | } 26 | -------------------------------------------------------------------------------- /overlays/systemd.nix: -------------------------------------------------------------------------------- 1 | final: prev: { 2 | systemd-tools = prev.systemd.override { withUkify = true; }; 3 | } 4 | -------------------------------------------------------------------------------- /plot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 40 | 41 | 42 | 43 | Startup finished in 68ms (firmware) + 185ms (loader) + 272ms (kernel) + 1.526s (initrd) + 454ms (userspace) = 2.507s 44 | multi-user.target reached after 454ms in userspace.NixOS 23.11 (Tapir) nixos (Linux 6.1.46 #1-NixOS SMP Wed Aug 16 16:27:31 UTC 2023) arm64 apple 45 | 46 | 47 | 48 | 49 | 0.0s 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 1.0s 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 2.0s 72 | 73 | 74 | 75 | firmware 76 | 77 | loader 78 | 79 | kernel 80 | 81 | 82 | 83 | 84 | initrd 85 | 86 | 87 | 88 | -.slice 89 | 90 | 91 | 92 | system.slice 93 | 94 | 95 | 96 | init.scope 97 | 98 | 99 | 100 | system-systemd\x2dveritysetup.slice 101 | 102 | 103 | 104 | systemd-journald-dev-log.socket 105 | 106 | 107 | 108 | systemd-journald.socket 109 | 110 | 111 | 112 | sys-module-configfs.device (1.665s) 113 | 114 | 115 | 116 | dev-hvc4.device (1.668s) 117 | 118 | 119 | 120 | sys-devices-virtual-tty-hvc4.device (1.668s) 121 | 122 | 123 | 124 | dev-hvc3.device (1.665s) 125 | 126 | 127 | 128 | sys-devices-virtual-tty-hvc3.device (1.665s) 129 | 130 | 131 | 132 | dev-hvc1.device (1.665s) 133 | 134 | 135 | 136 | sys-devices-virtual-tty-hvc1.device (1.665s) 137 | 138 | 139 | 140 | dev-ttyS2.device (1.669s) 141 | 142 | 143 | 144 | sys-devices-platform-serial8250-tty-ttyS2.device (1.669s) 145 | 146 | 147 | 148 | sys-devices-virtual-tty-hvc6.device (1.668s) 149 | 150 | 151 | 152 | dev-hvc6.device (1.668s) 153 | 154 | 155 | 156 | dev-ttyp1.device (1.674s) 157 | 158 | 159 | 160 | sys-devices-virtual-tty-ttyp1.device (1.674s) 161 | 162 | 163 | 164 | dev-ttyp0.device (1.673s) 165 | 166 | 167 | 168 | sys-devices-virtual-tty-ttyp0.device (1.673s) 169 | 170 | 171 | 172 | sys-devices-virtual-tty-ttyp2.device (1.673s) 173 | 174 | 175 | 176 | dev-ttyp2.device (1.673s) 177 | 178 | 179 | 180 | dev-ttyp5.device (1.673s) 181 | 182 | 183 | 184 | sys-devices-virtual-tty-ttyp5.device (1.673s) 185 | 186 | 187 | 188 | sys-devices-virtual-tty-ttyp3.device (1.673s) 189 | 190 | 191 | 192 | dev-ttyp3.device (1.673s) 193 | 194 | 195 | 196 | sys-devices-virtual-tty-ttyp7.device (1.673s) 197 | 198 | 199 | 200 | dev-ttyp7.device (1.673s) 201 | 202 | 203 | 204 | dev-ttyp8.device (1.673s) 205 | 206 | 207 | 208 | sys-devices-virtual-tty-ttyp8.device (1.673s) 209 | 210 | 211 | 212 | sys-devices-virtual-tty-ttyp9.device (1.673s) 213 | 214 | 215 | 216 | dev-ttyp9.device (1.673s) 217 | 218 | 219 | 220 | sys-devices-virtual-tty-ttypa.device (1.673s) 221 | 222 | 223 | 224 | dev-ttypa.device (1.673s) 225 | 226 | 227 | 228 | dev-hvc5.device (1.666s) 229 | 230 | 231 | 232 | sys-devices-virtual-tty-hvc5.device (1.666s) 233 | 234 | 235 | 236 | sys-devices-virtual-tty-ttypd.device (1.673s) 237 | 238 | 239 | 240 | dev-ttypd.device (1.673s) 241 | 242 | 243 | 244 | sys-devices-virtual-tty-ttypc.device (1.672s) 245 | 246 | 247 | 248 | dev-ttypc.device (1.672s) 249 | 250 | 251 | 252 | sys-devices-virtual-tty-ttyp4.device (1.671s) 253 | 254 | 255 | 256 | dev-ttyp4.device (1.671s) 257 | 258 | 259 | 260 | sys-devices-virtual-tty-ttype.device (1.672s) 261 | 262 | 263 | 264 | dev-ttype.device (1.672s) 265 | 266 | 267 | 268 | dev-ttypb.device (1.672s) 269 | 270 | 271 | 272 | sys-devices-virtual-tty-ttypb.device (1.672s) 273 | 274 | 275 | 276 | dev-ttyp6.device (1.671s) 277 | 278 | 279 | 280 | sys-devices-virtual-tty-ttyp6.device (1.671s) 281 | 282 | 283 | 284 | sys-devices-platform-serial8250-tty-ttyS3.device (1.664s) 285 | 286 | 287 | 288 | dev-ttyS3.device (1.664s) 289 | 290 | 291 | 292 | sys-devices-virtual-tty-ttypf.device (1.671s) 293 | 294 | 295 | 296 | dev-ttypf.device (1.671s) 297 | 298 | 299 | 300 | sys-devices-virtual-tty-hvc7.device (1.659s) 301 | 302 | 303 | 304 | dev-hvc7.device (1.659s) 305 | 306 | 307 | 308 | dev-ttyS1.device (1.664s) 309 | 310 | 311 | 312 | sys-devices-platform-serial8250-tty-ttyS1.device (1.664s) 313 | 314 | 315 | 316 | sys-devices-platform-serial8250-tty-ttyS0.device (1.663s) 317 | 318 | 319 | 320 | dev-ttyS0.device (1.663s) 321 | 322 | 323 | 324 | sys-devices-virtual-tty-hvc2.device (1.658s) 325 | 326 | 327 | 328 | dev-hvc2.device (1.658s) 329 | 330 | 331 | 332 | dev-disk-by\x2ddiskseq-9.device (1.670s) 333 | 334 | 335 | 336 | dev-disk-by\x2dpath-virtio\x2dpci\x2d0000:00:06.0.device (1.670s) 337 | 338 | 339 | 340 | dev-vda.device (1.670s) 341 | 342 | 343 | 344 | dev-disk-by\x2dpath-pci\x2d0000:00:06.0.device (1.670s) 345 | 346 | 347 | 348 | sys-devices-pci0000:00-0000:00:06.0-virtio2-block-vda.device (1.670s) 349 | 350 | 351 | 352 | dev-disk-by\x2dpath-pci\x2d0000:00:06.0\x2dpart3.device (1.670s) 353 | 354 | 355 | 356 | dev-disk-by\x2dpartlabel-usr\x2darm64\x2dverity.device (1.670s) 357 | 358 | 359 | 360 | dev-disk-by\x2duuid-b3a14934\x2dcab4\x2d4c7a\x2d831a\x2d981bea64ed49.device (1.670s) 361 | 362 | 363 | 364 | sys-devices-pci0000:00-0000:00:06.0-virtio2-block-vda-vda3.device (1.670s) 365 | 366 | 367 | 368 | dev-disk-by\x2dpartuuid-35cdc9ac\x2d908f\x2d5eb9\x2d8f87\x2d14eae997bb22.device (1.670s) 369 | 370 | 371 | 372 | dev-disk-by\x2dpath-virtio\x2dpci\x2d0000:00:06.0\x2dpart3.device (1.670s) 373 | 374 | 375 | 376 | dev-vda3.device (1.670s) 377 | 378 | 379 | 380 | dev-disk-by\x2ddiskseq-9\x2dpart3.device (1.670s) 381 | 382 | 383 | 384 | dev-hvc0.device (1.657s) 385 | 386 | 387 | 388 | sys-devices-virtual-tty-hvc0.device (1.657s) 389 | 390 | 391 | 392 | dev-disk-by\x2dpartuuid-f7d392c9\x2d950f\x2d8d3d\x2da1fa\x2d2e714d31b12b.device (1.670s) 393 | 394 | 395 | 396 | dev-disk-by\x2dpath-pci\x2d0000:00:06.0\x2dpart2.device (1.670s) 397 | 398 | 399 | 400 | dev-disk-by\x2ddiskseq-9\x2dpart2.device (1.670s) 401 | 402 | 403 | 404 | sys-devices-pci0000:00-0000:00:06.0-virtio2-block-vda-vda2.device (1.670s) 405 | 406 | 407 | 408 | dev-disk-by\x2dpartlabel-usr.device (1.670s) 409 | 410 | 411 | 412 | dev-vda2.device (1.670s) 413 | 414 | 415 | 416 | dev-disk-by\x2duuid-7cc19d74\x2df2c4\x2d4fef\x2db1ba\x2d61cb4b6dda18.device 417 | 418 | 419 | 420 | dev-disk-by\x2dpath-virtio\x2dpci\x2d0000:00:06.0\x2dpart2.device (1.670s) 421 | 422 | 423 | 424 | dev-disk-by\x2dpartuuid-9953b88f\x2d2dfd\x2d4ff2\x2db02e\x2d91719765730e.device (1.689s) 425 | 426 | 427 | 428 | dev-disk-by\x2dpartlabel-esp.device (1.689s) 429 | 430 | 431 | 432 | dev-disk-by\x2dlabel-ESP.device (1.689s) 433 | 434 | 435 | 436 | dev-disk-by\x2ddiskseq-9\x2dpart1.device (1.689s) 437 | 438 | 439 | 440 | dev-disk-by\x2dpath-pci\x2d0000:00:06.0\x2dpart1.device (1.689s) 441 | 442 | 443 | 444 | sys-devices-pci0000:00-0000:00:06.0-virtio2-block-vda-vda1.device (1.689s) 445 | 446 | 447 | 448 | dev-disk-by\x2dpath-virtio\x2dpci\x2d0000:00:06.0\x2dpart1.device (1.689s) 449 | 450 | 451 | 452 | dev-disk-by\x2duuid-E921\x2d0AD8.device (1.689s) 453 | 454 | 455 | 456 | dev-vda1.device (1.689s) 457 | 458 | 459 | 460 | systemd-veritysetup@usr.service (13ms) 461 | 462 | 463 | 464 | sys-devices-pci0000:00-0000:00:05.0-virtio1-virtio\x2dports-vport1p0.device (1.711s) 465 | 466 | 467 | 468 | dev-vport1p0.device (1.711s) 469 | 470 | 471 | 472 | sys-subsystem-net-devices-enp0s1.device (1.644s) 473 | 474 | 475 | 476 | sys-devices-pci0000:00-0000:00:01.0-virtio0-net-enp0s1.device (1.644s) 477 | 478 | 479 | 480 | sys-devices-virtual-block-dm\x2d0.device 481 | 482 | 483 | 484 | dev-dm\x2d0.device 485 | 486 | 487 | 488 | dev-disk-by\x2did-dm\x2dname\x2dusr.device 489 | 490 | 491 | 492 | dev-disk-by\x2did-dm\x2duuid\x2dCRYPT\x2dVERITY\x2db3a14934cab44c7a831a981bea64ed49\x2dusr.device 493 | 494 | 495 | 496 | dev-mapper-usr.device 497 | 498 | 499 | 500 | veritysetup.target 501 | 502 | 503 | 504 | initrd-switch-root.target 505 | 506 | 507 | 508 | 509 | systemd 510 | 511 | 512 | 513 | etc.mount 514 | 515 | 516 | 517 | nix-store.mount 518 | 519 | 520 | 521 | run-keys.mount 522 | 523 | 524 | 525 | run-wrappers.mount 526 | 527 | 528 | 529 | usr.mount 530 | 531 | 532 | 533 | system-getty.slice 534 | 535 | 536 | 537 | system-modprobe.slice 538 | 539 | 540 | 541 | system-serial\x2dgetty.slice 542 | 543 | 544 | 545 | user.slice 546 | 547 | 548 | 549 | systemd-ask-password-console.path 550 | 551 | 552 | 553 | systemd-ask-password-wall.path 554 | 555 | 556 | 557 | cryptsetup.target 558 | 559 | 560 | 561 | machines.target 562 | 563 | 564 | 565 | paths.target 566 | 567 | 568 | 569 | remote-fs.target 570 | 571 | 572 | 573 | slices.target 574 | 575 | 576 | 577 | swap.target 578 | 579 | 580 | 581 | systemd-coredump.socket 582 | 583 | 584 | 585 | systemd-networkd.socket 586 | 587 | 588 | 589 | systemd-oomd.socket 590 | 591 | 592 | 593 | systemd-udevd-control.socket 594 | 595 | 596 | 597 | systemd-udevd-kernel.socket 598 | 599 | 600 | 601 | dev-hugepages.mount (5ms) 602 | 603 | 604 | 605 | dev-mqueue.mount (5ms) 606 | 607 | 608 | 609 | sys-kernel-debug.mount (5ms) 610 | 611 | 612 | 613 | kmod-static-nodes.service (4ms) 614 | 615 | 616 | 617 | modprobe@drm.service (15ms) 618 | 619 | 620 | 621 | modprobe@efi_pstore.service (3ms) 622 | 623 | 624 | 625 | modprobe@fuse.service (8ms) 626 | 627 | 628 | 629 | mount-pstore.service (10ms) 630 | 631 | 632 | 633 | systemd-journald.service (15ms) 634 | 635 | 636 | 637 | systemd-modules-load.service (19ms) 638 | 639 | 640 | 641 | systemd-remount-fs.service (8ms) 642 | 643 | 644 | 645 | systemd-udev-trigger.service (91ms) 646 | 647 | 648 | 649 | systemd-tmpfiles-setup-dev.service (12ms) 650 | 651 | 652 | 653 | run-credentials-systemd\x2dtmpfiles\x2dsetup\x2ddev.service.mount 654 | 655 | 656 | 657 | sys-fs-fuse-connections.mount (1ms) 658 | 659 | 660 | 661 | systemd-random-seed.service (3ms) 662 | 663 | 664 | 665 | local-fs-pre.target 666 | 667 | 668 | 669 | boot.automount 670 | 671 | 672 | 673 | local-fs.target 674 | 675 | 676 | 677 | systemd-udevd.service (19ms) 678 | 679 | 680 | 681 | systemd-journal-flush.service (8ms) 682 | 683 | 684 | 685 | firewall.service (145ms) 686 | 687 | 688 | 689 | systemd-sysctl.service (8ms) 690 | 691 | 692 | 693 | run-credentials-systemd\x2dsysctl.service.mount 694 | 695 | 696 | 697 | systemd-tmpfiles-setup.service (6ms) 698 | 699 | 700 | 701 | systemd-journal-catalog-update.service (13ms) 702 | 703 | 704 | 705 | systemd-oomd.service (24ms) 706 | 707 | 708 | 709 | systemd-resolved.service (71ms) 710 | 711 | 712 | 713 | systemd-timesyncd.service (24ms) 714 | 715 | 716 | 717 | systemd-update-utmp.service (5ms) 718 | 719 | 720 | 721 | sys-module-fuse.device 722 | 723 | 724 | 725 | systemd-update-done.service (4ms) 726 | 727 | 728 | 729 | sysinit.target 730 | 731 | 732 | 733 | logrotate.timer 734 | 735 | 736 | 737 | systemd-tmpfiles-clean.timer 738 | 739 | 740 | 741 | timers.target 742 | 743 | 744 | 745 | dbus.socket 746 | 747 | 748 | 749 | nix-daemon.socket 750 | 751 | 752 | 753 | sockets.target 754 | 755 | 756 | 757 | basic.target 758 | 759 | 760 | 761 | audit.service (13ms) 762 | 763 | 764 | 765 | logrotate-checkconf.service (5ms) 766 | 767 | 768 | 769 | nix-path-registration.service 770 | 771 | 772 | 773 | nscd.service (16ms) 774 | 775 | 776 | 777 | reload-systemd-vconsole-setup.service 778 | 779 | 780 | 781 | dbus.service (20ms) 782 | 783 | 784 | 785 | nss-lookup.target 786 | 787 | 788 | 789 | nss-user-lookup.target 790 | 791 | 792 | 793 | systemd-logind.service (18ms) 794 | 795 | 796 | 797 | network-pre.target 798 | 799 | 800 | 801 | network-interfaces.target 802 | 803 | 804 | 805 | systemd-networkd.service (80ms) 806 | 807 | 808 | 809 | dev-rfkill.device 810 | 811 | 812 | 813 | sys-devices-virtual-misc-rfkill.device 814 | 815 | 816 | 817 | systemd-rfkill.socket 818 | 819 | 820 | 821 | network-local-commands.service (1ms) 822 | 823 | 824 | 825 | network.target 826 | 827 | 828 | 829 | network-online.target 830 | 831 | 832 | 833 | systemd-user-sessions.service (3ms) 834 | 835 | 836 | 837 | getty@tty1.service 838 | 839 | 840 | 841 | serial-getty@hvc0.service 842 | 843 | 844 | 845 | getty.target 846 | 847 | 848 | 849 | multi-user.target 850 | 851 | 852 | 853 | Activating 854 | 855 | Active 856 | 857 | Deactivating 858 | 859 | Setting up security module 860 | 861 | Generators 862 | 863 | Loading unit files 864 | 865 | 866 | 867 | 868 | -------------------------------------------------------------------------------- /runvf/.gitignore: -------------------------------------------------------------------------------- 1 | .swiftpm 2 | .vscode 3 | .build 4 | -------------------------------------------------------------------------------- /runvf/Makefile: -------------------------------------------------------------------------------- 1 | PREFIX?=${out} 2 | 3 | .PHONY: install 4 | 5 | .build/release/runvf: Sources/runvf/Runvf.swift Package.swift 6 | swift build -c release --disable-sandbox 7 | chmod +x .build/release/runvf 8 | codesign -s - -f --entitlements runvf.entitlements .build/release/runvf 9 | 10 | install: 11 | mkdir -p $(PREFIX)/bin 12 | install -m 755 .build/release/runvf $(PREFIX)/bin/runvf 13 | -------------------------------------------------------------------------------- /runvf/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "swift-argument-parser", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/apple/swift-argument-parser", 7 | "state" : { 8 | "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", 9 | "version" : "1.2.3" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /runvf/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "runvf", 8 | platforms: [.macOS(.v11)], 9 | products: [ 10 | .executable(name: "runvf", targets: ["runvf"]) 11 | ], 12 | dependencies: [ 13 | .package( 14 | url: "https://github.com/apple/swift-argument-parser", 15 | from: "1.2.0" 16 | ) 17 | ], 18 | targets: [ 19 | .executableTarget( 20 | name: "runvf", 21 | dependencies: [ 22 | .product(name: "ArgumentParser", package: "swift-argument-parser") 23 | ], 24 | swiftSettings: []) 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /runvf/Sources/runvf/Runvf.swift: -------------------------------------------------------------------------------- 1 | import ArgumentParser 2 | import Foundation 3 | import Virtualization 4 | 5 | class VirtualMachineDelegate: NSObject, VZVirtualMachineDelegate { 6 | var virtualMachine: VZVirtualMachine? 7 | let originalAttributes: termios 8 | 9 | init(_ virtualMachine: VZVirtualMachine, originalAttributes: termios) { 10 | self.virtualMachine = virtualMachine 11 | self.originalAttributes = originalAttributes 12 | super.init() 13 | virtualMachine.delegate = self 14 | } 15 | 16 | func run() throws { 17 | DispatchQueue.main.async { 18 | self.virtualMachine?.start { result in 19 | switch result { 20 | case .success: 21 | print("Started") 22 | case .failure(let error): 23 | self.restoreTTY() 24 | print("Failed to start: \(error)") 25 | } 26 | } 27 | } 28 | } 29 | func restoreTTY() { 30 | var attributes = originalAttributes 31 | tcsetattr(FileHandle.standardInput.fileDescriptor, TCSANOW, &attributes) 32 | } 33 | 34 | func guestDidStop(_ virtualMachine: VZVirtualMachine) { 35 | restoreTTY() 36 | print("Guest stopped") 37 | } 38 | func virtualMachine(_ virtualMachine: VZVirtualMachine, didStopWithError error: Error) { 39 | restoreTTY() 40 | print("Guest stopped with error: \(error)") 41 | } 42 | 43 | /*@available(macOS, introduced: 12.0) 44 | func virtualMachine( 45 | _ virtualMachine: VZVirtualMachine, networkDevice: VZNetworkDevice, 46 | attachmentWasDisconnectedWithError error: Error 47 | ) { 48 | print("Network device \(networkDevice) was disconnected with error: \(error)") 49 | }*/ 50 | } 51 | 52 | struct Bootspec: Codable { 53 | let bootspec: Spec 54 | 55 | struct Spec: Codable { 56 | let `init`: String 57 | let kernel: String 58 | let initrd: String 59 | let initrdSecrets: String? 60 | let kernelParams: [String] 61 | let label: String 62 | let system: String 63 | let toplevel: String 64 | } 65 | private enum CodingKeys: String, CodingKey { 66 | case bootspec = "org.nixos.bootspec.v1" 67 | } 68 | } 69 | 70 | @main 71 | struct Runvf: ParsableCommand { 72 | 73 | struct Options: ParsableArguments, VMConfig { 74 | @Option var memorySize: UInt64 = 512 * 1024 * 1024 75 | @Option var cpuCount: Int = 1 76 | 77 | func createConfig() -> VZVirtualMachineConfiguration { 78 | let config = VZVirtualMachineConfiguration() 79 | config.cpuCount = cpuCount 80 | config.memorySize = memorySize 81 | return config 82 | } 83 | } 84 | 85 | /*struct BootEFI: ParsableCommand { 86 | @OptionGroup var options: Options 87 | 88 | @Option var variableStore: String? 89 | @Argument var image: String? 90 | 91 | func validate() throws { 92 | guard #available(macOS 12, *) else { 93 | throw ValidationError("EFI boot is only supported on macOS 12 and later") 94 | } 95 | } 96 | }*/ 97 | 98 | protocol VMConfig { 99 | func createConfig() throws -> VZVirtualMachineConfiguration 100 | } 101 | 102 | struct Boot: ParsableCommand, VMConfig { 103 | @OptionGroup var options: Options 104 | 105 | @Option var nixStoreImage: String? 106 | @Argument var bootspec: String? 107 | 108 | func createConfig() throws -> VZVirtualMachineConfiguration { 109 | 110 | let config = options.createConfig() 111 | if let bootspec = bootspec { 112 | let bootspec: Bootspec = try JSONDecoder().decode( 113 | Bootspec.self, from: Data(contentsOf: URL(fileURLWithPath: bootspec))) 114 | let bootLoader = VZLinuxBootLoader( 115 | kernelURL: URL(fileURLWithPath: bootspec.bootspec.kernel)) 116 | bootLoader.initialRamdiskURL = URL(fileURLWithPath: bootspec.bootspec.initrd) 117 | var params = bootspec.bootspec.kernelParams 118 | params.append("init=\(bootspec.bootspec.`init`)") 119 | bootLoader.commandLine = params.joined(separator: " ") 120 | config.bootLoader = bootLoader 121 | 122 | } else { 123 | throw ValidationError("No bootspec specified") 124 | } 125 | 126 | 127 | if #available(macOS 12, *) { 128 | let dev = VZVirtioFileSystemDeviceConfiguration(tag: "nix-store") 129 | let share = VZSingleDirectoryShare(directory: VZSharedDirectory(url: URL(fileURLWithPath: "/nix/store"), readOnly: true)) 130 | dev.share = share 131 | config.directorySharingDevices = [dev] 132 | } 133 | 134 | if #available(macOS 13, *) { 135 | let rosettaDirectoryShare = try VZLinuxRosettaDirectoryShare() 136 | if #available(macOS 14, *) { 137 | try? rosettaDirectoryShare.setCachingOptions(.abstractSocket("rosetta")) 138 | } 139 | } 140 | 141 | 142 | 143 | config.serialPorts = [createConsoleConfiguration()] 144 | try config.validate() 145 | return config 146 | } 147 | func createConsoleConfiguration() -> VZSerialPortConfiguration { 148 | let consoleConfiguration = VZVirtioConsoleDeviceSerialPortConfiguration() 149 | let stdioAttachment = VZFileHandleSerialPortAttachment( 150 | fileHandleForReading: FileHandle.standardInput, 151 | fileHandleForWriting: FileHandle.standardOutput) 152 | consoleConfiguration.attachment = stdioAttachment 153 | return consoleConfiguration 154 | } 155 | 156 | func setupTTY() -> termios { 157 | var attributes = termios() 158 | tcgetattr(FileHandle.standardInput.fileDescriptor, &attributes) 159 | let originalAttributes = attributes 160 | 161 | // istty 162 | if isatty(FileHandle.standardInput.fileDescriptor) != 0 { 163 | cfmakeraw(&attributes) 164 | tcsetattr(FileHandle.standardInput.fileDescriptor, TCSANOW, &attributes) 165 | } 166 | return originalAttributes 167 | } 168 | 169 | mutating func run() throws { 170 | let config = try createConfig() 171 | let delegate = VirtualMachineDelegate( 172 | VZVirtualMachine(configuration: config), originalAttributes: setupTTY()) 173 | try delegate.run() 174 | RunLoop.main.run() 175 | } 176 | } 177 | 178 | static var configuration = CommandConfiguration( 179 | abstract: "Run virtual machines on macOS", 180 | 181 | subcommands: [Boot.self], 182 | defaultSubcommand: Boot.self) 183 | 184 | } 185 | -------------------------------------------------------------------------------- /runvf/default.nix: -------------------------------------------------------------------------------- 1 | { swift, swiftpm, stdenv, swiftpm2nix, darwin, apple_sdk, Dispatch, Foundation }: 2 | let 3 | generated = swiftpm2nix.helpers ./nix; 4 | in 5 | stdenv.mkDerivation { 6 | pname = "runvf"; 7 | version = "0.0.0"; 8 | src = ./.; 9 | nativeBuildInputs = [ darwin.sigtool swift swiftpm ]; 10 | buildInputs = [ 11 | Dispatch 12 | Foundation 13 | apple_sdk.frameworks.Virtualization 14 | ]; 15 | # The helper provides a configure snippet that will prepare all dependencies 16 | # in the correct place, where SwiftPM expects them. 17 | configurePhase = generated.configure; 18 | 19 | # NOTE: This works but idk why 20 | postFixup = '' 21 | codesign -s - -f --entitlements runvf.entitlements $out/bin/runvf 22 | ''; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /runvf/nix/default.nix: -------------------------------------------------------------------------------- 1 | # This file was generated by swiftpm2nix. 2 | { 3 | workspaceStateFile = ./workspace-state.json; 4 | hashes = { 5 | "swift-argument-parser" = "1463yiahwa8qwgzbbxfzj3srcr9yx8z71lniqqaj8hvav3dpfhm8"; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /runvf/nix/workspace-state.json: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "artifacts": [], 4 | "dependencies": [ 5 | { 6 | "basedOn": null, 7 | "packageRef": { 8 | "identity": "swift-argument-parser", 9 | "kind": "remoteSourceControl", 10 | "location": "https://github.com/apple/swift-argument-parser", 11 | "name": "swift-argument-parser" 12 | }, 13 | "state": { 14 | "checkoutState": { 15 | "revision": "8f4d2753f0e4778c76d5f05ad16c74f707390531", 16 | "version": "1.2.3" 17 | }, 18 | "name": "sourceControlCheckout" 19 | }, 20 | "subpath": "swift-argument-parser" 21 | } 22 | ] 23 | }, 24 | "version": 6 25 | } 26 | -------------------------------------------------------------------------------- /runvf/runvf.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.virtualization 6 | 7 | 8 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | flake = builtins.getFlake (toString ./.); 3 | in 4 | flake.outputs.packages.${builtins.currentSystem}.image 5 | 6 | --------------------------------------------------------------------------------