{ flake, lib, pkgs, ... }: let inherit (flake.config.people) user0; inherit (flake.config.services.instances) gel; inherit (flake.inputs.gel.packages."x86_64-linux") gel-cli gel-server; inherit (flake.self.packages."x86_64-linux") tome; serviceCfg = gel; in { gelVM = { user, ip, mac, userMac, host, ssh, mnt, instance, }: { microvm.vms = { "${serviceCfg.name}-${user}" = { autostart = true; restartIfChanged = true; config = { system.stateVersion = "24.05"; time.timeZone = "America/Winnipeg"; users.users.root.openssh.authorizedKeys.keys = flake.config.people.users.${user0}.sshKeys; environment.systemPackages = [ gel-cli tome ]; services = { openssh = { enable = true; settings = { PasswordAuthentication = false; PermitRootLogin = "prohibit-password"; }; }; }; users.users.gel = { isSystemUser = true; group = "gel"; }; users.groups.gel = { }; networking.firewall.allowedTCPPorts = [ 22 serviceCfg.ports.port0 ]; systemd = { services = { gel-setup = { description = "Setup gel directories and schema"; wantedBy = [ "multi-user.target" ]; before = [ "gel.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' # copy bootstrap script mkdir -p /etc/gel cp /run/secrets/bootstrap.edgesql /etc/gel/bootstrap.edgesql chown gel:gel /etc/gel/bootstrap.edgesql chmod 400 /etc/gel/bootstrap.edgesql # update schema files if [ -z "$(ls -A /var/lib/gel-schema)" ]; then # first boot: copy everything cp -r ${tome}/* /var/lib/gel-schema/ else # subsequent boots: update config and schema files rm -f /var/lib/gel-schema/gel.toml cp ${tome}/gel.toml /var/lib/gel-schema/ rm -f /var/lib/gel-schema/dbschema/*.gel cp ${tome}/dbschema/*.gel /var/lib/gel-schema/dbschema/ fi chmod -R u+w /var/lib/gel-schema chown -R gel:gel /var/lib/gel-schema ''; }; gel = { description = "Gel database server"; after = [ "network.target" "run-secrets.mount" ]; requires = [ "gel-setup.service" "run-secrets.mount" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { ExecStartPre = '' ${lib.getExe' gel-server "gel-server"} \ --bootstrap-command-file=/etc/gel/bootstrap.edgesql \ --bootstrap-only \ --data-dir=/var/lib/${serviceCfg.name} \ --instance-name=${instance} ''; ExecStart = '' ${lib.getExe' gel-server "gel-server"} \ --admin-ui=enabled \ --bind-address=${ip} \ --data-dir=/var/lib/${serviceCfg.name} \ --instance-name=${instance} \ --security=insecure_dev_mode ''; User = "gel"; Group = "gel"; Restart = "always"; RestartSec = 5; TimeoutStartSec = 300; }; }; gel-password-sync = { description = "Sync gel admin password"; after = [ "gel.service" ]; requires = [ "gel.service" ]; serviceConfig = { Type = "oneshot"; User = "gel"; ExecStartPre = "+" + pkgs.writeShellScript "copy-password" '' # copy password file to a location gel user can read cp /run/secrets/${user}-pass /etc/gel/admin_password chown gel:gel /etc/gel/admin_password chmod 400 /etc/gel/admin_password ''; ExecStart = pkgs.writeShellScript "gel-password-sync" '' # wait for admin socket to be ready for i in {1..30}; do if [ -S /var/lib/${serviceCfg.name}/.s.GEL.admin.5656 ]; then break fi sleep 1 done ${lib.getExe' gel-cli "gel"} \ --admin \ --unix-path /var/lib/${serviceCfg.name} \ query "ALTER ROLE admin SET password := '$(cat /etc/gel/admin_password)'" ''; ExecStartPost = "+" + pkgs.writeShellScript "cleanup-password" '' rm -f /etc/gel/admin_password ''; }; }; gel-migrate = { description = "Run gel migrations"; after = [ "gel-password-sync.service" ]; requires = [ "gel-password-sync.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; ExecStart = pkgs.writeShellScript "gel-migrate" '' cd /var/lib/gel-schema cat /run/secrets/${user}-pass | ${lib.getExe' gel-cli "gel"} \ --host ${ip} \ --password-from-stdin \ --tls-security insecure \ --user admin \ migration create --non-interactive cat /run/secrets/${user}-pass | ${lib.getExe' gel-cli "gel"} \ --host ${ip} \ --password-from-stdin \ --tls-security insecure \ --user admin \ migrate ''; }; }; systemd-networkd.wantedBy = [ "multi-user.target" ]; }; network = { enable = true; networks."20-lan" = { matchConfig.Name = "enp0s6"; addresses = [ { Address = "${ip}/24"; } ]; routes = [ { Destination = "0.0.0.0/0"; Gateway = "192.168.50.1"; } ]; dns = [ "1.1.1.1" "8.8.8.8" ]; }; }; }; microvm = { vcpu = 2; mem = 1024 * 4; hypervisor = "qemu"; interfaces = [ { type = "tap"; id = "vm-gel-${user}"; mac = mac; } { type = "user"; id = "vmuser-gel"; mac = userMac; } ]; forwardPorts = [ { from = "host"; host.port = ssh; guest.port = 22; } ]; shares = [ { mountPoint = "/nix/.ro-store"; proto = "virtiofs"; source = "/nix/store"; tag = "read_only_nix_store"; } { mountPoint = "/var/lib/${serviceCfg.name}"; proto = "virtiofs"; source = "${mnt}/${serviceCfg.name}"; tag = "${serviceCfg.name}_${user}_data"; } { mountPoint = "/var/lib/gel-schema"; proto = "virtiofs"; source = "${mnt}/gel-schema"; tag = "${serviceCfg.name}_${user}_schema"; } { mountPoint = "/run/secrets"; proto = "virtiofs"; source = "/run/secrets/${serviceCfg.name}"; tag = "host_secrets"; } ]; }; }; }; }; systemd.tmpfiles.rules = [ "d ${mnt}/${serviceCfg.name} 0751 microvm wheel - -" "d ${mnt}/gel-schema 0751 microvm wheel - -" ]; sops.secrets = { "${serviceCfg.name}/${user}-pass" = { owner = "root"; mode = "0600"; }; "${serviceCfg.name}/bootstrap.edgesql" = { owner = "root"; mode = "0600"; }; }; }; }