Generated Computers
Locals: an Apple Silicon engine that runs local LLMs up to 3x faster. A product of Generated Computers.
翻译准备中
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证NixithOS: NixOS without systemd, on s6-rc
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗NixOS without systemd: PID 1 is s6-linux-init, services are run by
s6-rc (or, if you choose it, both are dinit), devices by eudev, logs
by s6-log.
This is not a rewritten distribution but a layer on top of ordinary nixpkgs
(currently the nixos-26.05 branch). The configuration is still a NixOS
configuration: the same configuration.nix, the same options, the same
service modules. What changes is what runs underneath them.
{ pkgs, ... }: {
networking.hostName = "box";
services.openssh.enable = true;
users.users.alice.isNormalUser = true;
# your own service is described the same way as in NixOS
systemd.services.hello = {
wantedBy = [ "multi-user.target" ];
serviceConfig.ExecStart = "${pkgs.hello}/bin/hello";
};
}
$ cat /proc/1/comm
s6-svscan
$ s6-rc -a list | head -3
basic.target
dbus
dbus-log
In NixOS every daemon, mount point and boot step is described by a systemd
unit; two thousand modules are written that way. Throwing them away would be
silly, so here systemd.services.* stays the data model: the units are
generated by the same code as in NixOS, and then the unitc compiler (it
runs only while the system is built) turns the directory of units into an
s6-rc service database.
| unit | what it becomes in s6-rc |
|---|---|
foo.service, Type=oneshot | oneshot foo |
foo.service, any other type | longrun foo + its logger foo-log |
foo.target | an empty oneshot: a synchronisation point |
foo.mount, foo.swap | oneshot running mount / swapon |
foo.socket | oneshot: the socket is bound in advance and kept in s6-fdholder; the service takes it on every start |
foo.timer | longrun with a scheduler (OnCalendar= and the rest) |
dev-….device | oneshot that waits for the device to appear |
The process of every service is started by unit-runtime: the environment,
User=, directories (RuntimeDirectory= and the like), limits,
capabilities, credentials, readiness (Type=notify, forking, dbus),
Restart=, ExecStartPre/Post, ExecStop, a cgroup per service.
s6-rc has one kind of dependency: "start after, require, restart together".
In systemd, ordering (After=) and requirement (Requires=) are different
things. If all the ordering were turned into dependencies, adding any timer
during nixith switch would restart half the system, because everything is
"after basic.target", and that one is "after timers.target".
So two databases are compiled from the same services:
compiled-boot: all the ordering is written as dependencies. The
system boots from it: s6-rc brings everything up in the right order and as
much in parallel as the order allows.compiled: only the real dependencies (Requires=, BindsTo=). The
system switches to it right after boot (s6-rc-update, without a single
restart) and lives on it.The order between units that are later started or stopped together
(systemctl start, switching the configuration, shutdown) is recovered from
a "rank": a number that grows along every After=.
More in docs/design.md.
The init system is a setting: nixithos.init = "dinit"; (the installer asks
too). dinit is PID 1 and the service manager then, and each user's manager
is dinit --user; the units, systemctl (with --user too), journalctl
and the logs stay the same. The unit compiler writes dinit's service
descriptions from the same graph, and since dinit tells requirement from
ordering itself (depends-on, waits-for, after), one set of them does
for the boot and for what comes after: no second database. A unit's
processes run under unit-supervise, which does for them what
s6-supervise does under s6. A switch to the other init system applies the
rest of the configuration at once and the init system at the next boot.
$ cat /proc/1/comm
dinit
$ dinitctl list | head -3
[[+] ] boot
[{+} ] default
[{+} ] dbus (pid: 612)
| was | is |
|---|---|
| systemd (PID 1) | s6-linux-init → s6-svscan |
units, systemctl | s6-rc; systemctl stays as a thin wrapper |
journald, journalctl | s6-log in /var/log/s6/<service>/, syslog through s6-socklog; journalctl -u … reads these directories |
| systemd-udevd | eudev; .link files (MTU, MAC, name) are applied by unit-link |
| systemd-hostnamed | hostnamectl and a small hostname-agent for users such as dhcpcd |
| systemd-tmpfiles, sysusers | sd-tools (the same tmpfiles.d rules) |
| systemd in the initrd | the scripted NixOS initrd with eudev |
| dbus-broker | dbus-daemon |
| systemd-logind | elogind (services.elogind.enable; turns itself on when an X server, a display manager or a Wayland compositor is enabled) |
| systemd-timesyncd | our own SNTP client (unit-sntp), the same services.timesyncd.* options |
PrivateTmp=, ProtectSystem= and the like | a mount namespace, unit-sandbox |
systemd --user | one s6-svscan per user, started with the first session (PAM) |
| socket activation | sockets in s6-fdholder (they survive a service restart) |
switch-to-configuration | our own, on top of s6-rc-update |
| boot loaders | GRUB, Limine as they were; systemd-boot: bootctl and the EFI files from the systemd package, nothing else from it |
systemd is never started, but nixpkgs packages are built with support for
its libraries (libsystemd, libudev). What happens to that is decided by
s6.systemdPackages:
| value | what happens | cost |
|---|---|---|
"replace" (default) | packages come from the cache as they are; underneath them libsystemd → libelogind, libudev → eudev are swapped in | nothing is compiled; the binaries contain unused systemd code |
"rebuild" | packages are built without systemd support, udev → eudev | there is no cache for these variants: hours of compiling for a desktop. The only mode that changes the packages themselves |
"keep" | nothing | the systemd libraries stay in the closure |
Except with keep, a system whose closure still contains systemd does not
build. There are no exceptions besides the boot loader: when
boot.loader.systemd-boot is enabled, bootctl and the EFI files are taken
from the systemd package (copied with fixed-up paths, without the rest of the
package): that is a boot loader, not an init.
The init system and everything tied to it changes; the packages stay the
same. In replace mode they are literally the same binaries from the cache.
Works as before: everything that is simply started as a daemon or a command, which is nearly all of nixpkgs: servers, databases, browsers, desktops, games, Nix itself. Their NixOS modules are the same two thousand, unmodified.
Works through replacement: what talked to systemd through its libraries.
sd_notify, sd_listen_fds, sd_login_* and logind's D-Bus services
(polkit, desktops, libseat) are served by elogind. Of the public API of
libsystemd, elogind lacks twenty functions (mostly sd_device_monitor_*,
sd_bus_creds_*pidfd*) and about two hundred more for varlink and JSON; a
program calling them gets -ENOSYS (or NULL) and still runs.
Does not work, because it is systemd: systemd-networkd, resolved,
machined, homed, nspawn, oomd, repart, sysupdate, journald as a
structured journal, systemd-cryptsetup and a systemd-based initrd. The
options of these modules are there (other modules read them), the
implementation is not; what replaces what is in the table above and in
docs/status.md. networking.useNetworkd is forced off:
interfaces, addresses and routes from networking.* are set up by the
scripted network, and nothing applies a hand-written systemd.network.*
(you get a warning). Programs that check the state of systemd
(systemctl is-system-running, busctl, the journal by fields) get the
thin wrapper's answer or an error.
The system is built and switched by nixith, in place of nixos-rebuild and
the other nixos-* tools (they are no longer in PATH; nixith calls them
itself where needed). The configuration lives in /etc/nixith (a system
installed earlier moves /etc/nixos there on activation and leaves a link in
the old place).
$ sudo nixith switch # build, make it the boot default, switch to it
$ sudo nixith boot | test # only the boot default / only switch
$ nixith build | dry-build # build (./result) / show what would be built
$ nixith build-vm # the same in QEMU
$ sudo nixith rollback # back to the previous generation
$ nixith generations # the system's generations
$ sudo nixith switch --flake /etc/nixith#box # flake: nixithConfigurations.box
Rebuild part of the configuration without touching the rest:
$ sudo nixith switch --only services.nginx
$ sudo nixith switch --only environment.systemPackages,users.users
$ nixith parts # what the running system was built from
--only takes the path of an option or a group of options. Options under
those paths get the values the configuration gives them now; everything
else gets the values of the running system, even if the files have changed
since. So you can fix and apply one service and leave unfinished edits
elsewhere in the file for later. The rest evaluates to the same store paths,
so switch-to-configuration leaves it alone (checked: after
--only services.openssh.ports the only difference in the new system is
etc/ssh/sshd_config, while sw, the kernel and the initrd are the same
paths).
How it works (lib/nixith.nix): every system built by
nixith keeps in nixith/source.json a copy of the configuration directory
(or flake) it was built from, with the tree and nixpkgs it was evaluated
with; this is part of its closure. A partial build evaluates that base as it
was and adds a "layer": the definitions of the options under --only from
the current configuration, above all definitions of the base. Further partial
builds stack as layers on top (nixith parts shows them); a full
nixith switch starts from a new base. This means the configuration
directory is no place for secrets: it is copied into the store, like
everything else the configuration refers to by path.
The other tools are subcommands or nixith-*: nixith install
(nixith-install), nixith generate-config (writes to /etc/nixith),
nixith enter, nixith option, nixith version, nixith edit.
Both forms work, and nixpkgs stays yours:
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
inputs.nixithos.url = "github:YoungEscapist/nixithos";
inputs.nixithos.inputs.nixpkgs.follows = "nixpkgs";
outputs = { nixpkgs, nixithos, ... }: {
# the stock nixosSystem plus one module
nixosConfigurations.box = nixpkgs.lib.nixosSystem {
modules = [ nixithos.nixosModules.default ./configuration.nix ];
};
# or the same through the ready-made wrapper; `nixith --flake .#box2`
# looks in nixithConfigurations first, then in nixosConfigurations
nixithConfigurations.box2 = nixithos.lib.system {
modules = [ ./configuration.nix ];
};
};
}
nixosModules.default uses disabledModules to switch off the nixpkgs
modules that are the systemd integration, brings back their options
(without an implementation) and adds the modules of this tree. The module
adds the overlay with this tree's packages (pkgs.nixithos) through
nixpkgs.overlays by itself; if you set nixpkgs.pkgs to a ready-made
package set, add nixithos.overlays.default to it.
The nixpkgs revision has to be the one the module list was written for
(modules/parts.nix checks this during evaluation and says exactly what
moved).
The system calls itself NixithOS: NAME=NixithOS, ID=nixithos in
/etc/os-release, and so in the boot menu, in the console greeting and for
everything else that reads that file. A machine without a name is called
nixith, the configuration lives in /etc/nixith, the system in the store
is nixith-system-…, the ISO's live user is nixith, the commands are
nixith …. Only ID_LIKE=nixos remains: it is not a name but information
for programs ("behaves like NixOS"), which tells them the layout of the
system. The mark is branding/nixithos.png, also drawn
in characters in branding/logo.txt:
.
:
_____ | _____
\ | /
\ | /
______ \ / ______
\ \ / /
\ ___|___ /
/ / \ \
/ / \ \
. --- -------< < > >------- --- .
\ \ / /
\ \_______/ /
/ | \
______/ / \ \______
/ \
/ | \
_____/ | \_____
|
:
'
fastfetch shows it by itself: the system has
/etc/xdg/fastfetch/config.jsonc with this logo (a user's own
~/.config/fastfetch/config.jsonc takes precedence). fastfetch itself is
installed with nixithos.branding.fastfetch.install = true; (a build
without optional dependencies) or any other way you like.
nixithos.branding.enable = false; brings back the NixOS name.
The mark also exists as pictures (nixithos.branding.artwork): the
nixithos icon in share/icons/hicolor and share/pixmaps (referred to by
LOGO=nixithos in os-release) and the backgrounds of the ISO's boot menu.
$ nix-build -A iso # result/iso/nixithos-26.05…-x86_64-linux.iso (1.8 GB)
$ nix-build -A isoGraphical # …-graphical-…: LXQt and Calamares (2.8 GB)
The main image (images/iso.nix), the one in the releases,
is the stock minimal NixOS installation image on this tree with an installer
of our own in the console: it boots via BIOS (syslinux) and UEFI (GRUB), the
menu shows the NixithOS mark, then the live user nixith (no password) is
logged in on tty1 and nixith-installer starts by itself
(pkgs/nixith-installer).
nixith-installer is a small C program over ncurses (about 100 KB) that
speaks English or Russian. Its steps are those of the Calamares installer
below, with the same screenshots next to every choice and the same slides
during the installation (on the Linux console they are drawn straight to the
framebuffer), and with a network step in front: a simpler nmtui on top of
nmcli. A cable connects by itself; a network is a form, not just a
password: WPA2, WPA3, WEP or 802.1X with a login, hidden networks, a cable
with the address, DNS and MAC by hand, DSL / PPPoE. The connection made there
is copied into the installed system. nixith-net starts that step alone, on
any system. One step Calamares did not have is the kernel: the long-term one
NixOS uses by default (it is on the image), the newest, the older long-term
6.12 or 6.6, Zen or XanMod (boot.kernelPackages; all of them come from the
NixOS cache). After it comes the init system: s6, or dinit
(nixithos.init). Nothing is written until the summary is confirmed; then install.sh
partitions the disk (GPT: a 1 GiB EFI partition, or a BIOS boot partition,
and ext4 or btrfs for the rest) or takes what is mounted on /mnt, writes
/etc/nixith/configuration.nix the way Calamares did, with the passwords as
yescrypt hashes, and runs nixith install, retrying when a download breaks
off. Parallax is carried on the image, so installing it is not compiling it.
Checked in QEMU end to end (UEFI, Parallax, ly): the installation, booting
the installed system, logging in with the password given in the installer.
By hand, without the installer:
# nixith generate-config --root /mnt
# nixith install
nixith install installs NixithOS the same way nixith switch builds it
(lib/nixith.nix), so the installed system can do
nixith switch --only … from its first generation. Checked in QEMU: GPT
partitioning, systemd-boot, booting the installed system.
The graphical one is images/iso-graphical.nix: a
live system with LXQt on labwc on tty1 (automatic login as nixith without a
password, modules/desktop/lxqt.nix), and with it
the graphical installer opens: Calamares with the NixithOS mark and slides
(pkgs/calamares-nixithos). Once closed, the
installer starts again from the menu (System → "Install NixithOS").
The installer's steps: language and time zone, keyboard layout, user, then what the system will have:
| step | choice | in the configuration |
|---|---|---|
| Desktop | dwl, Parallax, GNOME, KDE Plasma, Hyprland, niri or no desktop | nixithos.desktop.<name>.enable |
| Login | ly, greetd (tuigreet) or the console | nixithos.desktop.displayManager |
| Shell | Bash, Zsh, fish, Nushell | users.users.<name>.shell |
| Bootloader | systemd-boot, Limine, GRUB, rEFInd | boot.loader.* |
then unfree software and partitioning. On BIOS the boot loader is always
GRUB: the other three are UEFI only here. Without a display manager the
automatic login is the console's, and dwl starts by itself on logging in on
tty1; the other desktops are then started by hand. The installer writes the
configuration to /etc/nixith and installs it with nixith install.
Parallax is on the image (the NixOS cache does not have it); the other
desktops are downloaded from the cache during installation, so a network is
needed.
Every choice on these steps comes with a screenshot of a real NixithOS
(branding/screenshots): desktops with open windows,
shells in a terminal, ly/greetd/console, boot loader menus. They were taken
in QEMU (desktops and shells from a configuration like configs.desktops,
menus by a test like tests/bootloader.nix with a long menu timeout); niri
as a nested window filling the screen, since it does not draw by itself with
software rendering. While the installation runs, 17 tips go by (show.qml:
nixith, generations, services and logs, packages, desktops, shells); a
click or the arrow keys show the next one.
The desktops were checked as a whole in QEMU
(nix-build -A configs.desktops.vm, login yarik/yarik): ly on tty1;
Parallax, GNOME, Plasma (Wayland) and Hyprland log in and draw a desktop;
niri starts completely but refuses to draw on QEMU's software rendering
("software EGL renderers are skipped"): it needs a GPU. The GNOME 50 lock
screen needs GDM, so with ly it is off (GNOME says so itself at login).
You need Nix. It downloads or builds everything else.
$ nix-build -A configs.vm.vm # the system in QEMU
$ ./result/bin/run-six-vm # login: root / root
$ nix-build -A tests.boot # tests: each boots a VM and checks
$ nix-build -A tests.units # the behaviour of units, one property at a time
$ nix-build -A tests.switch # switching the configuration live
$ nix-build -A tests.login # logging in on the console and over ssh
$ nix-build -A tests.replace # replace mode: the cache + swapped libraries
$ nix-build -A tests.elogind # sessions and loginctl
$ nix-build -A tests.user-services # user services, systemctl --user
$ nix-build -A tests.bootloader # GRUB (BIOS and UEFI), Limine, systemd-boot, rEFInd
$ nix-build -A upstream.openssh # a stock nixpkgs test against this tree
$ nix-build -A upstream.networking.scripted.static # names as in nixosTests
Your own configuration:
# system.nix
(import /path/to/nixithos { }).eval { modules = [ ./configuration.nix ]; }
$ nix-build system.nix -A config.system.build.toplevel
or through a flake, see above.
$ systemctl status sshd # the familiar commands work
$ systemctl restart nginx
$ journalctl -u nginx -f
$ s6-rc -a list # and these are the native ones
$ s6-svstat /run/service/sshd
$ tail -F /var/log/s6/sshd/current
$ cat /run/uncaught-logs/current # the boot log and everything without a logger of its own
systemctl stop X stops one service, as in systemd.
s6-rc -d change X stops the service and everything that requires it.
See docs/status.md. In short: system call filters
(SystemCallFilter=, RestrictAddressFamilies=; the rest of the sandbox
works, and what is not applied is listed in the database's report.txt),
.automount, template instances created on the fly (systemctl start foo@new when no dependency and no drop-in names it), containers.
MIT, like nixpkgs. The files modules/system/boot/stage-1* and
stage-2-init.sh are taken from nixpkgs and modified.