PatOS is born!
This commit is contained in:
parent
0f3b2072f4
commit
44d8f9c90d
12 changed files with 383 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use flake
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.env
|
||||
.direnv
|
||||
.task
|
||||
result
|
||||
.*.swp
|
||||
.*.swo
|
11
base.nix
Normal file
11
base.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
{ ... }: {
|
||||
imports = [
|
||||
./modules/system_overrides.nix
|
||||
./modules/minimize.nix
|
||||
./modules/generic.nix
|
||||
./modules/filesystems.nix
|
||||
./modules/partitions.nix
|
||||
./modules/network.nix
|
||||
./modules/sysupdate.nix
|
||||
];
|
||||
}
|
27
flake.lock
Normal file
27
flake.lock
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1725983898,
|
||||
"narHash": "sha256-4b3A9zPpxAxLnkF9MawJNHDtOOl6ruL0r6Og1TEDGCE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1355a0cbfeac61d785b7183c0caaec1f97361b43",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
70
flake.nix
Normal file
70
flake.nix
Normal file
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
description = "PatOS is a minimal, immutable Linux distribution specialized for the Patagia Platform.";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs }: {
|
||||
lib = {
|
||||
# Prepare a ready-to-boot disk image.
|
||||
mkInstallImage = nixos:
|
||||
let
|
||||
config = nixos.config;
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux.pkgs;
|
||||
in
|
||||
nixos.pkgs.runCommand "update-${config.system.image.version}"
|
||||
{
|
||||
nativeBuildInputs = with pkgs; [ qemu ];
|
||||
} ''
|
||||
mkdir -p $out
|
||||
qemu-img convert -f raw -O qcow2 -C ${config.system.build.image}/${config.boot.uki.name}_${config.system.image.version}.raw $out/disk.qcow2
|
||||
'';
|
||||
};
|
||||
|
||||
devShells.x86_64-linux.default =
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [
|
||||
self.packages.x86_64-linux.qemu-efi
|
||||
];
|
||||
};
|
||||
|
||||
packages.x86_64-linux = {
|
||||
default = self.packages.x86_64-linux.patos_image;
|
||||
|
||||
patos_image = self.lib.mkInstallImage self.nixosConfigurations.patos;
|
||||
|
||||
# A helper script to run the disk images above.
|
||||
qemu-efi =
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
in
|
||||
pkgs.writeShellApplication {
|
||||
name = "qemu-efi";
|
||||
|
||||
runtimeInputs = [ pkgs.qemu_kvm ];
|
||||
|
||||
text = ''
|
||||
qemu-system-x86_64 \
|
||||
-smp 2 -m 2048 -machine q35,accel=kvm \
|
||||
-bios ${pkgs.OVMF.fd}/FV/OVMF.fd \
|
||||
-snapshot \
|
||||
-display none \
|
||||
-serial stdio "$@"
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
nixosConfigurations = {
|
||||
patos = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
./base.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
44
modules/filesystems.nix
Normal file
44
modules/filesystems.nix
Normal file
|
@ -0,0 +1,44 @@
|
|||
{ config, ... }: {
|
||||
|
||||
zramSwap = {
|
||||
enable = true;
|
||||
algorithm = "zstd";
|
||||
memoryPercent = 20;
|
||||
};
|
||||
|
||||
fileSystems = {
|
||||
"/" = {
|
||||
fsType = "tmpfs";
|
||||
options = [
|
||||
"size=20%"
|
||||
];
|
||||
};
|
||||
|
||||
"/var" =
|
||||
let
|
||||
partConf = config.image.repart.partitions."var".repartConfig;
|
||||
in
|
||||
{
|
||||
device = "/dev/disk/by-partuuid/${partConf.UUID}";
|
||||
fsType = partConf.Format;
|
||||
};
|
||||
|
||||
"/boot" =
|
||||
let
|
||||
partConf = config.image.repart.partitions."esp".repartConfig;
|
||||
in
|
||||
{
|
||||
device = "/dev/disk/by-partuuid/${partConf.UUID}";
|
||||
fsType = partConf.Format;
|
||||
};
|
||||
|
||||
"/nix/store" =
|
||||
let
|
||||
partConf = config.image.repart.partitions."store".repartConfig;
|
||||
in
|
||||
{
|
||||
device = "/dev/disk/by-partlabel/${partConf.Label}";
|
||||
fsType = partConf.Format;
|
||||
};
|
||||
};
|
||||
}
|
41
modules/generic.nix
Normal file
41
modules/generic.nix
Normal file
|
@ -0,0 +1,41 @@
|
|||
{ pkgs, config, ... }: {
|
||||
|
||||
boot.uki.name = "patos";
|
||||
boot.kernelParams = [ "console=ttyS0" ];
|
||||
|
||||
system.nixos.release = "2024-09";
|
||||
system.nixos.codeName = "Finn";
|
||||
|
||||
system.nixos.distroId = "patos";
|
||||
system.nixos.distroName = "PatOS";
|
||||
system.image.version = "0.0.1"; # FIXME: Use epoch version.
|
||||
|
||||
# Make the current system version visible in the prompt.
|
||||
programs.bash.promptInit = ''
|
||||
export PS1="\u@\h (version ${config.system.image.version}) $ "
|
||||
'';
|
||||
|
||||
# Not compatible with system.etc.overlay.enable yet.
|
||||
# users.mutableUsers = false;
|
||||
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
boot.initrd.systemd.enable = true;
|
||||
|
||||
# Don't accumulate crap.
|
||||
boot.tmp.cleanOnBoot = true;
|
||||
services.journald.extraConfig = ''
|
||||
SystemMaxUse=10M
|
||||
'';
|
||||
|
||||
# Debugging
|
||||
environment.systemPackages = with pkgs; [
|
||||
parted
|
||||
(runCommand "systemd-sysupdate" { } ''
|
||||
mkdir -p $out/bin
|
||||
ln -s ${config.systemd.package}/lib/systemd/systemd-sysupdate $out/bin
|
||||
'')
|
||||
];
|
||||
|
||||
system.stateVersion = "24.11";
|
||||
}
|
19
modules/minimize.nix
Normal file
19
modules/minimize.nix
Normal file
|
@ -0,0 +1,19 @@
|
|||
{ modulesPath, ... }: {
|
||||
imports = [
|
||||
"${modulesPath}/profiles/minimal.nix"
|
||||
];
|
||||
|
||||
boot.loader.grub.enable = false;
|
||||
|
||||
system.switch.enable = false;
|
||||
nix.enable = false;
|
||||
|
||||
system.etc.overlay.enable = true;
|
||||
systemd.sysusers.enable = true;
|
||||
|
||||
system.disableInstallerTools = true;
|
||||
programs.less.lessopen = null;
|
||||
programs.command-not-found.enable = false;
|
||||
boot.enableContainers = false;
|
||||
environment.defaultPackages = [ ];
|
||||
}
|
11
modules/network.nix
Normal file
11
modules/network.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
{ config, ... }: {
|
||||
networking = {
|
||||
useNetworkd = true;
|
||||
|
||||
# Easy debugging.
|
||||
firewall.enable = false;
|
||||
};
|
||||
|
||||
# Faster boot.
|
||||
systemd.network.wait-online.enable = false;
|
||||
}
|
85
modules/partitions.nix
Normal file
85
modules/partitions.nix
Normal file
|
@ -0,0 +1,85 @@
|
|||
{ config, pkgs, lib, modulesPath, ... }: {
|
||||
|
||||
imports = [
|
||||
"${modulesPath}/image/repart.nix"
|
||||
];
|
||||
|
||||
image.repart =
|
||||
let
|
||||
efiArch = pkgs.stdenv.hostPlatform.efiArch;
|
||||
in
|
||||
{
|
||||
name = config.boot.uki.name;
|
||||
split = true;
|
||||
|
||||
partitions = {
|
||||
"esp" = {
|
||||
contents = {
|
||||
"/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
|
||||
"${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
|
||||
|
||||
"/EFI/Linux/${config.system.boot.loader.ukiFile}".source =
|
||||
"${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
|
||||
|
||||
# systemd-boot configuration
|
||||
"/loader/loader.conf".source = (pkgs.writeText "$out" ''
|
||||
timeout 3
|
||||
'');
|
||||
};
|
||||
repartConfig = {
|
||||
Type = "esp";
|
||||
UUID = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"; # Well known
|
||||
Format = "vfat";
|
||||
SizeMinBytes = "256M";
|
||||
SplitName = "-";
|
||||
};
|
||||
};
|
||||
"store" = {
|
||||
storePaths = [ config.system.build.toplevel ];
|
||||
stripNixStorePrefix = true;
|
||||
repartConfig = {
|
||||
Type = "linux-generic";
|
||||
Label = "store_${config.system.image.version}";
|
||||
Format = "squashfs";
|
||||
Minimize = "off";
|
||||
ReadOnly = "yes";
|
||||
|
||||
SizeMinBytes = "1G";
|
||||
SizeMaxBytes = "1G";
|
||||
SplitName = "store";
|
||||
};
|
||||
};
|
||||
|
||||
# Placeholder for the second installed Nix store.
|
||||
"store-empty" = {
|
||||
repartConfig = {
|
||||
Type = "linux-generic";
|
||||
Label = "_empty";
|
||||
Minimize = "off";
|
||||
SizeMinBytes = "1G";
|
||||
SizeMaxBytes = "1G";
|
||||
SplitName = "-";
|
||||
};
|
||||
};
|
||||
|
||||
# Persistent storage
|
||||
"var" = {
|
||||
repartConfig = {
|
||||
Type = "var";
|
||||
UUID = "4d21b016-b534-45c2-a9fb-5c16e091fd2d"; # Well known
|
||||
Format = "xfs";
|
||||
Label = "nixos-persistent";
|
||||
Minimize = "off";
|
||||
|
||||
# Has to be large enough to hold update files.
|
||||
SizeMinBytes = "2G";
|
||||
SizeMaxBytes = "2G";
|
||||
SplitName = "-";
|
||||
|
||||
# Wiping this gives us a clean state.
|
||||
FactoryReset = "yes";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
5
modules/system_overrides.nix
Normal file
5
modules/system_overrides.nix
Normal file
|
@ -0,0 +1,5 @@
|
|||
{ lib, options, ... }: {
|
||||
# This fields is immutable by default, but can be overridden.
|
||||
options.system.nixos.codeName = lib.mkOption { readOnly = false; };
|
||||
options.system.nixos.release = lib.mkOption { readOnly = false; };
|
||||
}
|
63
modules/sysupdate.nix
Normal file
63
modules/sysupdate.nix
Normal file
|
@ -0,0 +1,63 @@
|
|||
{ config, ... }: {
|
||||
systemd.sysupdate = {
|
||||
enable = true;
|
||||
|
||||
transfers = {
|
||||
"10-uki" = {
|
||||
Source = {
|
||||
MatchPattern = [
|
||||
"${config.boot.uki.name}_@v.efi.xz"
|
||||
];
|
||||
|
||||
# We could fetch updates from the network as well:
|
||||
#
|
||||
# Path = "https://download.example.com/";
|
||||
# Type = "url-file";
|
||||
Path = "/var/updates/";
|
||||
Type = "regular-file";
|
||||
};
|
||||
Target = {
|
||||
InstancesMax = 2;
|
||||
MatchPattern = [
|
||||
"${config.boot.uki.name}_@v.efi"
|
||||
];
|
||||
|
||||
Mode = "0444";
|
||||
Path = "/EFI/Linux";
|
||||
PathRelativeTo = "boot";
|
||||
|
||||
Type = "regular-file";
|
||||
};
|
||||
Transfer = {
|
||||
ProtectVersion = "%A";
|
||||
};
|
||||
};
|
||||
|
||||
"20-store" = {
|
||||
Source = {
|
||||
MatchPattern = [
|
||||
"store_@v.img.xz"
|
||||
];
|
||||
# Path = "https://download.example.com/";
|
||||
# Type = "url-file";
|
||||
Path = "/var/updates/";
|
||||
Type = "regular-file";
|
||||
};
|
||||
|
||||
Target = {
|
||||
InstancesMax = 2;
|
||||
|
||||
# This doesn't work, because / is a tmpfs and the heuristic is not that smart.
|
||||
#
|
||||
# Path = "auto";
|
||||
Path = "/dev/sda";
|
||||
|
||||
MatchPattern = "store_@v";
|
||||
|
||||
Type = "partition";
|
||||
ReadOnly = "yes";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue