LosOS boxes
A LosOS box is the project's home server. It is a
mini-PC running Nextcloud and Forgejo, with no SSH and no shell, managed from
its admin pages in a browser or from losos, its terminal client. LosOS
Desktop finds the boxes on the local network by itself and offers each one on
the desktop.
On a PC all of that happens in one machine, the LosOS
boxes machine, and not on the host. That machine finds the boxes, holds
losos with its keys and tokens, and opens the admin pages, and the other
machines reach a box through it. The host only lends it its resolver. A phone
has no machines, so there the session does the work itself, as described
below.
The LosOS boxes machine
The computer makes it at its first boot, as boxes, framed in green, with
1.5 GB of memory. It does not start then. The first time a box answers on
one of the computer's networks, losos-boxes-wake, a service on the host,
starts it, and stops watching. On later boots, if it remembers a box reachable
through its edge, it starts it without waiting for LAN discovery; an unpaired
machine still waits for a box on the network. A machine you stop after that
stays stopped until the next boot, and you can start it in the Machines app at
any time. If you remove it, the computer does not make it again. Make it again
in the Machines app from the losos-boxes template, or with
pm machine create boxes --image /run/current-system/sw/share/losos/machines/losos-boxes.qcow2 --label green
pm machine start boxes
Its template is nixos/guest/boxes.nix. That is the LosOS guest's machine,
nixos/guest/common.nix, described in the guest
template, without Flatpak and with losos,
losos-boxes, Danube, GNOME Keyring and the GitHub CLI, gh. losos ci setup uses gh to give a repository's Actions a key for a box.
Each box the machine finds becomes one of its apps on the desktop. The app is
named after the box, such as mattbox, and described as "LosOS v0.4.0 box at
mattbox.local". The desktop shows it as "mattbox in boxes", framed in the
machine's colour. Opening it opens the box in losos, in a derisk-terminal
window in the machine. If losos hosts does not list the box yet, it signs
in first, as described below. If anything fails, the
window stays open until Enter is pressed, so what losos said can be read.
The app's one action, Open the admin pages, opens http://mattbox.local/
in Danube, also in the machine.
Finding boxes
losos-boxes watch --terminal, a service of the machine's user, keeps those
entries up to date. It browses two DNS-SD types through systemd-resolved's
Varlink interface, io.systemd.Resolve.BrowseServices:
_losos._tcp, which a box announces on its front port, with its release in the TXT record asversion=v0.4.0._workstation._tcp, which every box announces, and so do most Linux machines running Avahi. Boxes installed before the_losos._tcpannouncement existed announce only this until they update. Solosos-boxesasks each workstation for/setup/state.jsonon port 80, which only a box serves. A box found both ways gets one entry.
It resolves each instance with ResolveService and writes
losos-box-<host>.desktop into ~/.local/share/applications, where the
machine's losos-guest-run --list finds it for the desktop. A box that sends
mDNS's goodbye, or whose records expire, loses its entry. The service removes
all entries when it stops and again when it starts, so a box that left while
nothing was watching is not offered. Only a host name goes into an entry,
because anything on the network can answer mDNS and the name ends up on an
Exec line. losos-boxes list prints what it finds within a few seconds,
one box per line, with its address and release.
The resolved it browses through is the host's. The machine does no mDNS of
its own, as names on the network
explains, and its multicast would not get past libvirt's NAT to the LAN
anyway. The host's resolved, on the other hand, does mDNS on every network
the host is on. So LOSOS_BOXES_RESOLVE points the machine's losos-boxes
at /run/losos-boxes/resolve, a socket that systemd-socket-proxyd carries
to the host over vsock, port 7351. On the host, losos-boxes relay, set up in
nixos/modules/losos-boxes.nix, takes the connection and asks libvirt which
machine has the vsock address the kernel stamped on it. It passes the
connection to resolved's Varlink socket only if pm recorded that machine's
template as losos-boxes, and refuses any other machine. Varlink's resolve
interface only looks things up, so the machine can browse and resolve through
the host but cannot change anything there.
Away from home
A box that reaches the internet through an
edge tells
losos login its address there, and losos keeps it with the box's key.
When the box's own address leads nowhere, because the computer is on
another network, losos sends its calls to that address instead. They are
still signed and encrypted end to end, so the edge carries them without
reading them.
losos-boxes watch offers such a box even while mDNS cannot find it. Once
a minute it reads losos hosts, and for each box with an away from
home address that is not on this network it writes the box's entry
anyway, described as "LosOS box mattbox.local, away from home, through
mattbox.losos.cfd". Opening it opens the box in losos, as at home. A
separate LosOS Cloud — mattbox entry opens LosOS Cloud at the edge
address, which the edge serves to anyone who signs in to LosOS Login. A
separate entry makes Cloud available through the PC's machine launcher,
which does not expose desktop actions. The admin pages stay on the box's
own network. When the box is found on the network again, its usual entry
replaces the away entry and the Cloud entry goes. When losos logout
forgets it, both entries go. While the keyring is locked watch reads
nothing, so it never asks for the keyring's password, and leaves the entries
as they were.
The palette's search inside the boxes' files (below)
goes through losos too, so it finds files on a box away from home the
same way.
From the other machines
The other machines reach a box through the LosOS boxes machine, so it is the one machine that talks to boxes:
losos-boxes route, a system service of the LosOS boxes machine, finds the boxes the same way. Whenever they change, it sends their names to the host's relay on vsock port 7352.- The relay writes those names, with the address libvirt's DHCP gave that
machine, into
/run/losos-boxes/hosts/<machine>. libvirt's dnsmasq, the DNS server of every machine, rereads that directory whenever it changes, through thehostsdiroption thatlosos-boxes-dnsadds to thedefaultnetwork once at boot. So dnsmasq answersmattbox.localwith the LosOS boxes machine's address before it would ask the host's resolved. routetakes each connection on ports 80 and 443 and reads which name it is for from itsHostheader or its TLS ClientHello's server name. It passes the connection on unchanged to that box, TLS included, so the certificate the other machine sees, and thatlosospins, is the box's own. A name that is not a present box gets nothing.
When the LosOS boxes machine stops, its hosts file goes away, and box names
are answered like any other .local name again, by the host with the box's
own address. Only ports 80 and 443 go through the machine, because that is
where a box serves its admin pages, its cloud and its Git. An app's own port
on a box is reached directly. Nothing stops a machine from connecting to a
box address it already knows. Only connections made by name go through the
LosOS boxes machine.
On a phone
The GSI has no machines, so the session runs losos-boxes watch itself and
carries losos. Each box is an app of the phone. Opening it opens the admin
pages in the default browser, and its action, Manage in the terminal,
opens losos.
In the command palette
Text typed into derisk's command palette is also looked for inside the
files of the boxes losos is signed in to: the files in LosOS Cloud and
the default branches of LosOS Git's repositories, as the box's own
palette finds them. A file found that way is a row named after the file,
with the folder or repository it is in and the words around the match,
such as budget.md mattbox · LosOS Cloud › Documents · the rent is due.
Choosing a cloud file opens its folder in LosOS Cloud, and choosing a line
in Git opens the file at that line, both in the default browser.
losos-boxes search <text> does the searching. It runs losos hosts,
then losos call GET /api/cloud/text and /api/git/text on up to three
of the boxes it lists, all at once, and prints the rows as JSON. On a PC
it runs in the LosOS boxes machine, where losos and its tokens are:
derisk runs losos-boxes-search there through the guest agent, which is
losos-boxes search as the machine's user, on that user's bus. derisk
asks only while the machine is running and never starts it for a search.
On a phone derisk runs losos-boxes search in the session itself.
A search never asks for anything. losos keeps its tokens in the
keyring, and asking a locked keyring for them makes it show a password
window, so while the keyring is locked losos-boxes search finds nothing.
It also finds nothing in a box that does not answer, or whose search is
off, or that runs no LosOS Git. derisk asks once the typed text has three
characters, and one search at a time.
The terminal client
losos shows the same panes as a box's admin pages, with the same words and
colours. Every request it sends is signed by your GPG key and encrypted to
the box, and the box answers the same way, so it needs no SSH and plain
http:// is safe for it. The handbook's
terminal client page is the full
guide. On LosOS Desktop it comes down to:
losos key import --gpg you@example.com
losos login http://mattbox.local
losos
A box accepts only keys that were on the stick it was installed from, so
the key has to be one of those, and the token login asks for comes from
Settings → Access tokens in the box's admin pages. login shows the
box's key in groups of four to compare with that pane before you accept
it.
losos keeps keys and tokens in the Secret Service through libsecret's
secret-tool, and never writes them to a file. GNOME Keyring answers the
Secret Service in the LosOS boxes machine, and in a phone's session. Nobody
signs in to the machine, so no sign-in password unlocks its keyring. The
host keeps a password for it instead, made at random the first time the
machine asks and kept in /var/lib/losos-boxes/keyrings/, readable only by
the relay's own account. At every boot the machine asks the relay for it on
vsock port 7353, and its keyring starts unlocked with it, so nothing asks
you for a password. A copy of the machine's disk alone does not open its
keyring. A keyring made before the host kept a password has the password
you chose for it then, and keeps asking for that one. To move it over,
remove the machine and make it again, then sign losos in again.
key import --gpg reads the key
from gpg. Both tools are on losos's PATH, after the user's own, so a gpg
installed with pm is the one it uses.
How it is built
nixos/pkgs/losos.nix builds the losos crate from the LosOS repository,
github.com/dasmatus/losos at admin-ui/tui, at a pinned commit. It is the
same crate and cargoHash as that repository's own .#losos. Its build
script reads the admin UI's catalogue, palette, pane keywords and the salmon,
and the crate links the envelope crate in backend/access. So a sparse
checkout fetches those directories and nothing else, about 3 MB of a 400 MB
tree. That is why it is a fetchgit pin and not a components/ submodule,
as trust explains: the flake copies its submodules on every
evaluation.
The commit and its hashes are in nixos/pkgs/losos-pin.nix. The LosOS
repository moves them: on every release tag its desktop-pin workflow
opens a pull request here that points the file at the tag, with the hashes
of the client's sources, its vendored crates and the box's sources, so
nobody edits them by hand. To pin another commit, run that repository's
.github/scripts/desktop-pin.sh <tag> <checkout of this repository>, or put
the commit in rev and let a build report each hash. The client's own test
suites, a mock box and edge over TLS and a pseudo terminal, run in the LosOS
repository's CI, not here.
losos-boxes lives in src/losos-boxes, and nixos/pkgs/losos-boxes.nix
builds it with its unit tests. nixos/tests/losos-boxes.nix boots two boxes,
as far as mDNS and nginx go, one announcing _losos._tcp and one from before
that announcement. It also boots a phone's session with resolved and no
Avahi. The session lists both boxes, writes their entries, and drops the
first box's entry when that box leaves the network.
nixos/tests/losos-box.nix boots a real box beside a desktop: the box's
own modules from the pinned commit, with lososd, LosOS Login, LosOS Cloud
and LosOS Git, claimed as the setup wizard claims it. The desktop finds it
by its announcement, signs losos in with a token made for the GPG key the
box was installed with and reads the box's state through the encrypted
envelope, then pins its certificate, signs the backups in through LosOS
Login with a code a browser approves, and backs a home up to it
(backups). The pin's pull requests run it, so a change in LosOS
that breaks the desktop shows there before it merges.
nixos/tests/machines.nix makes a LosOS boxes machine on a host whose LAN
has a box. It checks that the machine offers the box, that another machine's
request for the box's name goes through it and gets the box's answer, that a
name which is not a box is refused, and that stopping the machine removes its
names.