s6-rc: the "repo" subset of commands: definitions and concepts
Description: s6-rc: an overview
Keywords: s6-rc overview

s6-rc
Software
skarnet.org

# The repo subset of s6-rc commands: management of source definition directories

s6-rc commands such as s6-rc, s6-rc-compile, or s6-rc-db, implement pure mechanism, and leave all the burden of policy, i.e. how to organize source files, when to compile a service database, etc., to the user. This is on purpose, but does not make it easy to integrate s6-rc in a distribution, where service files are provided by packages installed by a package manager, and where the user wants to be able to easily define which services start at boot time and which do not, etc.

The solution to this conundrum is the high-level user interface to s6-rc (and more generally to the s6 ecosystem) named s6-frontend (currently in development). But in order for s6-frontend to be a user interface and not a whole engine in its own right, some stepping stones are in order: intermediary-level helpers to manage s6-rc source definition directories in a way that maps better to the way that distributions work than just the raw s6-rc-compile.

These intermediary-level helpers made their appearance in version 0.6.0.0 of s6-rc, and they're collectively known as repo commands — simply because they manage a repository of services.

## Definitions

### Store

Store: place where service definitions are stored

A service store is a place in the filesystem where source definition directories are written by the package manager or the user, where you can find the real contents of the services (instead of just symlinks), as in, source definition directories that are processed by s6-rc-compile. A repository will typically link to several stores: one provided by the package manager, and one — or more — provided locally by the admin. No s6-rc command will ever write to a store, but most repo commands read from all the stores.

The location of the stores is entirely determined by distribution policy, and s6-rc hardcodes no default for them. A good place to host service definition directories installed by a package manager could be, for instance, `/usr/share/s6-rc/sources`. A good place to host local service definition directories managed by an administrator could be `/etc/s6-rc/sources`.

### Repository

Repository: place in the filesystem where the repo commands work, write their data, maintain the dependency graph of all the services in the stores, etc.

The s6-rc repository is the central concept of the repo commands: it's the place in the filesystem where these commands perform their work and store their data. It is made of hierarchies of symlinks (lots and lots of symlinks), that ultimately point to source definition directories in the stores, as well as several compiled databases: the reference database, and the compiled databases made of the sets the user has committed, including the one the user will eventually install.

The repository is where offline work on services happens. It must be located on a read-write filesystem, but that filesystem does not need to be accessible during the early boot. By default, the repository is located in `/var/lib/s6-rc/repository`; it is configurable at s6-rc build time.

The repository is generally unique for a system. However, it is possible to create and use repositories in non-default locations; that is, for instance, what non-root users would do in order to manage their own sets of services.

### Reference database

Reference database: the dependency graph between all the services in all the stores used by the repository.

When the repository is created or updated, the list of all services defined in all the stores used by this repository is analyzed, and all these services are automatically compiled into a reference database, stored in the repository. The reference database is never meant to be installed or go live in any way; its purpose is mainly to give repo commands access to the full dependency graph of all the services in the stores. Additionally, compiling this reference database ensures that the whole set of services defined by the unions of all the stores is consistent: if not, the initialization fails, so the problem is reported as early as possible.

### Set

Set: a set of services with all the states the user wants them to be in at boot time: unavailable in the database, available but not automatically started, started at boot time.

Inside the repository, a set is the fundamental unit of operation, and what users will interact the most with. A set contains all the services defined in the reference database (so, each service listed in all the stores used by the repository) and associates, to each service, a given state that the user wants this service to be in when the system boots. This target state is called a prescription, or rx for short.

The idea is that the user can work on a set, making modifications to the target states as they see fit; the repo commands always ensure that the set will be consistent, i.e. that dependencies will be respected and target states are reachable. When satisfied with the states, the user can commit the set, then install it.

### Prescriptions

Prescription (or rx): the state the user wants a service to reach at boot time.

A prescription is the target state for every service in a set. At all times, each service is in one and only one rx. The list of rxs the service can be in is the following:

- masked: when the set is committed, masked services will not be included in the compiled set database. They will be completely absent; if the set database is installed, s6-rc will not know them. A service in another rx cannot depend on a masked service. Users typically mask services that are included in a store but are superfluous to them, services they just do not want to interact with.
- disabled: these services will appear in the compiled set database, and will be startable by s6-rc if the database is installed. However, they will not be included in the default bundle, i.e. they will not be started at boot time, and their state will be down until manually started by a `s6-rc start` command addressing them or a service that depends on them. Other names for disabled are: inactive, unmasked, latent, usable. (Tooling will likely report these services as latent or usable.)
- enabled: these services will appear in the compiled set database, and in the default bundle, which means they will be started at boot time. Other names for enabled are: active.
- essential, aka always: these services have been marked as essential in their source definition directories, so they will always be enabled. They cannot be disabled or masked. They cannot depend on non-essential services. This rx is typically used by early boot services that should always run.

Working on a set basically means moving services from one of these rxs to another, mostly choosing what services to enable or disable, and sometimes masking services the user does not want to see at all. Once the user is happy with a set, they should commit it.

### Commit

Committing a set means assembling a subset of all the source definition directories in the set and compiling them into a set database, replacing any previous database for the same set. This set database can then be installed.

### Install

Installing a committed set means copying the compiled set database to the place (defined by distribution policy) where it can be used as a live database, i.e. booted on, and then switching the live database to it. In other words: making the set database into the new live database.

## Workflow

- At distribution installation time:
  - The installer creates a repository with s6-rc-repo-init, linking to stores according to distribution policy.
  - The installer also sets a default live service database up.
- Whenever distribution policies change, for instance when declaring an additional local store:
  - The admin updates the list of stores with s6-rc-repo-init -U.
- Whenever service files change, typically when the package manager installs/updates/deletes packages that provide daemons, or admins modify services in local stores:
  - The package manager, or the admin, invokes s6-rc-repo-sync to keep the sets in the repository in sync with the stores.
  - It may also be necessary to commit the live set again with s6-rc-set-commit, and to install it with s6-rc-set-install, so the live database is also kept in sync with the machine state. (Failure to do so may prevent the machine from booting next time.)
- Whenever the admin wants to modify the current, live, machine state, by starting, stopping or restarting services:
  - They use the original s6-rc change command, which operates on the live database. No repo command is involved here.
- Whenever the admin wants to customize their startup process:
  - They can create new sets (with default prescriptions coming from policy that's hardcoded in the stores) with s6-rc-set-new, make a copy of an existing set with s6-rc-set-copy, or delete sets with s6-rc-set-delete.
  - They can list the existing sets with s6-rc-repo-list.
  - They can mask, unmask, enable or disable services in their favorite set with s6-rc-set-change.
  - They can check the list of services and their prescriptions with s6-rc-set-status.
  - They can check for inconsistencies and fix them with s6-rc-set-fix.
  - They commit the set with s6-rc-set-commit.
  - They install the set with s6-rc-set-install. This action potentially modifies the current state of the machine!
- At boot time:
  - The system boots on the installed live database. None of the repo commands are involved.

All these steps can be done manually, but they are made easier by the higher-level user interface provided by s6-frontend.