# Contributing

> How to set up new things in this repository and what tooling you will run into while doing it.

---

LLMS index: [llms.txt](/llms.txt)

---

## Project setup

This project is intended to be used on a system that has access to a nix daemon.
On a system that has access to a nix daemon a devShell is provided with tooling
to ease development. If you have access to direnv you can have the devShell load
itself automatically.

## Tools

### Version control

Version control is git, with branch protection enforced in two places. Locally,
a pre-commit hook and a pre-push hook (wired up automatically by entering the
dev shell) refuse to commit or push directly to `main` — each has an
environment-variable escape hatch for the rare case that needs it. These hooks
are not intended to be enforcement but instead as guide rails to prevent issues
when interacting with quality control validations further down the road.

Quality control takes place on the
[Forgejo forge](https://git.jan-leila.com/jan-leila/nix-config) via webhooks for
when branches have things happen to them. The main branch in particular rejects
any commits being added to it unless they come from a pull request or are
provided by our automated [CI/CD scripts](#cicd-scripts). Before a pull request
can be merged into main the branch it represents may be subject to a further set
of scripts to validate that it passes some conditions.

See the [Version control page](version-control.md) to get a tutorial on how to
manage commits, branches, remote repositories, and pull requests.

### Linter

Linters are static code analysis tools that are used to check for and correct
formatting and styling errors automatically. The intention of these tools is to
provide a consistent feeling of how the code should be read throughout the
project via a set of automated rules being applied to every file.

The particular linter used by this repository is
[alejandra](https://github.com/kamadorueda/alejandra) and is included as a part
of the devShell and can be run using the command `alejandra . --exclude ./npins`
or be set up to run automatically as a part of
[integrated development environment](https://en.wikipedia.org/wiki/Integrated_development_environment)s
such as vscode.

This linter was chosen specifically because it is highly opinionated making it
so that there should be one canonical way that any given
[abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) can
be represented by a stream of tokens that have made their way onto the `main`
branch.

While the linting tools are included as a part of the devShell that this
repository provides there is no need to run them manually. Formatting should
ideally happen automatically with any code that is merged to main. Pull requests
should be unable to merge to main if the changes that they contain are not able
to be successfully formatted into a consistent state.

In addition to the nix linter we also have a markdown linter that can be run
using this command: `mdformat "--wrap" "80" --number .`

### CI/CD Scripts

The CI/CD scripts used by this repository are not an implementation of this
repository. They are instead declared as a part of the configuration that this
repository defines. If this project was to stop being hosted on devices that it
describes then the tooling itself would move to the configuration of wherever
became the new home of the repository.

This means instead of depending on in repo actions we instead set up external
scripts to manage continuous integration and continuous delivery. Each one of
the CI/CD scripts that are used in this project are dedicated tools to manage a
particular task and are set up against their target repositories to listen to
webhooks and produce events based on the results of those hooks.

This is the current list of running scripts that we have:

- [lint-daemon](https://git.jan-leila.com/jan-leila/lint-daemon) - autoformats
  every branch on every push and pushes the fixup commit straight back, posting
  a status for it — there's no separate dry run, and it runs whether or not the
  branch is headed for a pull request
- [build-daemon](https://git.jan-leila.com/jan-leila/build-daemon) - the actual
  merge gate: on a pull request, builds every `nixosConfiguration` the repo
  declares, so neither fork can merge something that breaks the other's machines
- [sync-daemon](https://git.jan-leila.com/jan-leila/sync-daemon) - on a push to
  `main`, force-pushes it to a `sync/from-<owner>` branch on each
  [peer fork](version-control.md#forks) and opens a pull request there (or
  updates the one already open), so changes flow both ways and every sync lands
  through the receiving fork's own checks

Planned scripts

- test-daemon - run all of the tests as a part of CI/CD
- deploy-daemon - run build server and then deploy all configurations to all
  available hosts
- auto-update - automatically create and keep up to date PRs bumping npins.

> [!TODO]
>
> link tickets for each of the planned scripts

These scripts have secrets provisioned for them so that they are able to log in
to accounts on the forge that have been granted the permissions that they need
to be able to take the actions required of them.

### Test scripts

To try to help make sure that your changes do what you expect them to, as well
as to prevent from accidentally shooting yourself in the foot it is best
practice to follow a technique called
[Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development).

Tests will be automatically run when trying to merge into main and will prevent
merging to main if they are failing. If you would like to run the tests manually
you can use the command `nix-build tests`.

build-daemon only checks that every configuration can be built. Actually running
the tests before a merge is waiting on the planned test-daemon.

> [!TODO]
>
> create document that defines how to make tests for different parts of the
> project

### Provisioning new systems

The install script is intended to be used to provision a new device over ssh.
You can generate an image that can be used to boot into that already has ssh
keys added to it by using the command
`nix-build -A nixosConfigurations.installer.config.system.build.isoImage`. From
there you can boot your target device into the image and then use the install
command to install the target configuration onto the device.

> [!TODO]
>
> how to provision a new system

### Dependency management

Dependencies in the repository generally fall under two categories.

- Configuration dependencies - how the configuration itself is defined
- Asset dependencies - things used by the configuration to generate the system

Configuration dependencies are managed using
[npins](https://github.com/andir/npins) to pin dependency versioning to a known
working, reproducible version. This tool then manages the contents of the
`npins/` directory and updates sources as needed.

The choice to use `npins` over the more popular flakes pattern is because flakes
try to do too much at one time making them hard to learn, hard to work with,
hard to exchange for other parts in the future when better ideas come around,
and centralize authority on the "right way" to configure things into one large
monolith that leaves little room for external voices to tinker.

Asset dependencies are managed ad hoc wherever needed. This can be things like
source code, bundles or binary for a package, or anything else that is
statically hosted somewhere that we might need to have as a part of our
configuration.

See the
[administration documentation](../administration/README.md#updating-dependencies)
for how to update dependencies that already exist.

### Secret management

Currently secrets are protected using
[sops-nix](https://github.com/Mic92/sops-nix) and are stored in a separate
private repository. While sops does encrypt the secrets in a way where they
could be committed as a part of this repository doing so assumes that the
encryption being used will continue to be strong in the long term. To protect
against
[Harvest now, decrypt later](https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later)
attacks the encrypted secrets are stored in a
[separate private repository](https://git.jan-leila.com/jan-leila/nix-config-secrets)
that is only accessible by users who are trusted to have access to those
specific configuration secrets.

The values in the secrets repository are not anything special and the pin for
them can be replaced with a different secrets repository for any users who would
like to use or extend these configurations on their own system.

When a new system is set up using the install script the keys to decrypt the
secrets provided by the secrets repository are shipped to the new system.

As a part of our threat model currently beacon is not allowed to have any
secrets on it as we do not want to ship our global keys and encrypted values to
a vulnerable system.

In the future it would be nice to replace this method of managing secrets with a
more robust portable implementation. See the
[secrets module documentation](../modules/secrets/README.md) for plans on how we
will go about doing that.

> [!TODO]
>
> how to create secrets

### Private configurations

While ideally all configured values should be able to be public, in practice
there are some configurations that are not able to be shared publicly.

Currently those configurations are defined in
[another repository](https://git.jan-leila.com/jan-leila/nix-config-private) and
are layered under the configuration defined in this repository. Eventually this
repository will be reworked to be completely standalone with the minimal private
aspects being optional enhancements that can be deployed on top of the
configuration.

### Ticket tracking

Tickets of work that needs to be done or bugs that have been found are located
on a [vikunja instance](https://tasks.jan-leila.com).

### Metrics

Currently metrics are not yet implemented. More to come on this in the future.
See [the metrics module docs](../modules/metrics/README.md) for more details on
the plans.

### Value inspection

To look at what a configuration actually evaluated to, after every module and
every override has been applied you can use tooling provided by the devShell or
nix command line tooling.

The interactive tool provided as a part of the devShell is
[nix-inspect](https://github.com/bluskript/nix-inspect). It can be run using the
command `nix-inspect -p .`. From there you can inspect values and view available
values and options that exist in the configuration.

If you already know which value you are looking for and want a way to quickly
access it, or want to check it from a script `nix eval` can print it directly.
For example
`nix eval -f . nixosConfigurations.defiant.config.networking.hostName` prints
the host name for defiant. This can also be useful for evaluating the results of
functions which is not easy to do with `nix-inspect`.

---

Section pages:

- [Version Control](/contributing/version-control/): How to manage commits, branches, remote repositories, forks, and pull requests.
- [Module Structure](/contributing/module-structure/): How fixed points, the nixpkgs module system, and dendrites fit together to structure this configuration.
