Add cloud development host mode

This commit is contained in:
Timothy J. Aveni
2026-06-27 13:30:29 -07:00
parent 29044716ca
commit 7a8204e8a0
6 changed files with 516 additions and 51 deletions
+451 -49
View File
@@ -7,6 +7,9 @@ let
quixosPackages = self.packages.${pkgs.stdenv.hostPlatform.system};
localPersistMount = "${utils.escapeSystemdPath "/persist-local"}.mount";
sharedPersistMount = "${utils.escapeSystemdPath "/persist"}.mount";
devHomeMount = "${utils.escapeSystemdPath cfg.devHome.mountPoint}.mount";
sourceCheckoutService = "quixos-source-checkout.service";
visualizerDevService = "quixos-project-visualizer-dev.service";
secretFiles = {
controlSecret = "${secretsCfg.runtimeDirectory}/control-secret";
stateContextSecret = "${secretsCfg.runtimeDirectory}/state-context-secret";
@@ -100,6 +103,102 @@ let
fi
'';
};
qxdevStatus = pkgs.writeShellApplication {
name = "qxdev-status";
runtimeInputs = [ pkgs.git pkgs.systemd ];
text = ''
source_path=${lib.escapeShellArg cfg.sourceCheckout.path}
echo "source: $source_path"
if [ -d "$source_path/.git" ]; then
git -C "$source_path" status --short --branch
else
echo "source checkout is missing"
fi
echo
systemctl status --no-pager \
quixos-orchestrator.service \
${visualizerDevService} \
caddy.service || true
'';
};
qxdevBootstrap = pkgs.writeShellApplication {
name = "qxdev-bootstrap";
runtimeInputs = [ pkgs.bash pkgs.git pkgs.sudo pkgs.systemd pkgs.yarn-berry_4 ];
text = ''
${lib.optionalString (!cfg.sourceCheckout.enable) ''
echo "Quixos source checkout mode is not enabled on this host." >&2
exit 1
''}
source_path=${lib.escapeShellArg cfg.sourceCheckout.path}
source_user=${lib.escapeShellArg cfg.devHome.user}
sudo systemctl start ${sourceCheckoutService}
if [ ! -d "$source_path/.git" ]; then
echo "source checkout is missing: $source_path" >&2
exit 1
fi
sudo -u "$source_user" -H ${pkgs.bash}/bin/bash -lc ${lib.escapeShellArg ''
set -eu
cd "$1/orchestrator"
${pkgs.yarn-berry_4}/bin/yarn install --immutable
${pkgs.yarn-berry_4}/bin/yarn build
cd "$1/project-visualizer"
${pkgs.yarn-berry_4}/bin/yarn install --immutable
''} qxdev-bootstrap "$source_path"
'';
};
qxdevRestart = pkgs.writeShellApplication {
name = "qxdev-restart";
runtimeInputs = [ pkgs.bash pkgs.sudo pkgs.systemd pkgs.yarn-berry_4 ];
text = ''
${lib.optionalString (!cfg.sourceCheckout.enable) ''
echo "Quixos source checkout mode is not enabled on this host." >&2
exit 1
''}
source_path=${lib.escapeShellArg cfg.sourceCheckout.path}
source_user=${lib.escapeShellArg cfg.devHome.user}
${lib.optionalString cfg.orchestrator.source.enable ''
sudo -u "$source_user" -H ${pkgs.bash}/bin/bash -lc ${lib.escapeShellArg ''
set -eu
cd "$1/orchestrator"
${pkgs.yarn-berry_4}/bin/yarn build
''} qxdev-restart "$source_path"
''}
sudo systemctl restart quixos-orchestrator.service
${lib.optionalString cfg.visualizer.vite.enable ''
sudo systemctl restart ${visualizerDevService}
''}
'';
};
qxdevLogs = pkgs.writeShellApplication {
name = "qxdev-logs";
runtimeInputs = [ pkgs.systemd ];
text = ''
exec journalctl -fu \
quixos-orchestrator.service \
${visualizerDevService} \
caddy.service
'';
};
qxdevOrchLogs = pkgs.writeShellApplication {
name = "qxdev-orch-logs";
runtimeInputs = [ pkgs.systemd ];
text = ''
exec journalctl -fu quixos-orchestrator.service
'';
};
qxdevVizLogs = pkgs.writeShellApplication {
name = "qxdev-viz-logs";
runtimeInputs = [ pkgs.systemd ];
text = ''
exec journalctl -fu ${visualizerDevService}
'';
};
quixosCodexDir = "/persist/var/lib/quixos/codex";
quixosCodexRule = ''prefix_rule(pattern=[\x22qx-control\x22],decision=\x22allow\x22)'';
in
@@ -206,6 +305,114 @@ in
description = "Enable inbound SSH in the NixOS host config. AWS ingress is managed by OpenTofu.";
};
devHome = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Create a persistent development user home on its own ZFS dataset.";
};
user = lib.mkOption {
type = lib.types.str;
default = "quixos-dev";
description = "User that owns the development source checkout.";
};
mountPoint = lib.mkOption {
type = lib.types.str;
default = "/persist-local/home/quixos-dev";
description = "Mount point for the development home dataset.";
};
dataset = lib.mkOption {
type = lib.types.str;
default = "${cfg.persistZfsPool}/dev-home";
description = "ZFS dataset mounted as the development user home.";
};
};
sourceCheckout = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Ensure a mutable Quixos source checkout exists for source-backed development services.";
};
path = lib.mkOption {
type = lib.types.str;
default = "${cfg.devHome.mountPoint}/quixos";
description = "Mutable Quixos source checkout path.";
};
repo = lib.mkOption {
type = lib.types.str;
default = "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git";
description = "Quixos source repository cloned when the source checkout is missing.";
};
ref = lib.mkOption {
type = lib.types.str;
default = "master";
description = "Initial ref checked out when the source checkout is first cloned.";
};
};
orchestrator = {
nix.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Run the Nix-built orchestrator from the immutable Quixos flake.";
};
source = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Run the orchestrator from a mutable source checkout.";
};
path = lib.mkOption {
type = lib.types.str;
default = cfg.sourceCheckout.path;
description = "Quixos source checkout used for the source-backed orchestrator.";
};
};
};
visualizer = {
static.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Serve the Nix-built static project visualizer from Caddy.";
};
vite = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Run the project visualizer through a Vite dev server and proxy it through Caddy.";
};
path = lib.mkOption {
type = lib.types.str;
default = "${cfg.sourceCheckout.path}/project-visualizer";
description = "Project visualizer source directory for Vite dev mode.";
};
host = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "Vite bind host.";
};
port = lib.mkOption {
type = lib.types.port;
default = 4173;
description = "Vite bind port.";
};
};
};
secrets = {
provider = lib.mkOption {
type = lib.types.enum [ "files" "aws-ssm" ];
@@ -284,6 +491,32 @@ in
);
message = "services.quixosHost.secrets.awsSsm.*Parameter options must all be set when provider is aws-ssm.";
}
{
assertion = (if cfg.orchestrator.nix.enable then 1 else 0)
+ (if cfg.orchestrator.source.enable then 1 else 0) == 1;
message = "Exactly one of services.quixosHost.orchestrator.nix.enable or services.quixosHost.orchestrator.source.enable must be true.";
}
{
assertion = (if cfg.visualizer.static.enable then 1 else 0)
+ (if cfg.visualizer.vite.enable then 1 else 0) == 1;
message = "Exactly one of services.quixosHost.visualizer.static.enable or services.quixosHost.visualizer.vite.enable must be true.";
}
{
assertion = (!cfg.sourceCheckout.enable) || cfg.devHome.enable;
message = "services.quixosHost.sourceCheckout.enable requires services.quixosHost.devHome.enable.";
}
{
assertion = (!cfg.orchestrator.source.enable) || cfg.sourceCheckout.enable;
message = "services.quixosHost.orchestrator.source.enable requires services.quixosHost.sourceCheckout.enable.";
}
{
assertion = (!cfg.visualizer.vite.enable) || cfg.sourceCheckout.enable;
message = "services.quixosHost.visualizer.vite.enable requires services.quixosHost.sourceCheckout.enable.";
}
{
assertion = cfg.sourceCheckout.path != cfg.packagesPath;
message = "services.quixosHost.sourceCheckout.path must be separate from services.quixosHost.packagesPath.";
}
];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
@@ -298,32 +531,44 @@ in
pools = [ cfg.nixZfsPool cfg.persistZfsPool cfg.sharedPersistZfsPool ];
};
fileSystems."/nix" = {
device = "${cfg.nixZfsPool}/nix";
fsType = "zfs";
neededForBoot = true;
};
fileSystems = {
"/nix" = {
device = "${cfg.nixZfsPool}/nix";
fsType = "zfs";
neededForBoot = true;
};
fileSystems."/persist-local" = {
device = "${cfg.persistZfsPool}/persist";
fsType = "zfs";
options =
[ "defaults" ]
++ lib.optionals (cfg.persistDevice != null) [
"x-systemd.requires=quixos-persist-init.service"
"x-systemd.after=quixos-persist-init.service"
];
};
"/persist-local" = {
device = "${cfg.persistZfsPool}/persist";
fsType = "zfs";
options =
[ "defaults" ]
++ lib.optionals (cfg.persistDevice != null) [
"x-systemd.requires=quixos-persist-init.service"
"x-systemd.after=quixos-persist-init.service"
];
};
fileSystems."/persist" = {
device = "${cfg.sharedPersistZfsPool}/persist";
fsType = "zfs";
options =
[ "defaults" ]
++ lib.optionals (cfg.sharedPersistDevice != null) [
"x-systemd.requires=quixos-shared-persist-init.service"
"x-systemd.after=quixos-shared-persist-init.service"
"/persist" = {
device = "${cfg.sharedPersistZfsPool}/persist";
fsType = "zfs";
options =
[ "defaults" ]
++ lib.optionals (cfg.sharedPersistDevice != null) [
"x-systemd.requires=quixos-shared-persist-init.service"
"x-systemd.after=quixos-shared-persist-init.service"
];
};
} // lib.optionalAttrs cfg.devHome.enable {
${cfg.devHome.mountPoint} = {
device = cfg.devHome.dataset;
fsType = "zfs";
options = [
"defaults"
"x-systemd.requires=quixos-dev-home-init.service"
"x-systemd.after=quixos-dev-home-init.service"
];
};
};
systemd.services.quixos-persist-init = lib.mkIf (cfg.persistDevice != null) {
@@ -471,17 +716,58 @@ in
'';
};
systemd.services.quixos-dev-home-init = lib.mkIf cfg.devHome.enable {
description = "Initialize Quixos development home ZFS dataset";
requiredBy = [ devHomeMount ];
before = [ devHomeMount "local-fs.target" ];
after = [ localPersistMount ];
requires = [ localPersistMount ];
path = [ pkgs.coreutils pkgs.zfs ];
unitConfig = {
DefaultDependencies = false;
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -eu
dataset=${lib.escapeShellArg cfg.devHome.dataset}
mount_point=${lib.escapeShellArg cfg.devHome.mountPoint}
install -d -m 0755 "$(dirname "$mount_point")"
if ! zfs list -H "$dataset" >/dev/null 2>&1; then
zfs create -o mountpoint=legacy "$dataset"
fi
install -d -m 0755 -o ${lib.escapeShellArg cfg.devHome.user} -g users "$mount_point"
'';
};
users.groups.quixos = {};
users.groups.quixos-control = {};
users.users.quixos = {
isSystemUser = true;
group = "quixos";
extraGroups = [ "quixos-control" ];
home = cfg.dataRoot;
createHome = true;
shell = pkgs.bashInteractive;
users.users = {
quixos = {
isSystemUser = true;
group = "quixos";
extraGroups = [ "quixos-control" ];
home = cfg.dataRoot;
createHome = true;
shell = pkgs.bashInteractive;
};
caddy.extraGroups = [ "quixos" "quixos-control" ];
} // lib.optionalAttrs cfg.devHome.enable {
${cfg.devHome.user} = {
isNormalUser = true;
group = "users";
extraGroups = [ "wheel" "quixos" "quixos-control" ];
home = cfg.devHome.mountPoint;
createHome = true;
shell = pkgs.bashInteractive;
};
};
users.users.caddy.extraGroups = [ "quixos" "quixos-control" ];
environment.systemPackages = [
pkgs.codex
@@ -491,6 +777,12 @@ in
pkgs.amazon-ssm-agent
qxControl
quixosTrackMaster
qxdevStatus
qxdevBootstrap
qxdevRestart
qxdevLogs
qxdevOrchLogs
qxdevVizLogs
];
programs.bash.shellAliases = {
nixrb = "sudo quixos-track-master /etc/nixos/flake.nix && sudo nix --refresh flake update quixos --flake /etc/nixos && sudo nixos-rebuild switch --flake /etc/nixos#quixos-host";
@@ -533,6 +825,7 @@ in
"z /persist 0755 root root -"
"z /persist-local 0755 root root -"
"d /persist/etc/ssh 0700 root root -"
"d /persist-local/home 0755 root root -"
"d /persist-local/srv 0755 root root -"
"d /persist-local/srv/quixos 0755 quixos quixos -"
"d /persist-local/srv/quixos/packages 0755 quixos quixos -"
@@ -557,6 +850,10 @@ in
"quixos-orchestrator.service"
"quixos-codex-app-server.service"
"caddy.service"
] ++ lib.optionals cfg.sourceCheckout.enable [
sourceCheckoutService
] ++ lib.optionals cfg.visualizer.vite.enable [
visualizerDevService
] ++ lib.optionals cfg.enableSsh [
"sshd-keygen.service"
"sshd.service"
@@ -567,12 +864,16 @@ in
"quixos-orchestrator.service"
"quixos-codex-app-server.service"
"caddy.service"
] ++ lib.optionals cfg.sourceCheckout.enable [
sourceCheckoutService
] ++ lib.optionals cfg.visualizer.vite.enable [
visualizerDevService
] ++ lib.optionals cfg.enableSsh [
"sshd-keygen.service"
"sshd.service"
];
after = [ localPersistMount sharedPersistMount ];
requires = [ localPersistMount sharedPersistMount ];
after = [ localPersistMount sharedPersistMount ] ++ lib.optionals cfg.devHome.enable [ devHomeMount ];
requires = [ localPersistMount sharedPersistMount ] ++ lib.optionals cfg.devHome.enable [ devHomeMount ];
path = [ config.systemd.package ];
serviceConfig = {
Type = "oneshot";
@@ -584,6 +885,47 @@ in
'';
};
systemd.services.quixos-source-checkout = lib.mkIf cfg.sourceCheckout.enable {
description = "Prepare Quixos development source checkout";
wantedBy = [ "multi-user.target" ];
before =
lib.optionals cfg.orchestrator.source.enable [ "quixos-orchestrator.service" ]
++ lib.optionals cfg.visualizer.vite.enable [ visualizerDevService ];
after = [ "network-online.target" "quixos-persist-layout.service" ];
wants = [ "network-online.target" ];
requires = [ "quixos-persist-layout.service" ];
path = [ pkgs.coreutils pkgs.git ];
serviceConfig = {
Type = "oneshot";
User = cfg.devHome.user;
Group = "users";
RemainAfterExit = true;
};
script = ''
set -eu
source_path=${lib.escapeShellArg cfg.sourceCheckout.path}
source_parent="$(dirname "$source_path")"
install -d -m 0755 "$source_parent"
if [ -d "$source_path/.git" ]; then
echo "Using existing source checkout: $source_path"
exit 0
fi
if [ -e "$source_path" ]; then
echo "Source checkout path exists but is not a Git worktree: $source_path" >&2
exit 1
fi
git clone ${lib.escapeShellArg cfg.sourceCheckout.repo} "$source_path"
git -C "$source_path" fetch --tags origin '+refs/heads/*:refs/remotes/origin/*'
git -C "$source_path" checkout ${lib.escapeShellArg cfg.sourceCheckout.ref} \
|| git -C "$source_path" checkout refs/remotes/origin/${lib.escapeShellArg cfg.sourceCheckout.ref}
'';
};
systemd.services.sshd-keygen = lib.mkIf cfg.enableSsh {
after = [ "quixos-persist-layout.service" ];
requires = [ "quixos-persist-layout.service" ];
@@ -692,21 +1034,35 @@ in
description = "Quixos orchestrator";
wantedBy = [ "multi-user.target" ];
upheldBy = [ "multi-user.target" ];
restartTriggers = [
quixosPackages.quixos-orchestrator
qxControl
];
after = [ "network-online.target" "nix-daemon.socket" "quixos-persist-layout.service" "quixos-secrets.service" "quixos-package-tree.service" ];
wants = [ "network-online.target" "nix-daemon.socket" "quixos-secrets.service" "quixos-package-tree.service" ];
requires = [ "quixos-persist-layout.service" "quixos-secrets.service" "quixos-package-tree.service" ];
path = [ qxControl ];
restartTriggers =
[ qxControl ]
++ lib.optionals cfg.orchestrator.nix.enable [ quixosPackages.quixos-orchestrator ];
after =
[ "network-online.target" "nix-daemon.socket" "quixos-persist-layout.service" "quixos-secrets.service" "quixos-package-tree.service" ]
++ lib.optionals cfg.orchestrator.source.enable [ sourceCheckoutService ];
wants =
[ "network-online.target" "nix-daemon.socket" "quixos-secrets.service" "quixos-package-tree.service" ]
++ lib.optionals cfg.orchestrator.source.enable [ sourceCheckoutService ];
requires =
[ "quixos-persist-layout.service" "quixos-secrets.service" "quixos-package-tree.service" ]
++ lib.optionals cfg.orchestrator.source.enable [ sourceCheckoutService ];
path =
[ qxControl ]
++ lib.optionals cfg.orchestrator.source.enable [
pkgs.git
pkgs.nodejs_24
pkgs.yarn-berry_4
];
serviceConfig = {
User = "quixos";
Group = "quixos-control";
SupplementaryGroups = [ "quixos" ];
RuntimeDirectory = "quixos";
RuntimeDirectoryMode = "0770";
WorkingDirectory = "/persist-local/srv/quixos";
WorkingDirectory =
if cfg.orchestrator.source.enable
then "${cfg.orchestrator.source.path}/orchestrator"
else "/persist-local/srv/quixos";
Restart = "always";
RestartSec = "5s";
};
@@ -716,7 +1072,16 @@ in
export QUIXOS_CONTROL_SECRET
QUIXOS_STATE_CONTEXT_SECRET="$(cat ${lib.escapeShellArg secretFiles.stateContextSecret})"
export QUIXOS_STATE_CONTEXT_SECRET
exec ${quixosPackages.quixos-orchestrator}/bin/quixos-orchestrator --packages-path ${lib.escapeShellArg cfg.packagesPath}
${if cfg.orchestrator.source.enable then ''
if [ ! -f dist/server.js ]; then
echo "Missing orchestrator build output at $(pwd)/dist/server.js" >&2
echo "Run qxdev-bootstrap or qxdev-restart." >&2
exit 1
fi
exec ${pkgs.yarn-berry_4}/bin/yarn node --enable-source-maps dist/server.js --packages-path ${lib.escapeShellArg cfg.packagesPath}
'' else ''
exec ${quixosPackages.quixos-orchestrator}/bin/quixos-orchestrator --packages-path ${lib.escapeShellArg cfg.packagesPath}
''}
'';
environment = {
GIT_ASKPASS = packageTreeGitAskPass;
@@ -727,9 +1092,15 @@ in
GIT_COMMITTER_EMAIL = "quixos-runtime@localhost";
XDG_DATA_HOME = cfg.dataRoot;
QUIXOS_WORKSPACE_ROOT = "/persist-local/srv/quixos";
QUIXOS_APP_ROOT = "${self}";
QUIXOS_APP_ROOT =
if cfg.orchestrator.source.enable
then cfg.orchestrator.source.path
else "${self}";
QUIXOS_PACKAGES_PATH = cfg.packagesPath;
QUIXOS_PACKAGE_TEMPLATES_DIR = "${self}/package-templates";
QUIXOS_PACKAGE_TEMPLATES_DIR =
if cfg.orchestrator.source.enable
then "${cfg.orchestrator.source.path}/package-templates"
else "${self}/package-templates";
QUIXOS_CONTROL_SOCKET = "/run/quixos/control.sock";
QUIXOS_DEVICE_ASSIGNMENTS_FILE = "${cfg.packagesPath}/device-assignments.json";
QUIXOS_REMOTE_DOM_RELAY_SOCKET = "/run/quixos/relay.sock";
@@ -738,6 +1109,32 @@ in
};
};
systemd.services.quixos-project-visualizer-dev = lib.mkIf cfg.visualizer.vite.enable {
description = "Quixos project visualizer Vite dev server";
wantedBy = [ "multi-user.target" ];
upheldBy = [ "multi-user.target" ];
after = [ "network-online.target" "quixos-persist-layout.service" sourceCheckoutService ];
wants = [ "network-online.target" sourceCheckoutService ];
requires = [ "quixos-persist-layout.service" sourceCheckoutService ];
path = [
pkgs.nodejs_24
pkgs.yarn-berry_4
];
serviceConfig = {
User = cfg.devHome.user;
Group = "users";
WorkingDirectory = cfg.visualizer.vite.path;
Restart = "always";
RestartSec = "5s";
};
environment = {
QUIXOS_VITE_ALLOWED_HOSTS = cfg.domain;
};
script = ''
exec ${pkgs.yarn-berry_4}/bin/yarn dev --host ${lib.escapeShellArg cfg.visualizer.vite.host} --port ${toString cfg.visualizer.vite.port}
'';
};
systemd.services.quixos-codex-app-server = {
description = "Codex app server";
wantedBy = [ "multi-user.target" ];
@@ -793,18 +1190,23 @@ in
}
handle {
root * ${quixosPackages.project-visualizer-static}
file_server
${if cfg.visualizer.vite.enable then ''
reverse_proxy ${cfg.visualizer.vite.host}:${toString cfg.visualizer.vite.port}
'' else ''
root * ${quixosPackages.project-visualizer-static}
file_server
''}
}
'';
};
systemd.services.caddy = {
upheldBy = [ "multi-user.target" ];
reloadTriggers = [
reloadTriggers = lib.optionals cfg.visualizer.static.enable [
quixosPackages.project-visualizer-static
];
after = [ "quixos-persist-layout.service" ];
after = [ "quixos-persist-layout.service" ] ++ lib.optionals cfg.visualizer.vite.enable [ visualizerDevService ];
wants = lib.optionals cfg.visualizer.vite.enable [ visualizerDevService ];
requires = [ "quixos-persist-layout.service" ];
};
};
+6 -1
View File
@@ -1,13 +1,18 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
const configuredAllowedHosts = process.env.QUIXOS_VITE_ALLOWED_HOSTS
?.split(",")
.map((host) => host.trim())
.filter(Boolean);
export default defineConfig({
plugins: [react()],
server: {
host: "0.0.0.0",
port: 4173,
strictPort: true,
allowedHosts: [
allowedHosts: configuredAllowedHosts ?? [
"quixos-orchestrator.tutti.internal.tutti.syntaxblitz.net",
"quixos-orchestrator.tutti.syntaxblitz.net",
],
+35 -1
View File
@@ -22,6 +22,8 @@ enable_ssh_with_local_key=false
ssh_mode_specified=false
ssh_public_key_file=""
ssh_public_key=""
dev_mode=false
dev_mode_specified=false
usage() {
cat >&2 <<'EOF'
@@ -46,6 +48,8 @@ Options:
--quixos-master Deploy Quixos from the master branch instead of package-tree metadata
--quixos-from-package-tree
Pin Quixos to the rev recorded in package-tree metadata
--dev Enable cloud development mode
--no-dev Disable cloud development mode
--enable-ssh-with-local-key
Enable SSH using the first local ~/.ssh/*.pub key found
--no-ssh Do not enable SSH
@@ -202,6 +206,16 @@ while [ "$#" -gt 0 ]; do
quixos_source_specified=true
shift
;;
--dev)
dev_mode=true
dev_mode_specified=true
shift
;;
--no-dev)
dev_mode=false
dev_mode_specified=true
shift
;;
--enable-ssh-with-local-key)
enable_ssh_with_local_key=true
ssh_mode_specified=true
@@ -282,6 +296,20 @@ if [ "$quixos_source_specified" = false ]; then
esac
fi
if [ "$dev_mode_specified" = false ]; then
require_interactive_or_explicit \
"Development mode was not specified. Pass --dev or --no-dev for noninteractive deploys."
dev_choice="$(prompt_choice "Enable cloud development mode? [y/N]" ":y:Y:n:N:" "n")"
case "$dev_choice" in
y|Y)
dev_mode=true
;;
n|N)
dev_mode=false
;;
esac
fi
if [ "$enable_ssh_with_local_key" = true ]; then
select_local_ssh_public_key
fi
@@ -414,6 +442,11 @@ if [ "$enable_ssh_with_local_key" = true ]; then
else
echo "SSH: disabled" >&2
fi
if [ "$dev_mode" = true ]; then
echo "Dev mode: enabled" >&2
else
echo "Dev mode: disabled" >&2
fi
cd "$script_dir"
@@ -439,7 +472,8 @@ fi
apply_args=(
-var "package_tree_ref=$resolved_packages_ref" \
-var "quixos_ref=$quixos_ref" \
-var "quixos_ref_is_rev=$quixos_ref_is_rev"
-var "quixos_ref_is_rev=$quixos_ref_is_rev" \
-var "dev_mode=$dev_mode"
)
if [ "$explicit_package_tree_repo_url" = true ]; then
+3
View File
@@ -192,6 +192,7 @@ resource "aws_instance" "host" {
user_data = templatefile("${path.module}/user-data-flake.nix.tftpl", {
admin_user = var.admin_user
control_secret_parameter = aws_ssm_parameter.control_secret.name
dev_mode = var.dev_mode
domain = local.domain
enable_ssh = var.enable_ssh
force_package_tree_ref_on_boot = var.force_package_tree_ref_on_boot
@@ -200,6 +201,8 @@ resource "aws_instance" "host" {
package_tree_ref = var.package_tree_ref
package_tree_repo_url = var.package_tree_repo_url
persist_device = local.persist_device
quixos_ref = var.quixos_ref
quixos_repo_url = var.quixos_repo_url
quixos_flake_uri = local.quixos_flake_uri
shared_persist_device = local.shared_volume_id == null ? "null" : "\"${local.shared_persist_device}\""
ssh_authorized_keys = jsonencode(local.ssh_keys)
+15
View File
@@ -35,6 +35,21 @@
forcePackageTreeRefOnBoot = ${force_package_tree_ref_on_boot};
enableSsh = ${enable_ssh};
devHome.enable = ${dev_mode};
sourceCheckout = {
enable = ${dev_mode};
repo = "${quixos_repo_url}";
ref = "${quixos_ref}";
};
orchestrator = {
nix.enable = ${!dev_mode};
source.enable = ${dev_mode};
};
visualizer = {
static.enable = ${!dev_mode};
vite.enable = ${dev_mode};
};
secrets = {
provider = "aws-ssm";
awsSsm = {
+6
View File
@@ -137,6 +137,12 @@ variable "force_package_tree_ref_on_boot" {
description = "Force an existing package tree back to package_tree_ref on every boot."
}
variable "dev_mode" {
type = bool
default = false
description = "Enable cloud development services: persistent dev home, mutable Quixos checkout, source orchestrator, and Vite visualizer."
}
variable "enable_ssh" {
type = bool
default = false