1
0
forked from clan/clan-core

Compare commits

..

3 Commits

Author SHA1 Message Date
6e5898b95a s 2024-04-15 11:55:34 +02:00
674b22a728 s 2024-04-15 11:53:53 +02:00
4fe6c2facd documentation 2024-04-15 11:51:56 +02:00
245 changed files with 1678 additions and 12389 deletions

View File

@ -5,6 +5,11 @@ on:
branches:
- main
jobs:
checks:
runs-on: nix
steps:
- uses: actions/checkout@v3
- run: nix run --refresh github:Mic92/nix-fast-build -- --no-nom --eval-workers 10
checks-impure:
runs-on: nix
steps:

View File

@ -1,13 +0,0 @@
name: deploy
on:
push:
branches:
- main
jobs:
deploy-docs:
runs-on: nix
steps:
- uses: actions/checkout@v3
- run: nix run .#deploy-docs
env:
SSH_HOMEPAGE_KEY: ${{ secrets.SSH_HOMEPAGE_KEY }}

8
.gitignore vendored
View File

@ -13,9 +13,6 @@ nixos.qcow2
**/*.glade~
/docs/out
# dream2nix
.dream2nix
# python
__pycache__
.coverage
@ -31,8 +28,3 @@ build
build-dir
repo
.env
# node
node_modules
dist
.webui

View File

@ -1,4 +1,4 @@
# Contributing to Clan
# Contributing to cLAN
## Live-reloading documentation

View File

@ -1,6 +1,6 @@
# Clan Core Repository
# cLAN Core Repository
Welcome to the Clan Core Repository, the heart of the [clan.lol](https://clan.lol/) project! This monorepo is the foundation of Clan, a revolutionary open-source project aimed at restoring fun, freedom, and functionality to computing. Here, you'll find all the essential packages, NixOS modules, CLI tools, and tests needed to contribute to and work with the Clan project. Clan leverages the Nix system to ensure reliability, security, and seamless management of digital environments, putting the power back into the hands of users.
Welcome to the cLAN Core Repository, the heart of the [clan.lol](https://clan.lol/) project! This monorepo is the foundation of Clan, a revolutionary open-source project aimed at restoring fun, freedom, and functionality to computing. Here, you'll find all the essential packages, NixOS modules, CLI tools, and tests needed to contribute to and work with the cLAN project. Clan leverages the Nix system to ensure reliability, security, and seamless management of digital environments, putting the power back into the hands of users.
## Why Clan?
@ -14,11 +14,11 @@ Our mission is simple: to democratize computing by providing tools that empower
- **Robust Backup Management:** Long-term, self-hosted data preservation.
- **Intuitive Secret Management:** Simplified encryption and password management processes.
## Getting Started with Clan
## Getting Started with cLAN
If you're new to Clan and eager to dive in, start with our quickstart guide and explore the core functionalities that Clan offers:
If you're new to cLAN and eager to dive in, start with our quickstart guide and explore the core functionalities that Clan offers:
- **Quickstart Guide**: Check out [getting started](https://docs.clan.lol/#starting-with-a-new-clan-project)<!-- [docs/site/index.md](docs/site/index.md) --> to get up and running with Clan in no time.
- **Quickstart Guide**: Check out [getting started](https://docs.clan.lol/#starting-with-a-new-clan-project) to get up and running with cLAN in no time.
### Managing Secrets
@ -26,20 +26,20 @@ In the Clan ecosystem, security is paramount. Learn how to handle secrets effect
- **Secrets Management**: Securely manage secrets by consulting [secrets](https://docs.clan.lol/getting-started/secrets/)<!-- [secrets.md](docs/site/getting-started/secrets.md) -->.
### Contributing to Clan
### Contributing to cLAN
The Clan project thrives on community contributions. We welcome everyone to contribute and collaborate:
- **Contribution Guidelines**: Make a meaningful impact by following the steps in [contributing](https://docs.clan.lol/contributing/contributing/)<!-- [contributing.md](docs/CONTRIBUTING.md) -->.
- **Contribution Guidelines**: Make a meaningful impact by following the steps in https://docs.clan.lol/contributing/contributing/<!-- [contributing.md](docs/CONTRIBUTING.md) -->.
## Join the Revolution
Clan is more than a tool; it's a movement towards a better digital future. By contributing to the Clan project, you're part of changing technology for the better, together.
Clan is more than a tool; it's a movement towards a better digital future. By contributing to the cLAN project, you're part of changing technology for the better, together.
### Community and Support
Connect with us and the Clan community for support and discussion:
- [Matrix channel](https://matrix.to/#/#clan:lassul.us) for live discussions.
- [Matrix channel](https://matrix.to/#/!djzOHBBBHnwQkgNgdV:matrix.org?via=blog.clan.lol) for live discussions.
- IRC bridges (coming soon) for real-time chat support.

View File

@ -145,14 +145,14 @@
machine.succeed("echo testing > /var/test-backups/somefile")
# create
machine.succeed("clan backups create --debug --flake ${self} test-backup")
machine.succeed("clan --debug --flake ${self} backups create test-backup")
machine.wait_until_succeeds("! systemctl is-active borgbackup-job-test-backup >&2")
machine.succeed("test -f /run/mount-external-disk")
machine.succeed("test -f /run/unmount-external-disk")
# list
backup_id = json.loads(machine.succeed("borg-job-test-backup list --json"))["archives"][0]["archive"]
out = machine.succeed("clan backups list --debug --flake ${self} test-backup").strip()
out = machine.succeed("clan --debug --flake ${self} backups list test-backup").strip()
print(out)
assert backup_id in out, f"backup {backup_id} not found in {out}"
localbackup_id = "hdd::/mnt/external-disk/snapshot.0"
@ -160,14 +160,14 @@
## borgbackup restore
machine.succeed("rm -f /var/test-backups/somefile")
machine.succeed(f"clan backups restore --debug --flake ${self} test-backup borgbackup 'test-backup::borg@machine:.::{backup_id}' >&2")
machine.succeed(f"clan --debug --flake ${self} backups restore test-backup borgbackup 'test-backup::borg@machine:.::{backup_id}' >&2")
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
machine.succeed("test -f /var/test-service/pre-restore-command")
machine.succeed("test -f /var/test-service/post-restore-command")
## localbackup restore
machine.succeed("rm -f /var/test-backups/somefile /var/test-service/{pre,post}-restore-command")
machine.succeed(f"clan backups restore --debug --flake ${self} test-backup localbackup '{localbackup_id}' >&2")
machine.succeed(f"clan --debug --flake ${self} backups restore test-backup localbackup '{localbackup_id}' >&2")
assert machine.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
machine.succeed("test -f /var/test-service/pre-restore-command")
machine.succeed("test -f /var/test-service/post-restore-command")

View File

@ -46,6 +46,7 @@
syncthing = import ./syncthing nixosTestArgs;
wayland-proxy-virtwl = import ./wayland-proxy-virtwl nixosTestArgs;
};
schemaTests = pkgs.callPackages ./schemas.nix { inherit self; };
flakeOutputs =
lib.mapAttrs' (
@ -57,7 +58,7 @@
self'.legacyPackages.homeConfigurations or { }
);
in
{ inherit renderClanOptions; } // nixosTests // flakeOutputs;
{ inherit renderClanOptions; } // nixosTests // schemaTests // flakeOutputs;
legacyPackages = {
nixosTests =
let

View File

@ -9,16 +9,16 @@
}:
let
dependencies = [
pkgs.disko
self
pkgs.stdenv.drvPath
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.build.toplevel
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.build.diskoScript
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.build.diskoScript.drvPath
self.clanInternals.machines.${pkgs.hostPlatform.system}.test_install_machine.config.system.clan.deployment.file
self.inputs.nixpkgs.legacyPackages.${pkgs.hostPlatform.system}.disko
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in
{
# Currently disabled...
checks = pkgs.lib.mkIf (pkgs.stdenv.isLinux) {
flash = (import ../lib/test-base.nix) {
name = "flash";
@ -41,8 +41,7 @@
};
testScript = ''
start_all()
machine.succeed("clan flash --debug --flake ${../..} --yes --disk main /dev/vdb test_install_machine")
machine.succeed("clan --flake ${../..} flash --debug --yes --disk main /dev/vdb test_install_machine")
'';
} { inherit pkgs self; };
};

View File

@ -7,8 +7,6 @@
#!${pkgs.bash}/bin/bash
set -euo pipefail
unset CLAN_DIR
export PATH="${
lib.makeBinPath [
pkgs.gitMinimal

View File

@ -2,8 +2,8 @@
{
clan.machines.test_install_machine = {
clan.networking.targetHost = "test_install_machine";
fileSystems."/".device = lib.mkDefault "/dev/vdb";
boot.loader.grub.device = lib.mkDefault "/dev/vdb";
fileSystems."/".device = lib.mkDefault "/dev/null";
boot.loader.grub.device = lib.mkDefault "/dev/null";
imports = [ self.nixosModules.test_install_machine ];
};
@ -12,11 +12,11 @@
{ lib, modulesPath, ... }:
{
imports = [
self.clanModules.disk-layouts
self.clanModules.diskLayouts
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
(modulesPath + "/profiles/qemu-guest.nix")
];
clan.disk-layouts.singleDiskExt4.device = "/dev/vdb";
clan.diskLayouts.singleDiskExt4.device = "/dev/vdb";
environment.etc."install-successful".text = "ok";
@ -63,9 +63,7 @@
};
};
nodes.client = {
environment.systemPackages = [
self.packages.${pkgs.system}.clan-cli
] ++ self.packages.${pkgs.system}.clan-cli.runtimeDependencies;
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli ];
environment.etc."install-closure".source = "${closureInfo}/store-paths";
virtualisation.memorySize = 2048;
nix.settings = {
@ -98,7 +96,7 @@
client.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../lib/ssh/privkey} /root/.ssh/id_ed25519")
client.wait_until_succeeds("ssh -o StrictHostKeyChecking=accept-new -v root@target hostname")
client.succeed("clan machines install --debug --flake ${../..} --yes test_install_machine root@target >&2")
client.succeed("clan --debug --flake ${../..} machines install --yes test_install_machine root@target >&2")
try:
target.shutdown()
except BrokenPipeError:

View File

@ -10,7 +10,6 @@ in
hostPkgs = pkgs;
# speed-up evaluation
defaults = {
nix.package = pkgs.nixVersions.latest;
documentation.enable = lib.mkDefault false;
boot.isContainer = true;

View File

@ -10,7 +10,6 @@ in
defaults = {
documentation.enable = lib.mkDefault false;
nix.settings.min-free = 0;
nix.package = pkgs.nixVersions.latest;
};
# to accept external dependencies such as disko

48
checks/schemas.nix Normal file
View File

@ -0,0 +1,48 @@
{
self,
runCommand,
check-jsonschema,
pkgs,
lib,
...
}:
let
clanModules.clanCore = self.nixosModules.clanCore;
baseModule = {
imports = (import (pkgs.path + "/nixos/modules/module-list.nix")) ++ [
{
nixpkgs.hostPlatform = "x86_64-linux";
clanCore.clanName = "dummy";
}
];
};
optionsFromModule =
module:
let
evaled = lib.evalModules {
modules = [
module
baseModule
];
};
in
evaled.options.clan;
clanModuleSchemas = lib.mapAttrs (
_: module: self.lib.jsonschema.parseOptions (optionsFromModule module)
) clanModules;
mkTest =
name: schema:
runCommand "schema-${name}" { } ''
${check-jsonschema}/bin/check-jsonschema \
--check-metaschema ${builtins.toFile "schema-${name}" (builtins.toJSON schema)}
touch $out
'';
in
lib.mapAttrs' (name: schema: {
name = "schema-${name}";
value = mkTest name schema;
}) clanModuleSchemas

View File

@ -4,11 +4,10 @@
nodes.machine =
{ self, config, ... }:
{
environment.etc."privkey.age".source = ./key.age;
imports = [ (self.nixosModules.clanCore) ];
environment.etc."secret".source = config.sops.secrets.secret.path;
environment.etc."group-secret".source = config.sops.secrets.group-secret.path;
sops.age.keyFile = "/etc/privkey.age";
sops.age.keyFile = ./key.age;
clanCore.clanDir = "${./.}";
clanCore.machineName = "machine";

View File

@ -1,2 +0,0 @@
Efficient, deduplicating backup program with optional compression and secure encryption.
---

View File

@ -1,16 +0,0 @@
Email-based instant messaging for Desktop.
---
!!! warning "Under construction"
!!! info
This module will automatically configure an email server on the machine for handling the e-mail messaging seamlessly.
## Features
- [x] **Email-based**: Uses any email account as its backend.
- [x] **End-to-End Encryption**: Supports Autocrypt to automatically encrypt messages.
- [x] **No Phone Number Required**: Uses your email address instead of a phone number.
- [x] **Cross-Platform**: Available on desktop and mobile platforms.
- [x] **Automatic Server Setup**: Includes your own DeltaChat server for enhanced control and privacy.
- [ ] **Bake a cake**: This module cannot cake a bake.

View File

@ -1,2 +0,0 @@
Automatically format a disk drive on clan installation
---

View File

@ -1,6 +1,6 @@
{ config, lib, ... }:
{
options.clan.disk-layouts.singleDiskExt4 = {
options.clan.diskLayouts.singleDiskExt4 = {
device = lib.mkOption {
type = lib.types.str;
example = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_S21PNXAGB12345";
@ -13,14 +13,13 @@
disk = {
main = {
type = "disk";
device = config.clan.disk-layouts.singleDiskExt4.device;
device = config.clan.diskLayouts.singleDiskExt4.device;
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
priority = 1;
};
ESP = {
size = "512M";

View File

@ -1,2 +0,0 @@
A modern IRC server
---

View File

@ -1,26 +1,29 @@
{ ... }:
{ inputs, ... }:
{
flake.clanModules = {
disk-layouts = {
imports = [ ./disk-layouts ];
diskLayouts = {
imports = [
./diskLayouts.nix
inputs.disko.nixosModules.default
];
};
borgbackup = ./borgbackup;
deltachat = ./deltachat;
ergochat = ./ergochat;
localbackup = ./localbackup;
localsend = ./localsend;
matrix-synapse = ./matrix-synapse;
moonlight = ./moonlight;
root-password = ./root-password;
sshd = ./sshd;
sunshine = ./sunshine;
static-hosts = ./static-hosts;
syncthing = ./syncthing;
thelounge = ./thelounge;
trusted-nix-caches = ./trusted-nix-caches;
user-password = ./user-password;
xfce = ./xfce;
zerotier-static-peers = ./zerotier-static-peers;
zt-tcp-relay = ./zt-tcp-relay;
borgbackup = ./borgbackup.nix;
ergochat = ./ergochat.nix;
deltachat = ./deltachat.nix;
graphical = ./graphical.nix;
localbackup = ./localbackup.nix;
localsend = ./localsend.nix;
matrix-synapse = ./matrix-synapse.nix;
moonlight = ./moonlight.nix;
sshd = ./sshd.nix;
sunshine = ./sunshine.nix;
syncthing = ./syncthing.nix;
root-password = ./root-password.nix;
thelounge = ./thelounge.nix;
vm-user = ./vm-user.nix;
waypipe = ./waypipe.nix;
xfce = ./xfce.nix;
xfce-vm = ./xfce-vm.nix;
zt-tcp-relay = ./zt-tcp-relay.nix;
};
}

View File

@ -0,0 +1 @@
_: { fonts.enableDefaultPackages = true; }

View File

@ -1,2 +0,0 @@
Automatically backups current machine to local directory.
---

View File

@ -9,7 +9,7 @@
# - cli frontend: https://github.com/localsend/localsend/issues/11
# - ipv6 support: https://github.com/localsend/localsend/issues/549
options.clan.localsend = {
enable = lib.mkEnableOption "enable the localsend module";
enable = lib.mkEnableOption (lib.mdDoc "enable the localsend module");
defaultLocation = lib.mkOption {
type = lib.types.str;
description = "The default download location";

View File

@ -1,2 +0,0 @@
Securely sharing files and messages over a local network without internet connectivity.
---

View File

@ -1,2 +0,0 @@
A federated messaging server with end-to-end encryption.
---

View File

@ -1,2 +0,0 @@
A desktop streaming client optimized for remote gaming and synchronized movie viewing.
---

View File

@ -1,11 +0,0 @@
Automatically generates and configures a password for the root user.
---
After the system was installed/deployed the following command can be used to display the root-password:
```bash
clan secrets get {machine_name}-password
```
See also: [Facts / Secrets](../../getting-started/secrets.md)

View File

@ -1,7 +1,6 @@
{ config, pkgs, ... }:
{
services.openssh.enable = true;
services.openssh.settings.PasswordAuthentication = false;
services.openssh.hostKeys = [
{

View File

@ -1,2 +0,0 @@
Enables secure remote access to the machine over ssh
---

View File

@ -1,2 +0,0 @@
Statically configure the host names of machines based on their respective zerotier-ip.
---

View File

@ -1,44 +0,0 @@
{ lib, config, ... }:
{
options.clan.static-hosts = {
excludeHosts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default =
if config.clan.static-hosts.topLevelDomain != "" then [ ] else [ config.clanCore.machineName ];
description = "Hosts that should be excluded";
};
topLevelDomain = lib.mkOption {
type = lib.types.str;
default = "";
description = "Top level domain to reach hosts";
};
};
config.networking.hosts =
let
clanDir = config.clanCore.clanDir;
machineDir = clanDir + "/machines/";
zerotierIpMachinePath = machines: machineDir + machines + "/facts/zerotier-ip";
machines = builtins.readDir machineDir;
filteredMachines = lib.filterAttrs (
name: _: !(lib.elem name config.clan.static-hosts.excludeHosts)
) machines;
in
lib.filterAttrs (_: value: value != null) (
lib.mapAttrs' (
machine: _:
let
path = zerotierIpMachinePath machine;
in
if builtins.pathExists path then
lib.nameValuePair (builtins.readFile path) (
if (config.clan.static-hosts.topLevelDomain == "") then
[ machine ]
else
[ "${machine}.${config.clan.static-hosts.topLevelDomain}" ]
)
else
null
) filteredMachines
);
}

View File

@ -1,2 +0,0 @@
A desktop streaming server optimized for remote gaming and synchronized movie viewing.
---

View File

@ -34,10 +34,6 @@
'';
type = lib.types.listOf lib.types.str;
default = [ ];
example = [
"folder1"
"folder2"
];
};
};

View File

@ -1,32 +0,0 @@
A secure, file synchronization app for devices over networks, offering a private alternative to cloud services.
---
## Usage
We recommend configuring this module as an sync-service through the provided options. Although it provides a Web GUI through which more usage scenarios are supported.
## Features
- **Private and Secure**: Syncthing uses TLS encryption to secure data transfer between devices, ensuring that only the intended devices can read your data.
- **Decentralized**: No central server is involved in the data transfer. Each device communicates directly with others.
- **Open Source**: The source code is openly available for audit and contribution, fostering trust and continuous improvement.
- **Cross-Platform**: Syncthing supports multiple platforms including Windows, macOS, Linux, BSD, and Android.
- **Real-time Synchronization**: Changes made to files are synchronized in real-time across all connected devices.
- **Web GUI**: It includes a user-friendly web interface for managing devices and configurations. (`127.0.0.1:8384`)
## Configuration
- **Share Folders**: Select folders to share with connected devices and configure permissions and synchronization parameters.
!!! info
Clan automatically discovers other devices. Automatic discovery requires one machine to be an [introducer](#clan.syncthing.introducer)
If that is not the case you can add the other device by its Device ID manually.
You can find and share Device IDs under the "Add Device" button in the Web GUI. (`127.0.0.1:8384`)
## Troubleshooting
- **Sync Conflicts**: Resolve synchronization conflicts manually by reviewing file versions and modification times in the Web GUI (`127.0.0.1:8384`).
## Support
- **Documentation**: Extensive documentation is available on the [Syncthing website](https://docs.syncthing.net/).

View File

@ -1,2 +0,0 @@
Modern web IRC client
---

View File

@ -1,2 +0,0 @@
This module sets the `clan.lol` and `nix-community` cache up as a trusted cache.
----

View File

@ -1,10 +0,0 @@
{
nix.settings.trusted-substituters = [
"https://cache.clan.lol"
"https://nix-community.cachix.org"
];
nix.settings.trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"cache.clan.lol-1:3KztgSAB5R1M+Dz7vzkBGzXdodizbgLXGXKXlcQLA28="
];
}

View File

@ -1,18 +0,0 @@
Automatically generates and configures a password for the specified user account.
---
If setting the option prompt to true, the user will be prompted to type in their desired password.
!!! Note
This module will set `mutableUsers` to `false`, meaning you can not manage user passwords through `passwd` anymore.
After the system was installed/deployed the following command can be used to display the user-password:
```bash
clan secrets get {machine_name}-user-password
```
See also: [Facts / Secrets](../../getting-started/secrets.md)
To regenerate the password, delete the password files in the clan directory and redeploy the machine.

View File

@ -1,49 +0,0 @@
{
pkgs,
config,
lib,
...
}:
{
options.clan.user-password = {
user = lib.mkOption {
type = lib.types.str;
example = "alice";
description = "The user the password should be generated for.";
};
prompt = lib.mkOption {
type = lib.types.bool;
default = true;
example = false;
description = "Whether the user should be prompted.";
};
};
config = {
users.mutableUsers = false;
users.users.${config.clan.user-password.user}.hashedPasswordFile =
config.clanCore.facts.services.user-password.secret.user-password-hash.path;
sops.secrets."${config.clanCore.machineName}-user-password-hash".neededForUsers = true;
clanCore.facts.services.user-password = {
secret.user-password = { };
secret.user-password-hash = { };
generator.prompt = (
lib.mkIf config.clan.user-password.prompt "Set the password for your $user: ${config.clan.user-password.user}.
You can autogenerate a password, if you leave this prompt blank."
);
generator.path = with pkgs; [
coreutils
xkcdpass
mkpasswd
];
generator.script = ''
if [[ -n $prompt_value ]]; then
echo $prompt_value > $secrets/user-password
else
xkcdpass --numwords 3 --delimiter - --count 1 > $secrets/user-password
fi
cat $secrets/user-password | mkpasswd -s -m sha-512 > $secrets/user-password-hash
'';
};
};
}

20
clanModules/vm-user.nix Normal file
View File

@ -0,0 +1,20 @@
{
security = {
sudo.wheelNeedsPassword = false;
polkit.enable = true;
rtkit.enable = true;
};
users.users.user = {
isNormalUser = true;
createHome = true;
uid = 1000;
initialHashedPassword = "";
extraGroups = [
"wheel"
"video"
"render"
];
shell = "/run/current-system/sw/bin/bash";
};
}

15
clanModules/xfce-vm.nix Normal file
View File

@ -0,0 +1,15 @@
{
imports = [
./vm-user.nix
./graphical.nix
];
services.xserver = {
enable = true;
displayManager.autoLogin.enable = true;
displayManager.autoLogin.user = "user";
desktopManager.xfce.enable = true;
desktopManager.xfce.enableScreensaver = false;
xkb.layout = "us";
};
}

View File

@ -1,2 +0,0 @@
A lightweight desktop manager
---

View File

@ -1,5 +0,0 @@
Statically configure the `zerotier` peers of a clan network.
---
Statically configure the `zerotier` peers of a clan network.
Requires a machine, that is the zerotier controller configured in the network.

View File

@ -1,71 +0,0 @@
{
lib,
config,
pkgs,
inputs,
...
}:
let
clanDir = config.clanCore.clanDir;
machineDir = clanDir + "/machines/";
machinesFileSet = builtins.readDir machineDir;
machines = lib.mapAttrsToList (name: _: name) machinesFileSet;
zerotierNetworkIdPath = machines: machineDir + machines + "/facts/zerotier-network-id";
networkIdsUnchecked = builtins.map (
machine:
let
fullPath = zerotierNetworkIdPath machine;
in
if builtins.pathExists fullPath then builtins.readFile fullPath else null
) machines;
networkIds = lib.filter (machine: machine != null) networkIdsUnchecked;
networkId = if builtins.length networkIds == 0 then null else builtins.elemAt networkIds 0;
in
#TODO:trace on multiple found network-ids
#TODO:trace on no single found networkId
{
options.clan.zerotier-static-peers = {
excludeHosts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ config.clanCore.machineName ];
description = "Hosts that should be excluded";
};
};
config.systemd.services.zerotier-static-peers-autoaccept =
let
machines = builtins.readDir machineDir;
zerotierIpMachinePath = machines: machineDir + machines + "/facts/zerotier-ip";
filteredMachines = lib.filterAttrs (
name: _: !(lib.elem name config.clan.zerotier-static-peers.excludeHosts)
) machines;
hosts = lib.mapAttrsToList (host: _: host) (
lib.mapAttrs' (
machine: _:
let
fullPath = zerotierIpMachinePath machine;
in
if builtins.pathExists fullPath then
lib.nameValuePair (builtins.readFile fullPath) [ machine ]
else
null
) filteredMachines
);
in
lib.mkIf (config.clan.networking.zerotier.controller.enable) {
wantedBy = [ "multi-user.target" ];
after = [ "zerotierone.service" ];
path = [ pkgs.zerotierone ];
serviceConfig.ExecStart = pkgs.writeScript "static-zerotier-peers-autoaccept" ''
#!/bin/sh
${lib.concatMapStringsSep "\n" (host: ''
${
inputs.clan-core.packages.${pkgs.system}.zerotier-members
}/bin/zerotier-members allow --member-ip ${host}
'') hosts}
'';
};
config.clan.networking.zerotier.networkId = lib.mkDefault networkId;
}

View File

@ -20,7 +20,7 @@
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${
pkgs.callPackage ../../pkgs/zt-tcp-relay { }
pkgs.callPackage ../pkgs/zt-tcp-relay { }
}/bin/zt-tcp-relay --listen [::]:${builtins.toString config.clan.zt-tcp-relay.port}";
Restart = "always";
RestartSec = "5";

View File

@ -1,2 +0,0 @@
Enable ZeroTier VPN over TCP for networks where UDP is blocked.
---

View File

@ -1,4 +1,3 @@
{ ... }:
{
perSystem =
{
@ -27,8 +26,7 @@
packages = [
select-shell
pkgs.tea
# Better error messages than nix 2.18
pkgs.nixVersions.latest
pkgs.nix
self'.packages.tea-create-pr
self'.packages.merge-after-ci
self'.packages.pending-reviews
@ -36,6 +34,9 @@
config.treefmt.build.wrapper
];
shellHook = ''
# no longer used
rm -f "$(git rev-parse --show-toplevel)/.git/hooks/pre-commit"
echo -e "${ansiEscapes.green}switch to another dev-shell using: select-shell${ansiEscapes.reset}"
'';
};

View File

@ -1,6 +1,6 @@
source_up
watch_file $(find ./nix -name "*.nix" -printf '%p ')
watch_file nix/flake-module.nix nix/shell.nix nix/default.nix
# Because we depend on nixpkgs sources, uploading to builders takes a long time
use flake .#docs --builders ''

3
docs/.gitignore vendored
View File

@ -1,3 +0,0 @@
/site/reference
/site/static/Roboto-Regular.ttf
/site/static/FiraCode-VF.ttf

View File

@ -15,124 +15,92 @@ Let's get your development environment up and running:
1. **Install Nix Package Manager**:
- You can install the Nix package manager by either [downloading the Nix installer](https://github.com/DeterminateSystems/nix-installer/releases) or running this command:
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```
- You can install the Nix package manager by either [downloading the Nix installer](https://github.com/DeterminateSystems/nix-installer/releases) or running this command:
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```
2. **Install direnv**:
- To automatically setup a devshell on entering the directory
```bash
nix profile install nixpkgs#nix-direnv-flakes
```
- Download the direnv package from [here](https://direnv.net/docs/installation.html) or run the following command:
```bash
curl -sfL https://direnv.net/install.sh | bash
```
3. **Add direnv to your shell**:
- Direnv needs to [hook into your shell](https://direnv.net/docs/hook.html) to work.
You can do this by executing following command. The example below will setup direnv for `zsh` and `bash`
- Direnv needs to [hook into your shell](https://direnv.net/docs/hook.html) to work.
You can do this by executing following command. The example below will setup direnv for `zsh` and `bash`
```bash
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc && echo 'eval "$(direnv hook bash)"' >> ~/.bashrc && eval "$SHELL"
```
4. **Clone the Repository and Navigate**:
- Clone this repository and navigate to it.
5. **Allow .envrc**:
- When you enter the directory, you'll receive an error message like this:
```bash
direnv: error .envrc is blocked. Run `direnv allow` to approve its content
```
- Execute `direnv allow` to automatically execute the shell script `.envrc` when entering the directory.
# Setting Up Your Git Workflow
Let's set up your Git workflow to collaborate effectively:
1. **Register Your Gitea Account Locally**:
- Execute the following command to add your Gitea account locally:
```bash
tea login add
```
- Fill out the prompt as follows:
- URL of Gitea instance: `https://git.clan.lol`
- Name of new Login [gitea.gchq.icu]: `gitea.gchq.icu:7171`
- Do you have an access token? No
- Username: YourUsername
- Password: YourPassword
- Set Optional settings: No
2. **Git Workflow**:
1. Add your changes to Git using `git add <file1> <file2>`.
2. Run `nix fmt` to lint your files.
3. Commit your changes with a descriptive message: `git commit -a -m "My descriptive commit message"`.
4. Make sure your branch has the latest changes from upstream by executing:
```bash
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc && echo 'eval "$(direnv hook bash)"' >> ~/.bashrc && eval "$SHELL"
git fetch && git rebase origin/main --autostash
```
5. Use `git status` to check for merge conflicts.
6. If conflicts exist, resolve them. Here's a tutorial for resolving conflicts in [VSCode](https://code.visualstudio.com/docs/sourcecontrol/overview#_merge-conflicts).
7. After resolving conflicts, execute `git merge --continue` and repeat step 5 until there are no conflicts.
4. **Create a Gitea Account**:
- Register an account on https://git.clan.lol
- Fork the [clan-core](https://git.clan.lol/clan/clan-core) repository
- Clone the repository and navigate to it
- Add a new remote called upstream:
```bash
git remote add upstream gitea@git.clan.lol:clan/clan-core.git
```
3. **Create a Pull Request**:
5. **Register Your Gitea Account Locally**:
- To automatically open a pull request that gets merged if all tests pass, execute:
```bash
merge-after-ci
```
- Execute the following command to add your Gitea account locally:
```bash
tea login add
```
- Fill out the prompt as follows:
- URL of Gitea instance: `https://git.clan.lol`
- Name of new Login [git.clan.lol]:
- Do you have an access token? No
- Username: YourUsername
- Password: YourPassword
- Set Optional settings: No
4. **Review Your Pull Request**:
- Visit https://git.clan.lol and go to the project page. Check under "Pull Requests" for any issues with your pull request.
6. **Allow .envrc**:
- When you enter the directory, you'll receive an error message like this:
```bash
direnv: error .envrc is blocked. Run `direnv allow` to approve its content
```
- Execute `direnv allow` to automatically execute the shell script `.envrc` when entering the directory.
7. **(Optional) Install Git Hooks**:
- To syntax check your code you can run:
```bash
nix fmt
```
- To make this automatic install the git hooks
```bash
./scripts/pre-commit
```
8. **Open a Pull Request**:
- To automatically open up a pull request you can use our tool called:
```
merge-after-ci --reviewers Mic92 Lassulus Qubasa
```
5. **Push Your Changes**:
- If there are issues, fix them and redo step 2. Afterward, execute:
```bash
git push origin HEAD:YourUsername-main
```
- This will directly push to your open pull request.
# Debugging
Here are some methods for debugging and testing the clan-cli:
## See all possible packages and tests
To quickly show all possible packages and tests execute:
```bash
nix flake show --system no-eval
```
Under `checks` you will find all tests that are executed in our CI. Under `packages` you find all our projects.
```
git+file:///home/lhebendanz/Projects/clan-core
├───apps
│ └───x86_64-linux
│ ├───install-vm: app
│ └───install-vm-nogui: app
├───checks
│ └───x86_64-linux
│ ├───borgbackup omitted (use '--all-systems' to show)
│ ├───check-for-breakpoints omitted (use '--all-systems' to show)
│ ├───clan-dep-age omitted (use '--all-systems' to show)
│ ├───clan-dep-bash omitted (use '--all-systems' to show)
│ ├───clan-dep-e2fsprogs omitted (use '--all-systems' to show)
│ ├───clan-dep-fakeroot omitted (use '--all-systems' to show)
│ ├───clan-dep-git omitted (use '--all-systems' to show)
│ ├───clan-dep-nix omitted (use '--all-systems' to show)
│ ├───clan-dep-openssh omitted (use '--all-systems' to show)
│ ├───"clan-dep-python3.11-mypy" omitted (use '--all-systems' to show)
├───packages
│ └───x86_64-linux
│ ├───clan-cli omitted (use '--all-systems' to show)
│ ├───clan-cli-docs omitted (use '--all-systems' to show)
│ ├───clan-ts-api omitted (use '--all-systems' to show)
│ ├───clan-vm-manager omitted (use '--all-systems' to show)
│ ├───default omitted (use '--all-systems' to show)
│ ├───deploy-docs omitted (use '--all-systems' to show)
│ ├───docs omitted (use '--all-systems' to show)
│ ├───editor omitted (use '--all-systems' to show)
└───templates
├───default: template: Initialize a new clan flake
└───new-clan: template: Initialize a new clan flake
```
You can execute every test separately by following the tree path `nix build .#checks.x86_64-linux.clan-pytest` for example.
## Test Locally in Devshell with Breakpoints
To test the cli locally in a development environment and set breakpoints for debugging, follow these steps:
@ -182,14 +150,12 @@ If you need to inspect the Nix sandbox while running tests, follow these steps:
2. Use `cntr` and `psgrep` to attach to the Nix sandbox. This allows you to interactively debug your code while it's paused. For example:
```bash
cntr exec -w your_sandbox_name
psgrep -a -x your_python_process_name
cntr attach <container id, container name or process id>
```
Or you can also use the [nix breakpoint hook](https://nixos.org/manual/nixpkgs/stable/#breakpointhook)
# Standards
- Every new module name should be in kebab-case.
- Every fact definition, where possible should be in kebab-case.
Every new module name should be in kebab-case.
Every fact definition, where possible should be in kebab-case.

View File

@ -20,11 +20,11 @@ There are several reasons for choosing to self-host. These can include:
Alice wants to self-host a mumble server for her family.
- She visits to the Clan website, and follows the instructions on how to install Clan-OS on her server.
- Alice logs into a terminal on her server via SSH (alternatively uses Clan GUI app)
- Using the Clan CLI or GUI tool, alice creates a new private network for her family (VPN)
- Alice now browses a list of curated Clan modules and finds a module for mumble.
- She adds this module to her network using the Clan tool.
- She visits to the cLAN website, and follows the instructions on how to install cLAN-OS on her server.
- Alice logs into a terminal on her server via SSH (alternatively uses cLAN GUI app)
- Using the cLAN CLI or GUI tool, alice creates a new private network for her family (VPN)
- Alice now browses a list of curated cLAN modules and finds a module for mumble.
- She adds this module to her network using the cLAN tool.
- After that, she uses the clan tool to invite her family members to her network
- Other family members join the private network via the invitation.
- By accepting the invitation, other members automatically install all required software to interact with the network on their machine.
@ -33,7 +33,7 @@ Alice wants to self-host a mumble server for her family.
Alice wants to add a photos app to her private network
- She uses the clan CLI or GUI tool to manage her existing private Clan family network
- She uses the clan CLI or GUI tool to manage her existing private cLAN family network
- She discovers a module for photoprism, and adds it to her server using the tool
- Other members who are already part of her network, will receive a notification that an update is required to their environment
- After accepting, all new software and services to interact with the new photoprism service will be installed automatically.

View File

@ -1,4 +1,4 @@
# Joining a Clan network
# Joining a cLAN network
## General Description
@ -8,13 +8,13 @@ Joining a self-hosted infrastructure involves connecting to a network, server, o
### Story 1: Joining a private network
Alice' son Bob has never heard of Clan, but receives an invitation URL from Alice who already set up private Clan network for her family.
Alice' son Bob has never heard of cLAN, but receives an invitation URL from Alice who already set up private cLAN network for her family.
Bob opens the invitation link and lands on the Clan website. He quickly learns about what Clan is and can see that the invitation is for a private network of his family that hosts a number of services, like a private voice chat and a photo sharing platform.
Bob opens the invitation link and lands on the cLAN website. He quickly learns about what cLAN is and can see that the invitation is for a private network of his family that hosts a number of services, like a private voice chat and a photo sharing platform.
Bob decides to join the network and follows the instructions to install the Clan tool on his computer.
Bob decides to join the network and follows the instructions to install the cLAN tool on his computer.
Feeding the invitation link to the Clan tool, bob registers his machine with the network.
Feeding the invitation link to the cLAN tool, bob registers his machine with the network.
All programs required to interact with the network will be installed and configured automatically and securely.
@ -22,7 +22,7 @@ Optionally, bob can customize the configuration of these programs through a simp
### Story 2: Receiving breaking changes
The Clan family network which Bob is part of received an update.
The cLAN family network which Bob is part of received an update.
The existing photo sharing service has been removed and replaced with another alternative service. The new photo sharing service requires a different client app to view and upload photos.
@ -30,7 +30,7 @@ Bob accepts the update. Now his environment will be updated. The old client soft
Because Bob has customized the previous photo viewing app, he is notified that this customization is no longer valid, as the software has been removed (deprecation message).l
Optionally, Bob can now customize the new photo viewing software through his Clan configuration app or via a config file.
Optionally, Bob can now customize the new photo viewing software through his cLAN configuration app or via a config file.
## Challenges

View File

@ -1,10 +1,10 @@
# Clan module maintaining
# cLAN module maintaining
## General Description
Clan modules are pieces of software that can be used by admins to build a private or public infrastructure.
cLAN modules are pieces of software that can be used by admins to build a private or public infrastructure.
Clan modules should have the following properties:
cLAN modules should have the following properties:
1. Documented: It should be clear what the module does and how to use it.
1. Self contained: A module should be usable as is. If it requires any other software or settings, those should be delivered with the module itself.

View File

@ -1,41 +0,0 @@
from typing import Any
def define_env(env: Any) -> None:
static_dir = "/static/"
video_dir = "https://clan.lol/" + "videos/"
asciinema_dir = static_dir + "asciinema-player/"
@env.macro
def video(name: str) -> str:
return f"""<video loop muted autoplay id="{name}">
<source src={video_dir + name} type="video/webm">
Your browser does not support the video tag.
</video>"""
@env.macro
def asciinema(name: str) -> str:
return f"""<div id="{name}">
<script>
// Function to load the script and then create the Asciinema player
function loadAsciinemaPlayer() {{
var script = document.createElement('script');
script.src = "{asciinema_dir}/asciinema-player.min.js";
script.onload = function() {{
AsciinemaPlayer.create('{video_dir + name}', document.getElementById("{name}"), {{
loop: true,
autoPlay: true,
controls: false,
speed: 1.5,
theme: "solarized-light"
}});
}};
document.head.appendChild(script);
}}
// Load the Asciinema player script
loadAsciinemaPlayer();
</script>
<link rel="stylesheet" type="text/css" href="{asciinema_dir}/asciinema-player.css" />
</div>"""

View File

@ -1,4 +1,4 @@
site_name: Clan Documentation
site_name: cLAN documentation
site_url: https://docs.clan.lol
repo_url: https://git.clan.lol/clan/clan-core/
repo_name: clan-core
@ -10,25 +10,17 @@ validation:
unrecognized_links: warn
markdown_extensions:
- admonition
- attr_list
- footnotes
- md_in_html
- meta
- plantuml_markdown
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- footnotes
- meta
- admonition
- pymdownx.details
- pymdownx.highlight:
use_pygments: true
anchor_linenums: true
- pymdownx.keys
- toc:
title: On this page
@ -38,75 +30,30 @@ exclude_docs: |
/drafts/
nav:
- Blog:
- blog/index.md
- Getting started:
- index.md
- Installer: getting-started/installer.md
- Configure: getting-started/configure.md
- Secrets & Facts: getting-started/secrets.md
- Deploy Machine: getting-started/deploy.md
- Mesh VPN: getting-started/mesh-vpn.md
- Deploy Machine: getting-started/machines.md
- Installer: getting-started/installer.md
- Setup Networking: getting-started/networking.md
- Provision Secrets & Passwords: getting-started/secrets.md
- Backup & Restore: getting-started/backups.md
- Flake-parts: getting-started/flake-parts.md
- Modules:
- Clan Modules:
- reference/clanModules/borgbackup.md
- reference/clanModules/deltachat.md
- reference/clanModules/disk-layouts.md
- reference/clanModules/ergochat.md
- reference/clanModules/localbackup.md
- reference/clanModules/localsend.md
- reference/clanModules/matrix-synapse.md
- reference/clanModules/moonlight.md
- reference/clanModules/root-password.md
- reference/clanModules/sshd.md
- reference/clanModules/sunshine.md
- reference/clanModules/syncthing.md
- reference/clanModules/static-hosts.md
- reference/clanModules/thelounge.md
- reference/clanModules/trusted-nix-caches.md
- reference/clanModules/user-password.md
- reference/clanModules/xfce.md
- reference/clanModules/zerotier-static-peers.md
- reference/clanModules/zt-tcp-relay.md
- CLI:
- reference/cli/index.md
- reference/cli/backups.md
- reference/cli/config.md
- reference/cli/facts.md
- reference/cli/flakes.md
- reference/cli/flash.md
- reference/cli/history.md
- reference/cli/machines.md
- reference/cli/secrets.md
- reference/cli/ssh.md
- reference/cli/vms.md
- Clan Core:
- reference/clan-core/index.md
- reference/clan-core/backups.md
- reference/clan-core/facts.md
- reference/clan-core/sops.md
- reference/clan-core/state.md
- Templates: templates/index.md
- Contributing: contributing/contributing.md
docs_dir: site
site_dir: out
theme:
font: false
logo: https://clan.lol/static/logo/clan-white.png
favicon: https://clan.lol/static/dark-favicon/128x128.png
logo: static/logo.png
name: material
features:
- navigation.instant
- navigation.tabs
- content.code.annotate
- content.code.copy
- content.tabs.link
icon:
repo: fontawesome/brands/git-alt
custom_dir: overrides
repo: fontawesome/brands/git
palette:
# Palette toggle for light mode
@ -127,33 +74,5 @@ theme:
icon: material/weather-sunny
name: Switch to light mode
extra_css:
- static/extra.css
extra:
social:
- icon: fontawesome/regular/comment
link: https://matrix.to/#/#clan:lassul.us
- icon: fontawesome/brands/gitlab
link: https://git.clan.lol/clan/clan-core
- icon: fontawesome/brands/github
link: https://github.com/clan-lol/clan-core
- icon: fontawesome/solid/rss
link: /feed_rss_created.xml
plugins:
- search
- blog
- macros
- rss:
match_path: blog/posts/.*
use_git: false
date_from_meta:
as_creation: "date"
as_update: "date"
datetime_format: "%Y-%m-%d %H:%M"
default_timezone: Europe/Paris
default_time: "17:18"
categories:
- categories
- tags

72
docs/modules/default.nix Normal file
View File

@ -0,0 +1,72 @@
{
self,
lib,
inputs,
...
}:
{
imports = [ ./zola-pages.nix ];
perSystem =
{ pkgs, ... }:
let
allNixosModules = (import "${inputs.nixpkgs}/nixos/modules/module-list.nix") ++ [
"${inputs.nixpkgs}/nixos/modules/misc/assertions.nix"
{ nixpkgs.hostPlatform = "x86_64-linux"; }
];
clanCoreNixosModules = [
self.nixosModules.clanCore
{ clanCore.clanDir = ./.; }
] ++ allNixosModules;
# TODO: optimally we would not have to evaluate all nixos modules for every page
# but some of our module options secretly depend on nixos modules.
# We would have to get rid of these implicit dependencies and make them explicit
clanCoreNixos = pkgs.nixos { imports = clanCoreNixosModules; };
# using extendModules here instead of re-evaluating nixos every time
# improves eval performance slightly (10%)
options = modules: (clanCoreNixos.extendModules { inherit modules; }).options;
docs =
options:
pkgs.nixosOptionsDoc {
options = options;
warningsAreErrors = false;
# transform each option so that the declaration link points to git.clan.lol
# and not to the /nix/store
transformOptions =
opt:
opt
// {
declarations = lib.forEach opt.declarations (
decl:
if lib.hasPrefix "${self}" decl then
let
subpath = lib.removePrefix "${self}" decl;
in
{
url = "https://git.clan.lol/clan/clan-core/src/branch/main/" + subpath;
name = subpath;
}
else
decl
);
};
};
outputsFor = name: docs: { packages."docs-md-${name}" = docs.optionsCommonMark; };
clanModulesPages = lib.flip lib.mapAttrsToList self.clanModules (
name: module: outputsFor "module-${name}" (docs ((options [ module ]).clan.${name} or { }))
);
in
{
imports = clanModulesPages ++ [
# renders all clanCore options in a single page
(outputsFor "core-options" (docs (options [ ]).clanCore))
];
};
}

View File

@ -0,0 +1,88 @@
{
perSystem =
{
lib,
pkgs,
self',
...
}:
let
getMdPages =
prefix:
let
mdDocs' = lib.filterAttrs (name: _: lib.hasPrefix prefix name) self'.packages;
mdDocs = lib.mapAttrs' (name: pkg: lib.nameValuePair (lib.removePrefix prefix name) pkg) mdDocs';
in
if mdDocs != { } then
mdDocs
else
throw ''
Error: no markdown files found in clan-core.packages' with prefix "${prefix}"
'';
makeZolaIndexMd =
title: weight:
pkgs.writeText "_index.md" ''
+++
title = "${title}"
template = "docs/section.html"
weight = ${toString weight}
sort_by = "title"
draft = false
+++
'';
makeZolaPages =
{
sectionTitle,
files,
makeIntro ? _name: "",
weight ? 9999,
}:
pkgs.runCommand "zola-pages-clan-core" { } ''
mkdir $out
# create new section via _index.md
cp ${makeZolaIndexMd sectionTitle weight} $out/_index.md
# generate zola compatible md files for each nixos options md
${lib.concatStringsSep "\n" (
lib.flip lib.mapAttrsToList files (
name: md: ''
# generate header for zola with title, template, weight
title="${name}"
echo -e "+++\ntitle = \"$title\"\ntemplate = \"docs/page.html\"\n+++" > "$out/${name}.md"
cat <<EOF >> "$out/${name}.md"
${makeIntro name}
EOF
# append everything from the nixpkgs generated md file
cat "${md}" >> "$out/${name}.md"
''
)
)}
'';
in
{
packages.docs-zola-pages-core = makeZolaPages {
sectionTitle = "cLAN Core Reference";
files = getMdPages "docs-md-core-";
weight = 20;
};
packages.docs-zola-pages-modules = makeZolaPages {
sectionTitle = "cLAN Modules Reference";
files = getMdPages "docs-md-module-";
weight = 25;
makeIntro = name: ''
To use this module, import it like this:
\`\`\`nix
{config, lib, inputs, ...}: {
imports = [inputs.clan-core.clanModules.${name}];
# ...
}
\`\`\`
'';
};
};
}

View File

@ -1,45 +1,15 @@
{
pkgs,
module-docs,
clan-cli-docs,
asciinema-player-js,
asciinema-player-css,
roboto,
fira-code,
...
}:
let
uml-c4 = pkgs.python3Packages.plantuml-markdown.override { plantuml = pkgs.plantuml-c4; };
in
{ pkgs, ... }:
pkgs.stdenv.mkDerivation {
name = "clan-documentation";
src = ../.;
nativeBuildInputs =
[
pkgs.python3
uml-c4
]
[ pkgs.python3 ]
++ (with pkgs.python3Packages; [
mkdocs
mkdocs-material
mkdocs-rss-plugin
mkdocs-macros
]);
configurePhase = ''
mkdir -p ./site/reference/cli
cp -af ${module-docs}/* ./site/reference/
cp -af ${clan-cli-docs}/* ./site/reference/cli/
mkdir -p ./site/static/asciinema-player
ln -snf ${asciinema-player-js} ./site/static/asciinema-player/asciinema-player.min.js
ln -snf ${asciinema-player-css} ./site/static/asciinema-player/asciinema-player.css
# Link to fonts
ln -snf ${roboto}/share/fonts/truetype/Roboto-Regular.ttf ./site/static/
ln -snf ${fira-code}/share/fonts/truetype/FiraCode-VF.ttf ./site/static/
'';
buildPhase = ''
mkdocs build --strict

View File

@ -8,7 +8,7 @@
}:
writeShellScriptBin "deploy-docs" ''
set -eu -o pipefail
set -eux -o pipefail
export PATH="${
lib.makeBinPath [
coreutils
@ -17,26 +17,13 @@ writeShellScriptBin "deploy-docs" ''
]
}"
#########################################
# #
# DO NOT PRINT THE SSH KEY TO THE LOGS #
# #
#########################################
set +x
if [ -n "''${SSH_HOMEPAGE_KEY:-}" ]; then
if [ -n "$SSH_HOMEPAGE_KEY" ]; then
echo "$SSH_HOMEPAGE_KEY" > ./ssh_key
chmod 600 ./ssh_key
sshExtraArgs="-i ./ssh_key"
else
sshExtraArgs=
fi
set -x
###########################
# #
# END OF DANGER ZONE #
# #
###########################
rsync \
-e "ssh -o StrictHostKeyChecking=no $sshExtraArgs" \

View File

@ -1,4 +1,4 @@
{ inputs, self, ... }:
{ inputs, ... }:
{
perSystem =
{
@ -7,77 +7,11 @@
pkgs,
...
}:
let
# Simply evaluated options (JSON)
# { clanCore = «derivation JSON»; clanModules = { ${name} = «derivation JSON» }; }
jsonDocs = import ./get-module-docs.nix {
inherit (inputs) nixpkgs;
inherit pkgs self;
inherit (self.nixosModules) clanCore;
inherit (self) clanModules;
};
clanModulesFileInfo = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModules);
clanModulesReadmes = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesReadmes);
# Simply evaluated options (JSON)
renderOptions =
pkgs.runCommand "renderOptions.py"
{
# TODO: ruff does not splice properly in nativeBuildInputs
depsBuildBuild = [ pkgs.ruff ];
nativeBuildInputs = [
pkgs.python3
pkgs.mypy
];
}
''
install ${./scripts/renderOptions.py} $out
patchShebangs --build $out
ruff format --check --diff $out
ruff --line-length 88 $out
mypy --strict $out
'';
asciinema-player-js = pkgs.fetchurl {
url = "https://github.com/asciinema/asciinema-player/releases/download/v3.7.0/asciinema-player.min.js";
sha256 = "sha256-Ymco/+FinDr5YOrV72ehclpp4amrczjo5EU3jfr/zxs=";
};
asciinema-player-css = pkgs.fetchurl {
url = "https://github.com/asciinema/asciinema-player/releases/download/v3.7.0/asciinema-player.css";
sha256 = "sha256-GZMeZFFGvP5GMqqh516mjJKfQaiJ6bL38bSYOXkaohc=";
};
module-docs = pkgs.runCommand "rendered" { nativeBuildInputs = [ pkgs.python3 ]; } ''
export CLAN_CORE=${jsonDocs.clanCore}/share/doc/nixos/options.json
# A file that contains the links to all clanModule docs
export CLAN_MODULES=${clanModulesFileInfo}
export CLAN_MODULES_READMES=${clanModulesReadmes}
mkdir $out
# The python script will place mkDocs files in the output directory
python3 ${renderOptions}
'';
in
{
devShells.docs = pkgs.callPackage ./shell.nix {
inherit (self'.packages) docs clan-cli-docs;
inherit module-docs;
inherit asciinema-player-js;
inherit asciinema-player-css;
};
devShells.docs = pkgs.callPackage ./shell.nix { inherit (self'.packages) docs; };
packages = {
docs = pkgs.python3.pkgs.callPackage ./default.nix {
inherit (self'.packages) clan-cli-docs;
inherit (inputs) nixpkgs;
inherit module-docs;
inherit asciinema-player-js;
inherit asciinema-player-css;
};
docs = pkgs.python3.pkgs.callPackage ./default.nix { inherit (inputs) nixpkgs; };
deploy-docs = pkgs.callPackage ./deploy-docs.nix { inherit (config.packages) docs; };
inherit module-docs;
};
};
}

View File

@ -1,51 +0,0 @@
{
nixpkgs,
pkgs,
clanCore,
clanModules,
self,
}:
let
allNixosModules = (import "${nixpkgs}/nixos/modules/module-list.nix") ++ [
"${nixpkgs}/nixos/modules/misc/assertions.nix"
{ nixpkgs.hostPlatform = "x86_64-linux"; }
];
clanCoreNixosModules = [
clanCore
{ clanCore.clanDir = ./.; }
] ++ allNixosModules;
# TODO: optimally we would not have to evaluate all nixos modules for every page
# but some of our module options secretly depend on nixos modules.
# We would have to get rid of these implicit dependencies and make them explicit
clanCoreNixos = pkgs.nixos { imports = clanCoreNixosModules; };
# using extendModules here instead of re-evaluating nixos every time
# improves eval performance slightly (10%)
getOptions = modules: (clanCoreNixos.extendModules { inherit modules; }).options;
evalDocs =
options:
pkgs.nixosOptionsDoc {
options = options;
warningsAreErrors = false;
};
# clanModules docs
clanModulesDocs = builtins.mapAttrs (
name: module: (evalDocs ((getOptions [ module ]).clan.${name} or { })).optionsJSON
) clanModules;
clanModulesReadmes = builtins.mapAttrs (
module_name: _module: self.lib.modules.getReadme module_name
) clanModules;
# clanCore docs
clanCoreDocs = (evalDocs (getOptions [ ]).clanCore).optionsJSON;
in
{
inherit clanModulesReadmes;
clanCore = clanCoreDocs;
clanModules = clanModulesDocs;
}

View File

@ -1,237 +0,0 @@
# Options are available in the following format:
# https://github.com/nixos/nixpkgs/blob/master/nixos/lib/make-options-doc/default.nix
#
# ```json
# {
# ...
# "fileSystems.<name>.options": {
# "declarations": ["nixos/modules/tasks/filesystems.nix"],
# "default": {
# "_type": "literalExpression",
# "text": "[\n \"defaults\"\n]"
# },
# "description": "Options used to mount the file system.",
# "example": {
# "_type": "literalExpression",
# "text": "[\n \"data=journal\"\n]"
# },
# "loc": ["fileSystems", "<name>", "options"],
# "readOnly": false,
# "type": "non-empty (list of string (with check: non-empty))"
# "relatedPackages": "- [`pkgs.tmux`](\n https://search.nixos.org/packages?show=tmux&sort=relevance&query=tmux\n )\n",
# }
# }
# ```
import json
import os
from pathlib import Path
from typing import Any
# Get environment variables
CLAN_CORE = os.getenv("CLAN_CORE")
CLAN_MODULES = os.environ.get("CLAN_MODULES")
CLAN_MODULES_READMES = os.environ.get("CLAN_MODULES_READMES")
OUT = os.environ.get("out")
def sanitize(text: str) -> str:
return text.replace(">", "\\>")
def replace_store_path(text: str) -> tuple[str, str]:
res = text
if text.startswith("/nix/store/"):
res = "https://git.clan.lol/clan/clan-core/src/branch/main/" + str(
Path(*Path(text).parts[4:])
)
name = Path(res).name
return (res, name)
def render_option_header(name: str) -> str:
return f"# {name}\n"
def join_lines_with_indentation(lines: list[str], indent: int = 4) -> str:
"""
Joins multiple lines with a specified number of whitespace characters as indentation.
Args:
lines (list of str): The lines of text to join.
indent (int): The number of whitespace characters to use as indentation for each line.
Returns:
str: The indented and concatenated string.
"""
# Create the indentation string (e.g., four spaces)
indent_str = " " * indent
# Join each line with the indentation added at the beginning
return "\n".join(indent_str + line for line in lines)
def render_option(name: str, option: dict[str, Any], level: int = 3) -> str:
read_only = option.get("readOnly")
res = f"""
{"#" * level} {sanitize(name)}
{"Readonly" if read_only else ""}
{option.get("description", "No description available.")}
**Type**: `{option["type"]}`
"""
if option.get("default"):
res += f"""
**Default**:
```nix
{option["default"]["text"] if option.get("default") else "No default set."}
```
"""
example = option.get("example", {}).get("text")
if example:
example_indented = join_lines_with_indentation(example.split("\n"))
res += f"""
???+ example
```nix
{example_indented}
```
"""
if option.get("relatedPackages"):
res += f"""
### Related Packages
{option["relatedPackages"]}
"""
decls = option.get("declarations", [])
source_path, name = replace_store_path(decls[0])
print(source_path, name)
res += f"""
:simple-git: [{name}]({source_path})
"""
res += "\n"
return res
def module_header(module_name: str) -> str:
return f"# {module_name}\n"
def module_usage(module_name: str) -> str:
return f"""## Usage
To use this module, import it like this:
```nix
{{config, lib, inputs, ...}}: {{
imports = [ inputs.clan-core.clanModules.{module_name} ];
# ...
}}
```
"""
clan_core_descr = """ClanCore delivers all the essential features for every clan.
It's always included in your setup, and you can customize your clan's behavior with the configuration [options](#module-options) provided below.
"""
options_head = "\n## Module Options\n"
def produce_clan_core_docs() -> None:
if not CLAN_CORE:
raise ValueError(
f"Environment variables are not set correctly: $CLAN_CORE={CLAN_CORE}"
)
if not OUT:
raise ValueError(f"Environment variables are not set correctly: $out={OUT}")
# A mapping of output file to content
core_outputs: dict[str, str] = {}
with open(CLAN_CORE) as f:
options: dict[str, dict[str, Any]] = json.load(f)
module_name = "clan-core"
for option_name, info in options.items():
outfile = f"{module_name}/index.md"
# Create separate files for nested options
if len(option_name.split(".")) <= 2:
# i.e. clan-core.clanDir
output = core_outputs.get(
outfile,
module_header(module_name) + clan_core_descr + options_head,
)
output += render_option(option_name, info)
# Update the content
core_outputs[outfile] = output
else:
# Clan sub-options
[_, sub] = option_name.split(".")[0:2]
outfile = f"{module_name}/{sub}.md"
# Get the content or write the header
output = core_outputs.get(outfile, render_option_header(sub))
output += render_option(option_name, info)
# Update the content
core_outputs[outfile] = output
for outfile, output in core_outputs.items():
(Path(OUT) / outfile).parent.mkdir(parents=True, exist_ok=True)
with open(Path(OUT) / outfile, "w") as of:
of.write(output)
def produce_clan_modules_docs() -> None:
if not CLAN_MODULES:
raise ValueError(
f"Environment variables are not set correctly: $CLAN_MODULES={CLAN_MODULES}"
)
if not CLAN_MODULES_READMES:
raise ValueError(
f"Environment variables are not set correctly: $CLAN_MODULES_READMES={CLAN_MODULES_READMES}"
)
if not OUT:
raise ValueError(f"Environment variables are not set correctly: $out={OUT}")
with open(CLAN_MODULES) as f:
links: dict[str, str] = json.load(f)
with open(CLAN_MODULES_READMES) as readme:
readme_map: dict[str, str] = json.load(readme)
# {'borgbackup': '/nix/store/hi17dwgy7963ddd4ijh81fv0c9sbh8sw-options.json', ... }
for module_name, options_file in links.items():
with open(Path(options_file) / "share/doc/nixos/options.json") as f:
options: dict[str, dict[str, Any]] = json.load(f)
print(f"Rendering options for {module_name}...")
output = module_header(module_name)
if readme_map.get(module_name, None):
output += f"{readme_map[module_name]}\n"
output += module_usage(module_name)
output += options_head if len(options.items()) else ""
for option_name, info in options.items():
output += render_option(option_name, info)
outfile = Path(OUT) / f"clanModules/{module_name}.md"
outfile.parent.mkdir(
parents=True,
exist_ok=True,
)
with open(outfile, "w") as of:
of.write(output)
if __name__ == "__main__":
produce_clan_core_docs()
produce_clan_modules_docs()

View File

@ -1,31 +1 @@
{
docs,
pkgs,
module-docs,
clan-cli-docs,
asciinema-player-js,
asciinema-player-css,
roboto,
fira-code,
...
}:
pkgs.mkShell {
inputsFrom = [ docs ];
shellHook = ''
mkdir -p ./site/reference/cli
cp -af ${module-docs}/* ./site/reference/
cp -af ${clan-cli-docs}/* ./site/reference/cli/
chmod +w ./site/reference/*
echo "Generated API documentation in './site/reference/' "
mkdir -p ./site/static/asciinema-player
ln -snf ${asciinema-player-js} ./site/static/asciinema-player/asciinema-player.min.js
ln -snf ${asciinema-player-css} ./site/static/asciinema-player/asciinema-player.css
# Link to fonts
ln -snf ${roboto}/share/fonts/truetype/Roboto-Regular.ttf ./site/static/
ln -snf ${fira-code}/share/fonts/truetype/FiraCode-VF.ttf ./site/static/
'';
}
{ docs, pkgs, ... }: pkgs.mkShell { inputsFrom = [ docs ]; }

View File

@ -1,12 +0,0 @@
{% extends "base.html" %}
{% block extrahead %}
<meta property="og:title" content="Clan - Documentation, Blog & Getting Started Guide" />
<meta property="og:description" content="Documentation for Clan. The peer-to-peer machine deployment framework." />
<meta property="og:image" content="https://clan.lol/static/dark-favicon/128x128.png" />
<meta property="og:url" content="https://docs.clan.lol" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Clan" />
<meta property="og:locale" content="en_US" />
{% endblock %}

View File

@ -1,26 +0,0 @@
authors:
DavHau:
name: "DavHau"
description: "Core Developer"
avatar: "https://clan.lol/static/profiles/davhau.jpg"
url: "https://DavHau.com"
Lassulus:
name: "Lassulus"
description: "Core Developer"
avatar: "https://clan.lol/static/profiles/lassulus.jpg"
url: "https://http://lassul.us/"
Mic92:
name: "Mic92"
description: "Core Developer"
avatar: "https://clan.lol/static/profiles/mic92.jpg"
url: "https://thalheim.io"
W:
name: "W"
description: "Founder of Clan"
avatar: "https://clan.lol/static/profiles/w_profile.webp"
url: ""
Qubasa:
name: "Qubasa"
description: "Core Developer"
avatar: "https://clan.lol/static/profiles/qubasa.png"
url: "https://github.com/Qubasa"

View File

@ -1,2 +0,0 @@
# Blog

View File

@ -1,72 +0,0 @@
---
title: "Introducing Clan: Full-Stack Computing Redefined"
description: "Introducing Clan, a new model for a decentralized network, designed to provide families, smaller groups, and small businesses a platform thats private, secure, and user-friendly."
authors:
- W
- Qubasa
date: 2024-03-19
---
In a digital age where users are guided increasingly toward submission and dependence, Clan reclaims computing and networking from the ground up.
Clan enables users to build any system from a git repository, automate secret handling, and join devices in a secure darknet. This control extends beyond applications to communication protocols and the operating system itself, putting you fully in charge of your own digital environment.
## Why We're Building Clan
Our mission is simple: to restore fun, freedom, and functionality to computing as an open source project. We believe in building tools that empower users, foster innovation, and challenge the limitations imposed by outdated paradigms. Clan, in its essence, is an open source endeavor; it's our contribution to a future where technology serves humanity, not the other way around.
## How Clan Changes the Game
Clan embodies a new philosophy in system, application, and network design. It enables seamless, secure communication across devices, simplifies software distribution and updates, and offers both public and private network configurations. Here are some of the ways it accomplishes this:
- **Nix as a Foundation:** Imagine a safety net for your computer's operating system, one that lets you make changes or updates without the fear of causing a crash or losing data. Nix simplifies the complexities of system design, ensuring that updates are safe and systems are more reliable.
- **Simplified System Deployment:** Building and managing a computer system, from the operating system to the software you use, often feels like putting together a complex puzzle. With Clan, the puzzle pieces are replaced by a set of building blocks. Leveraging the power of Nix and Clan's innovative toolkit, anyone from tech-savvy administrators to everyday users can create and maintain what we call "full-stack systems" (everything your computer needs to run smoothly).
- **A Leap in Connectivity:** Imagine if you could create private, secure pathways between your devices, bypassing the noisy and often insecure internet. Clan makes this possible through something called "overlay networks." These networks are like private tunnels, allowing your devices to talk to each other securely and directly. With Clan's built-in overlay networks and automatically configured services, connecting your devices becomes seamless, secure, and hassle-free.
- **Security Through Separation:** Clan employs sandboxing and virtual machines, a technology that runs code in isolated environments - so even if you explore new Clans, your system remains protected from potential threats.
- **Reliable:** With Clan, your data and services are preserved for the long haul. We focus on self-hosted backups and integration with the [Fediverse](https://de.wikipedia.org/wiki/Fediverse), a network of interconnected, independent online communities, so your digital life remains uninterrupted and under your control.
## A Glimpse at Clan's Features
- **Social Scaling:** Choose between creating a private sanctuary for your closest contacts, a dynamic space for a self-contained community, or embracing the open web with public Clans anyone can join.
{{ video(name="show_join.webm")}}
- **Seamless VM Integration:** Applications running in virtual machines can appear and behave as if they're part of your main operating system — a blend of power and simplicity.
{{ video(name="show_run.webm")}}
- **Robust Backup Management:** Keep your data safe _forever_ - never worry about cloud services disappearing in 10 years.
{{ asciinema(name="backups.cast") }}
- **Intuitive Secret Management:** Clan simplifies digital security by automating the creation and management of encryption keys and passwords for your services.
{{ asciinema(name="secrets.cast") }}
- **Remote Install:** Set up and manage Clan systems anywhere in the world with just a QR scan or SSH access, making remote installations as easy as snapping a photo or sharing a link.
{{ asciinema(name="nixos-install.cast") }}
## Who Stands to Benefit?
Clan is for anyone and everyone who believes in the power of open source technology to connect, empower, and protect. From system administrators to less tech-savvy individuals, small business owners to privacy-conscious users, Clan offers something for everyone — a way to reclaim control and redefine how we interact with technology.
## Join the Revolution
Ready to control your digital world? Clan is more than a tool—it's a movement. Secure your data, manage your systems easily, or connect with others how you like. Start with Clan for a better digital future.
Connect with us on our [Matrix channel at clan.lol](https://matrix.to/#/#clan:lassul.us) or through our IRC bridges (coming soon).
Want to see the code? Check it out [on our Gitea](https://git.clan.lol/clan/clan-core) or [on GitHub](https://github.com/clan-lol/clan-core).
Or follow our [RSS feed](https://docs.clan.lol/feed_rss_created.xml)!
Join us and be part of changing technology for the better, together.

View File

@ -1,194 +0,0 @@
---
title: "Dev Report: Introducing the NixOS to JSON Schema Converter"
description: "Discover our new library designed to extract JSON schema interfaces from NixOS modules, streamlining frontend development"
authors:
- DavHau
date: 2024-05-25
slug: jsonschema-converter
---
## Overview
Weve developed a new library designed to extract interfaces from NixOS modules and convert them into JSON schemas, paving the way for effortless GUI generation. This blog post outlines the motivations behind this development, demonstrates the capabilities of the library, and guides you through leveraging it to create GUIs seamlessly.
## Motivation
In recent months, our team has been exploring various graphical user interfaces (GUIs) to streamline NixOS machine configuration. While our opinionated Clan modules simplify NixOS configurations, there's a need to configure these modules from diverse frontends, such as:
- Command-line interfaces (CLIs)
- Web-based UIs
- Desktop applications
- Mobile applications
- Large Language Models (LLMs)
Given this need, a universal format like JSON is a natural choice. It is already possible as of now, to import json based NixOS configurations, as illustrated below:
`configuration.json`:
```json
{ "networking": { "hostName": "my-machine" } }
```
This configuration can be then imported inside a classic NixOS config:
```nix
{config, lib, pkgs, ...}: {
imports = [
(lib.importJSON ./configuration.json)
];
}
```
This straightforward approach allows us to build a frontend that generates JSON, enabling the configuration of NixOS machines. But, two critical questions arise:
1. How does the frontend learn about existing configuration options?
2. How can it verify user input without running Nix?
Introducing [JSON schema](https://json-schema.org/), a widely supported standard that defines interfaces in JSON and validates input against them.
Example schema for `networking.hostName`:
```json
{
"type": "object",
"properties": {
"networking": {
"type": "object",
"properties": {
"hostName": {
"type": "string",
"pattern": "^$|^[a-z0-9]([a-z0-9_-]{0,61}[a-z0-9])?$"
}
}
}
}
}
```
## Client-Side Input Validation
Validating input against JSON schemas is both efficient and well-supported across numerous programming languages. Using JSON schema validators, you can accurately check configurations like our `configuration.json`.
Validation example:
```shell
$ nix-shell -p check-jsonschema
$ jsonschema -o pretty ./schema.json -i ./configuration.json
===[SUCCESS]===(./configuration.json)===
```
In case of invalid input, schema validators provide explicit error messages:
```shell
$ echo '{ "networking": { "hostName": "my/machine" } }' > configuration.json
$ jsonschema -o pretty ./schema.json -i ./configuration.json
===[ValidationError]===(./configuration.json)===
'my/machine' does not match '^$|^[a-z0-9]([a-z0-9_-]{0,61}[a-z0-9])?$'
Failed validating 'pattern' in schema['properties']['networking']['properties']['hostName']:
{'pattern': '^$|^[a-z0-9]([a-z0-9_-]{0,61}[a-z0-9])?$',
'type': 'string'}
On instance['networking']['hostName']:
'my/machine'
```
## Automatic GUI Generation
Certain libraries facilitate straightforward GUI generation from JSON schemas. For instance, the [react-jsonschema-form playground](https://rjsf-team.github.io/react-jsonschema-form/) auto-generates a form for any given schema.
## NixOS Module to JSON Schema Converter
To enable the development of responsive frontends, our library allows the extraction of interfaces from NixOS modules to JSON schemas. Open-sourced for community collaboration, this library supports building sophisticated user interfaces for NixOS.
Heres a preview of our library's functions exposed through the [clan-core](https://git.clan.lol/clan/clan-core) flake:
- `lib.jsonschema.parseModule` - Generates a schema for a NixOS module.
- `lib.jsonschema.parseOption` - Generates a schema for a single NixOS option.
- `lib.jsonschema.parseOptions` - Generates a schema from an attrset of NixOS options.
Example:
`module.nix`:
```nix
{lib, config, pkgs, ...}: {
# a simple service with two options
options.services.example-web-service = {
enable = lib.mkEnableOption "Example web service";
port = lib.mkOption {
type = lib.types.int;
description = "Port used to serve the content";
};
};
}
```
Converted, using the `parseModule` function:
```shell
$ cd clan-core
$ nix eval --json --impure --expr \
'(import ./lib/jsonschema {}).parseModule ./module.nix' | jq | head
{
"properties": {
"services": {
"properties": {
"example-web-service": {
"properties": {
"enable": {
"default": false,
"description": "Whether to enable Example web service.",
"examples": [
...
```
This utility can also generate interfaces for existing NixOS modules or options.
## GUI for NGINX in Under a Minute
Creating a prototype GUI for the NGINX module using our library and [react-jsonschema-form playground](https://rjsf-team.github.io/react-jsonschema-form/) can be done quickly:
1. Export all NGINX options into a JSON schema using a Nix expression:
```nix
# export.nix
let
pkgs = import <nixpkgs> {};
clan-core = builtins.getFlake "git+https://git.clan.lol/clan/clan-core";
options = (pkgs.nixos {}).options.services.nginx;
in
clan-core.lib.jsonschema.parseOption options
```
2. Write the schema into a file:
```shell
$ nix eval --json -f ./export.nix | jq > nginx.json
```
3. Open the [react-jsonschema-form playground](https://rjsf-team.github.io/react-jsonschema-form/), select `Blank` and paste the `nginx.json` contents.
This provides a quick look at a potential GUI (screenshot is cropped).
![Image title](https://clan.lol/static/blog-post-jsonschema/nginx-gui.jpg)
## Limitations
### Laziness
JSON schema mandates the declaration of all required fields upfront, which might be configured implicitly or remain unused. For instance, `services.nginx.virtualHosts.<name>.sslCertificate` must be specified even if SSL isnt enabled.
### Limited Types
Certain NixOS module types, like `types.functionTo` and `types.package`, do not map straightforwardly to JSON. For full compatibility, adjustments to NixOS modules might be necessary, such as substituting `listOf package` with `listOf str`.
### Parsing NixOS Modules
Currently, our converter relies on the `options` attribute of evaluated NixOS modules, extracting information from the `type.name` attribute, which is suboptimal. Enhanced introspection capabilities within the NixOS module system would be beneficial.
## Future Prospects
We hope these experiments inspire the community, encourage contributions and further development in this space. Share your ideas and contributions through our issue tracker or matrix channel!
## Links
- [Comments on NixOS Discourse](https://discourse.nixos.org/t/introducing-the-nixos-to-json-schema-converter/45948)
- [Source Code of the JSON Schema Library](https://git.clan.lol/clan/clan-core/src/branch/main/lib/jsonschema)
- [Our Issue Tracker](https://git.clan.lol/clan/clan-core/issues)
- [Our Matrix Channel](https://matrix.to/#/#clan:lassul.us)
- [react-jsonschema-form Playground](https://rjsf-team.github.io/react-jsonschema-form/)

View File

@ -1,13 +0,0 @@
---
title: "New documentation site and weekly new meetup"
authors:
- Lassulus
- Mic92
date: 2024-04-16
---
Last week, we added a new documentation hub for clan at [docs.clan.lol](https://docs.clan.lol).
We are still working on improving the installation procedures, so stay tuned.
We now have weekly office hours where people are invited to hangout and ask questions.
They are every Wednesday 15:30 UTC (17:30 CEST) in our [jitsi](https://jitsi.lassul.us/clan.lol).
Otherwise drop by in our [matrix channel](https://matrix.to/#/#clan:lassul.us).

View File

@ -1,63 +0,0 @@
---
title: "Git Based Machine Deployment with Clan-Core"
description: ""
authors:
- Qubasa
date: 2024-05-25
---
## Revolutionizing Server Management
In the world of server management, countless tools claim to offer seamless deployment of multiple machines. Yet, many fall short, leaving server admins and self-hosting enthusiasts grappling with complexity. Enter the Clan-Core Framework—a groundbreaking all in one solution designed to transform decentralized self-hosting into an effortless and scalable endeavor.
### The Power of Clan-Core
Imagine having the power to manage your servers with unparalleled ease, scaling your IT infrastructure like never before. Clan-Core empowers you to do just that. At its core, Clan-Core leverages a single Git repository to define everything about your machines. This central repository utilizes Nix or JSON files to specify configurations, including disk formatting, ensuring a streamlined and unified approach.
### Simplified Deployment Process
With Clan-Core, the cumbersome task of bootstrapping a specific ISO is a thing of the past. All you need is SSH access to your Linux server. Clan-Core allows you to overwrite any existing Linux distribution live over SSH, eliminating time-consuming setup processes. This capability means you can deploy updates or new configurations swiftly and efficiently, maximizing uptime and minimizing hassle.
### Secure and Efficient Secret Management
Security is paramount in server management, and Clan-Core takes it seriously. Passwords and other sensitive information are encrypted within the Git repository, automatically decrypted during deployment. This not only ensures the safety of your secrets but also simplifies their management. Clan-Core supports sharing secrets with other admins, fostering collaboration and maintaining reproducibillity and security without sacrificing convenience.
### Services as Apps
Setting up a service can be quite difficult. Many server adjustments need to be made, from setting up a database to adjusting webserver configurations and generating the correct private keys. However, Clan-Core aims to make setting up a service as easy as installing an application. Through Clan-Core's Module system, everything down to secrets can be automatically set up. This transforms the often daunting task of service setup into a smooth, automated process, making it accessible to all.
### Decentralized Mesh VPN
Building on these features is a self-configuring decentralized mesh VPN that interconnects all your machines into a private darknet. This ensures that sensitive services, which might have too much attack surface to be hosted on the public internet, can still be made available privately without the need to worry about potential system compromise. By creating a secure, private network, Clan-Core offers an additional layer of protection for your most critical services.
### Decentralized Domain Name System
Current DNS implementations are distributed but not truly decentralized. For Clan-Core, we implemented our own truly decentralized DNS module. This module uses simple flooding and caching algorithms to discover available domains inside the darknet. This approach ensures that your internal domain name system is robust, reliable, and independent of external control, enhancing the resilience and security of your infrastructure.
### A New Era of Decentralized Self-Hosting
Clan-Core is more than just a tool; it's a paradigm shift in server management. By consolidating machine definitions, secrets and network configuration, into a single, secure repository, it transforms how you manage and scale your infrastructure. Whether you're a seasoned server admin or a self-hosting enthusiast, Clan-Core offers a powerful, user-friendly solution to take your capabilities to the next level.
### Key Features of Clan-Core:
- **Unified Git Repository**: All machine configurations and secrets stored in a single repository.
- **Live Overwrites**: Deploy configurations over existing Linux distributions via SSH.
- **Automated Service Setup**: Easily set up services with Clan-Core's Module system.
- **Decentralized Mesh VPN**: Securely interconnect all machines into a private darknet.
- **Decentralized DNS**: Robust, independent DNS using flooding and caching algorithms.
- **Automated Secret Management**: Encrypted secrets that are automatically decrypted during deployment.
- **Collaboration Support**: Share secrets securely with other admins.
## Clan-Cores Future
Our vision for Clan-Core extends far beyond being just another deployment tool. Clan-Core is a framework we've developed to achieve something much greater. We want to put the "personal" back into "personal computing." Our goal is for everyday users to fully customize their phones or laptops and create truly private spaces for friends and family.
Our first major step is to develop a Graphical User Interface (GUI) that makes configuring all this possible. Initial tests have shown that AI can be leveraged as an alternative to traditional GUIs. This paves the way for a future where people can simply talk to their computers, and they will configure themselves according to the users' wishes.
By adopting Clan, you're not just embracing a tool—you're joining a movement towards a more efficient, secure, and scalable approach to server management. Join us and revolutionize your IT infrastructure today.

View File

@ -6,19 +6,13 @@ For installations on physical hardware, create a NixOS installer image and trans
To create a bootable USB flash drive with the NixOS installer:
### Download the install iso
### Build the Installer Image
Either with wget:
```shellSession
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso
```bash
nix build git+https://git.clan.lol/clan/clan-core.git#install-iso
```
or with curl:
```shellSession
curl -L https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso -o nixos-installer-x86_64-linux.iso
```
> Make sure you do this inside
### Prepare the USB Flash Drive
@ -26,8 +20,8 @@ curl -L https://github.com/nix-community/nixos-images/releases/download/nixos-un
2. Identify your flash drive with `lsblk`.
```shellSession
lsblk
```bash
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb 8:0 1 117,2G 0 disk
└─sdb1 8:1 1 117,2G 0 part /run/media/qubasa/INTENSO
@ -41,7 +35,7 @@ curl -L https://github.com/nix-community/nixos-images/releases/download/nixos-un
3. Ensure all partitions on the drive are unmounted. Replace `sdX` in the command below with your device identifier (like `sdb`, etc.):
```shellSession
```bash
sudo umount /dev/sdb1
```
@ -49,11 +43,11 @@ curl -L https://github.com/nix-community/nixos-images/releases/download/nixos-un
Use the `dd` utility to write the NixOS installer image to your USB drive:
```shellSession
sudo dd bs=4M conv=fsync oflag=direct status=progress if=./nixos-installer-x86_64-linux.iso of=/dev/sd<X>
```bash
sudo dd bs=4M conv=fsync oflag=direct status=progress if=./result/stick.raw of=/dev/sd<X>
```
In this case, the USB device is `sdb` use `of=/dev/sdb`
In case your USB device is `sdb` use `of=/dev/sdb`
### Boot and Connect
@ -91,15 +85,14 @@ After writing the installer to the USB drive, use it to boot the target machine.
- **Apple**: Option (Alt) Key (Boot Menu for Mac)
- If your hardware was not listed read the manufacturers instructions how to enter the boot Menu/BIOS Setup.
5. Select `NixOS` to boot into the clan installer
6. The installer will display an IP address and a root password, which you can use to connect via SSH.
Alternatively you can also use the displayed QR code.
7. Set your keyboard language (i.e. `de` for German keyboards, default is English). Important for writing passwords correctly.
7. Set your keyboard language. Important for writing passwords correctly.
```shellSession
```bash
loadkeys de
```
@ -107,31 +100,31 @@ After writing the installer to the USB drive, use it to boot the target machine.
1. Bring up the `iwd` shell
```shellSession
```bash
iwctl
```
2. List available networks. Double press tab after station for autocompleting your wlan device. In this case `wlan0`
2. List available networks. Double press tab after station for device autocompletion. In this case `wlan0` is the only network wifi device.
```shellSession
```bash
[iwd] station wlan0 get-networks
```
3. Connect to a Wifi network. Replace `SSID` with the wlan network name.
```shellSession
```bash
[iwd] station wlan0 connect SSID
```
9. Now that you have internet re-execute the init script by pressing `Ctrl+D` or by executing:
```shellSession
```bash
bash
```
10. Connect to the machine over ssh
```shellSession
```bash
ssh-copy-id -o PreferredAuthentications=password root@<ip>
```

View File

@ -1,324 +0,0 @@
## Secrets (CLI Reference)
#### Adding Secrets (set)
```bash
clan secrets set mysecret
> Paste your secret:
```
!!! note
As you type your secret won't be displayed. Press Enter to save the secret.
#### List all Secrets (list)
```bash
clan secrets list
```
#### Assigning Access (set)
By default, secrets are encrypted for your key. To specify which users and machines can access a secret:
```bash
clan secrets set --machine <machine1> --machine <machine2> --user <user1> --user <user2> <secret_name>
```
#### Displaying Secrets (get)
```bash
clan secrets get mysecret
```
#### Rename
TODO
#### Remove
TODO
#### import-sops
TODO
### Users (Reference)
Learn how to manage users and allowing access to existing secrets.
#### list user
Lists all added users
```bash
clan secrets user list
```
``` {.console, title="Example output", .no-copy}
jon
sara
```
!!! Question "Who can execute this command?"
Everyone - completely public.
#### add user
add a user
```bash
clan secrets users add {username} {public-key}
```
!!! Note
Changes can be trusted by maintainer review in version control.
#### get user
get a user public key
```bash
clan secrets users get {username}
```
``` {.console, title="Example output", .no-copy}
age1zk8uzrte55wkg9lkqxu5x6twsj2ja4lehegks0cw4mkg6jv37d9qsjpt44
```
#### remove user
remove a user
```bash
clan secrets users remove {username}
```
!!! Note
Changes can be trusted by maintainer review in version control.
#### add-secret user
Grants the user (`username`) access to the secret (`secret_name`)
```bash
clan secrets users add-secret {username} {secret_name}
```
!!! Note
Requires the executor of the command to have access to the secret (`secret_name`).
#### remove-secret user
remove the user (`username`) from accessing the secret (`secret_name`)
!!! Danger "Make sure at least one person has access."
It might still be possible for the machine to access the secret. (See [machines](#machines))
We highly recommend to use version control such as `git` which allows you to rollback secrets in case anything gets messed up.
```bash
clan secrets users remove-secret {username} {secret_name}
```
!!! Question "Who can execute this command?"
Requires the executor of the command to have access to the secret (`secret_name`).
### Machines (Reference)
- [list](): list machines
- [add](): add a machine
- [get](): get a machine public key
- [remove](): remove a machine
- [add-secret](): allow a machine to access a secret
- [remove-secret](): remove a machine's access to a secret
#### List machine
New machines in Clan come with age keys stored in `./sops/machines/<machine_name>`. To list these machines:
```bash
clan secrets machines list
```
#### Add machine
For clan machines the machine key is generated automatically on demand if none exists.
```bash
clan secrets machines add <machine_name> <age_key>
```
If you already have a device key and want to add it manually, see: [How to obtain a remote key](#obtain-remote-keys-manually)
#### get machine
TODO
#### remove machine
TODO
#### add-secret machine
TODO
#### remove-secret machine
TODO
### Groups (Reference)
The Clan-CLI makes it easy to manage access by allowing you to create groups.
- [list](): list groups
- [add-user](): add a user to group
- [remove-user](): remove a user from group
- [add-machine](): add a machine to group
- [remove-machine](): remove a machine from group
- [add-secret](): allow a user to access a secret
- [remove-secret](): remove a group's access to a secret
#### List Groups
```bash
clan secrets groups list
```
#### add-user
Assign users to a new group, e.g., `admins`:
```bash
clan secrets groups add-user admins <username>
```
!!! info
The group is created if no such group existed before.
The user must exist in beforehand (See: [users](#users-reference))
```{.console, .no-copy}
.
├── flake.nix
. ...
└── sops
├── groups
│ └── admins
│ └── users
│ └── <username> -> ../../../users/<username>
```
#### remove-user
TODO
#### add-machine
TODO
#### remove-machine
TODO
#### add-secret
```bash
clan secrets groups add-secret <group_name> <secret_name>
```
#### remove-secret
TODO
### Key (Reference)
- [generate]() generate age key
- [show]() show age public key
- [update]() re-encrypt all secrets with current keys (useful when changing keys)
#### generate
TODO
#### show
TODO
#### update
TODO
## Further
Secrets in the repository follow this structure:
```{.console, .no-copy}
sops/
├── secrets/
│ └── <secret_name>/
│ ├── secret
│ └── users/
│ └── <your_username>/
```
The content of the secret is stored encrypted inside the `secret` file under `mysecret`.
By default, secrets are encrypted with your key to ensure readability.
### Obtain remote keys manually
To fetch a **SSH host key** from a preinstalled system:
```bash
ssh-keyscan <domain_name> | nix shell nixpkgs#ssh-to-age -c ssh-to-age
```
!!! Success
This command converts the SSH key into an age key on the fly. Since this is the format used by the clan secrets backend.
Once added the **SSH host key** enables seamless integration of existing machines with clan.
Then add the key by executing:
```bash
clan secrets machines add <machine_name> <age_key>
```
See also: [Machine reference](#machines-reference)
### NixOS integration
A NixOS machine will automatically import all secrets that are encrypted for the
current machine. At runtime it will use the host key to decrypt all secrets into
an in-memory, non-persistent filesystem using [sops-nix](https://github.com/Mic92/sops-nix).
In your nixos configuration you can get a path to secrets like this `config.sops.secrets.<name>.path`. For example:
```nix
{ config, ...}: {
sops.secrets.my-password.neededForUsers = true;
users.users.mic92 = {
isNormalUser = true;
passwordFile = config.sops.secrets.my-password.path;
};
}
```
See the [readme](https://github.com/Mic92/sops-nix) of sops-nix for more
examples.
### Migration: Importing existing sops-based keys / sops-nix
`clan secrets` stores each secret in a single file, whereas [sops](https://github.com/Mic92/sops-nix) commonly allows to put all secrets in a yaml or json document.
If you already happened to use sops-nix, you can migrate by using the `clan secrets import-sops` command by importing these files:
```bash
% clan secrets import-sops --prefix matchbox- --group admins --machine matchbox nixos/matchbox/secrets/secrets.yaml
```
This will create secrets for each secret found in `nixos/matchbox/secrets/secrets.yaml` in a `./sops` folder of your repository.
Each member of the group `admins` in this case will be able to decrypt the secrets with their respective key.
Since our clan secret module will auto-import secrets that are encrypted for a particular nixos machine,
you can now remove `sops.secrets.<secrets> = { };` unless you need to specify more options for the secret like owner/group of the secret file.

View File

@ -5,42 +5,28 @@
In the `flake.nix` file:
- [x] set a unique `clanName`.
- [ ] set `clanIcon` (optional)
- [ ] Set `machineIcon` per machine (optional)
=== "**buildClan**"
These icons will be used by our future GUI.
```nix title="clan-core.lib.buildClan"
buildClan {
# Set a unique name
clanName = "Lobsters";
# Should usually point to the directory of flake.nix
directory = ./.;
```nix title="clan-core.lib.buildClan"
buildClan {
# Set a unique name
clanName = "Lobsters";
# Optional, a path to an image file
clanIcon = ./path/to/file;
machines = {
jon = {
# ...
};
machines = {
jon = {
# ...
}
# Optional, a path to an image file
clanCore.machineIcon = ./path/to/file;
};
# ...
}
```
=== "**flakeParts**"
!!! info "See [Clan with flake-parts](./flake-parts.md) for help migrating to flake-parts."
```nix title="clan-core.flakeModules.default"
clan = {
# Set a unique name
clanName = "Lobsters";
machines = {
jon = {
# ...
};
# ...
}
};
```
}
```
## Machine configuration
@ -51,15 +37,12 @@ Adding or configuring a new machine requires two simple steps:
1. Find the remote disk id by executing:
```bash title="setup computer"
ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
ssh root@<target-computer> lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
```
!!! Note
Replace `flash-installer.local` with the IP address of the machine if you don't have the avahi service running which resolves mDNS local domains.
Which should show something like:
```{.shellSession hl_lines="6" .no-copy}
```bash
NAME ID-LINK FSTYPE SIZE MOUNTPOINT
sda usb-ST_16GB_AA6271026J1000000509-0:0 14.9G
├─sda1 usb-ST_16GB_AA6271026J1000000509-0:0-part1 1M
@ -73,115 +56,41 @@ Adding or configuring a new machine requires two simple steps:
1. Edit the following fields inside the `flake.nix`
=== "**buildClan**"
```nix title="clan-core.lib.buildClan" hl_lines="18 23"
buildClan {
```nix title="clan-core.lib.buildClan"
buildClan {
# ...
machines = {
"jon" = {
# ...
machines = {
"jon" = {
imports = [
# ...
./modules/disko.nix
./machines/jon/configuration.nix
];
# ...
# Change this to the correct ip-address or hostname
# The hostname is the machine name by default
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon"
# Change this to the correct ip-address or hostname
# The hostname is the machine name by default
clan.networking.targetHost = pkgs.lib.mkDefault "root@<hostname>"
# Change this to the ID-LINK of the desired disk shown by 'lsblk'
clan.diskLayouts.singleDiskExt4 = {
device = "/dev/disk/by-id/__CHANGE_ME__";
}
# Change this to the ID-LINK of the desired disk shown by 'lsblk'
disko.devices.disk.main = {
device = "/dev/disk/by-id/__CHANGE_ME__";
}
# e.g. > cat ~/.ssh/id_ed25519.pub
users.users.root.openssh.authorizedKeys.keys = [
"<YOUR SSH_KEY>"
];
# ...
};
};
}
```
=== "**flakeParts**"
```nix title="clan-core.flakeModules.default" hl_lines="18 23"
clan = {
# ...
machines = {
"jon" = {
imports = [
# ...
./modules/disko.nix
./machines/jon/configuration.nix
];
# ...
# Change this to the correct ip-address or hostname
# The hostname is the machine name by default
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon"
# Change this to the ID-LINK of the desired disk shown by 'lsblk'
disko.devices.disk.main = {
device = "/dev/disk/by-id/__CHANGE_ME__";
}
# e.g. > cat ~/.ssh/id_ed25519.pub
users.users.root.openssh.authorizedKeys.keys = [
"__YOUR_SSH_KEY__"
];
# ...
};
};
};
```
};
}
```
### Step 2. Detect hardware specific drivers
!!! Info "Replace `__CHANGE_ME__` with the appropriate identifier, such as `nvme-eui.e8238fa6bf530001001b448b4aec2929`"
!!! Info "Replace `__YOUR_SSH_KEY__` with your personal key, like `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILoMI0NC5eT9pHlQExrvR5ASV3iW9+BXwhfchq0smXUJ jon@jon-desktop`"
1. Generate a `hardware-configuration.nix` for your target computer
These steps will allow you to update your machine later.
```bash
ssh root@<target-computer> nixos-generate-config --no-filesystems --show-hardware-config > hardware-configuration.nix
```
### Step 2: Detect Drivers
1. Move the generated file to `machines/jon/hardware-configuration.nix`.
Generate the `hardware-configuration.nix` file for your machine by executing the following command:
### Initialize the facts
```bash
ssh root@flash-installer.local nixos-generate-config --no-filesystems --show-hardware-config > machines/jon/hardware-configuration.nix
```
!!! Info
**All facts are automatically initialized.**
This command connects to `flash-installer.local` as `root`, runs `nixos-generate-config` to detect hardware configurations (excluding filesystems), and writes them to `machines/jon/hardware-configuration.nix`.
### Step 3: Custom Disk Formatting
In `./modules/disko.nix`, a simple `ext4` disk partitioning scheme is defined for the Disko module. For more complex disk partitioning setups, refer to the [Disko examples](https://github.com/nix-community/disko/tree/master/example).
### Step 4: Custom Configuration
Modify `./machines/jon/configuration.nix` to personalize the system settings according to your requirements.
### Step 5: Check Configuration
Validate your configuration by running:
```bash
nix flake check
```
This command helps ensure that your system configuration is correct and free from errors.
!!! Note
Integrate this step into your [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) workflow to ensure that only valid Nix configurations are merged into your codebase. This practice helps maintain system stability and reduces integration issues.
---
## Whats next?
- [Secrets & Facts](secrets.md): Setting up secrets with nix-sops
---
If you need additional help see our [facts chapter](./secrets.md)

View File

@ -1,231 +0,0 @@
# Deploy Machine
Integrating a new machine into your Clan environment is an easy yet flexible process, allowing for a straight forward management of multiple NixOS configurations.
We'll walk you through adding a new computer to your Clan.
## Installing a New Machine
Clan CLI, in conjunction with [nixos-anywhere](https://github.com/nix-community/nixos-anywhere), provides a seamless method for installing NixOS on various machines.
This process involves preparing a suitable hardware and disk partitioning configuration and ensuring the target machine is accessible via SSH.
### Step 0. Prerequisites
=== "**Physical Hardware**"
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
- [x] **Machine configuration**: See our basic [configuration guide](./configure.md)
- [x] **Initialized secrets**: See [secrets](secrets.md) for how to initialize your secrets.
- [x] **USB Flash Drive**: See [Clan Installer](installer.md)
!!! Steps
1. Create a NixOS installer image and transfer it to a bootable USB drive as described in the [installer](./installer.md).
2. Boot the target machine and connect it to a network that makes it reachable from your setup computer.
=== "**Remote Machines**"
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
- [x] **Machine configuration**: See our basic [configuration guide](./configure.md)
- [x] **Initialized secrets**: See [secrets](secrets.md) for how to initialize your secrets.
!!! Steps
- Any cloud machine if it is reachable via SSH and supports `kexec`.
### Step 1. Deploy the machine
**Finally deployment time!** Use the following command to build and deploy the image via SSH onto your machine.
=== "**Image Installer**"
This method makes use of the image installers of [nixos-images](https://github.com/nix-community/nixos-images).
See how to prepare the installer for use [here](./installer.md).
The installer will randomly generate a password and local addresses on boot, then run ssh with these preconfigured.
The installer shows it's deployment relevant information in two formats, a text form, as well as a QR code.
This is an example of the booted installer.
```{ .bash .annotate .no-copy .nohighlight}
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ ┌───────────────────────────┐ │
│ │███████████████████████████│ # This is the QR Code (1) │
│ │██ ▄▄▄▄▄ █▀▄█▀█▀▄█ ▄▄▄▄▄ ██│ │
│ │██ █ █ █▀▄▄▄█ ▀█ █ █ ██│ │
│ │██ █▄▄▄█ █▀▄ ▀▄▄▄█ █▄▄▄█ ██│ │
│ │██▄▄▄▄▄▄▄█▄▀ ▀▄▀▄█▄▄▄▄▄▄▄██│ │
│ │███▀▀▀ █▄▄█ ▀▄ ▄▀▄█ ███│ │
│ │██▄██▄▄█▄▄▀▀██▄▀ ▄▄▄ ▄▀█▀██│ │
│ │██ ▄▄▄▄▄ █▄▄▄▄ █ █▄█ █▀ ███│ │
│ │██ █ █ █ █ █ ▄▄▄ ▄▀▀ ██│ │
│ │██ █▄▄▄█ █ ▄ ▄ ▄ ▀█ ▄███│ │
│ │██▄▄▄▄▄▄▄█▄▄▄▄▄▄█▄▄▄▄▄█▄███│ │
│ │███████████████████████████│ │
│ └───────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────────────┐ │
│ │Root password: cheesy-capital-unwell # password (2) │ │
│ │Local network addresses: │ │
│ │enp1s0 UP 192.168.178.169/24 metric 1024 fe80::21e:6ff:fe45:3c92/64 │ │
│ │enp2s0 DOWN │ │
│ │wlan0 DOWN # connect to wlan (3) │ │
│ │Onion address: 6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion │ │
│ │Multicast DNS: nixos-installer.local │ │
│ └─────────────────────────────────────────────────────────────────────────────────┘ │
│ Press 'Ctrl-C' for console access │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
1. This is not an actual QR code, because it is displayed rather poorly on text sites.
This would be the actual content of this specific QR code prettified:
```json
{
"pass": "cheesy-capital-unwell",
"tor": "6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion",
"addrs": [
"2001:9e8:347:ca00:21e:6ff:fe45:3c92"
]
}
```
To generate the actual QR code, that would be displayed use:
```shellSession
echo '{"pass":"cheesy-capital-unwell","tor":"6evxy5yhzytwpnhc2vpscrbti3iktxdhpnf6yim6bbs25p4v6beemzyd.onion","addrs":["2001:9e8:347:ca00:21e:6ff:fe45:3c92"]}' | nix run nixpkgs#qrencode -- -s 2 -m 2 -t utf8
```
2. The root password for the installer medium.
This password is autogenerated and meant to be easily typeable.
3. See how to connect the installer medium to wlan [here](./installer.md#optional-connect-to-wifi).
4. :man_raising_hand: I'm a code annotation! I can contain `code`, __formatted
text__, images, ... basically anything that can be written in Markdown.
!!!tip
For easy sharing of deployment information via QR code, we highly recommend using [KDE Connect](https://apps.kde.org/de/kdeconnect/).
There are two ways to deploy your machine:
1. **SSH with Password Authentication**
Run the following command to install using SSH:
```bash
clan machines install [MACHINE] flash-installer.local
```
2. **Scanning a QR Code for Installation Details**
You can input the information by following one of these methods:
- **Using a JSON String or File Path:**
Provide the path to a JSON string or input the string directly:
```terminal
clan machines install [MACHINE] --json [JSON]
```
- **Using an Image Containing the QR Code:**
Provide the path to an image file containing the relevant QR code:
```terminal
clan machines install [MACHINE] --png [PATH]
```
=== "**SSH access**"
Replace `<target_host>` with the **target computers' ip address**:
```bash
clan machines install [MACHINE] <target_host>
```
If you are using our template `[MACHINE]` would be `jon`
!!! success
Your machine is all set up. 🎉 🚀
## Update Your Machines
Clan CLI enables you to remotely update your machines over SSH. This requires setting up a target address for each target machine.
### Setting the Target Host
Replace `root@jon` with the actual hostname or IP address of your target machine:
```{.nix hl_lines="9" .no-copy}
buildClan {
# ...
machines = {
# "jon" will be the hostname of the machine
"jon" = {
# Set this for clan commands use ssh i.e. `clan machines update`
# If you change the hostname, you need to update this line to root@<new-hostname>
# This only works however if you have avahi running on your admin machine else use IP
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon";
};
};
};
```
!!! warning
The use of `root@` in the target address implies SSH access as the `root` user.
Ensure that the root login is secured and only used when necessary.
### Updating Machine Configurations
Execute the following command to update the specified machine:
```bash
clan machines update jon
```
You can also update all configured machines simultaneously by omitting the machine name:
```bash
clan machines update
```
### Setting a Build Host
If the machine does not have enough resources to run the NixOS evaluation or build itself,
it is also possible to specify a build host instead.
During an update, the cli will ssh into the build host and run `nixos-rebuild` from there.
```{.nix hl_lines="5" .no-copy}
buildClan {
# ...
machines = {
"jon" = {
clan.networking.buildHost = "root@<host_or_ip>";
};
};
};
```
### Excluding a machine from `clan machine update`
To exclude machines from being updated when running `clan machines update` without any machines specified,
one can set the `clan.deployment.requireExplicitUpdate` option to true:
```{.nix hl_lines="5" .no-copy}
buildClan {
# ...
machines = {
"jon" = {
clan.deployment.requireExplicitUpdate = true;
};
};
};
```
This is useful for machines that are not always online or are not part of the regular update cycle.
---
## What's next ?
- [**Mesh VPN**](./mesh-vpn.md): Configuring a secure mesh network.
---

View File

@ -35,7 +35,6 @@ After updating your flake inputs, the next step is to import the `clan-core` fla
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } (
{
#
imports = [
inputs.clan-core.flakeModules.default
];
@ -50,48 +49,45 @@ Configure your clan settings and define machine configurations.
Below is a guide on how to structure this in your flake.nix:
```nix
outputs = inputs@{ flake-parts, clan-core, ... }:
flake-parts.lib.mkFlake { inherit inputs; } ({self, pkgs, ...}: {
# We define our own systems below. you can still use this to add system specific outputs to your flake.
# See: https://flake.parts/getting-started
systems = [];
outputs =
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } (
{
imports = [
inputs.clan-core.flakeModules.default
];
clan = {
## Clan wide settings. (Required)
clanName = "__CHANGE_ME__"; # Ensure to choose a unique name.
directory = self; # Point this to the repository root.
# import clan-core modules
imports = [
clan-core.flakeModules.default
];
# Define your clan
clan = {
# Clan wide settings. (Required)
clanName = ""; # Ensure to choose a unique name.
machines = {
jon = {
imports = [
./machines/jon/configuration.nix
./modules/disko.nix
# ... more modules
];
nixpkgs.hostPlatform = "x86_64-linux";
# Set this for clan commands use ssh i.e. `clan machines update`
clan.networking.targetHost = pkgs.lib.mkDefault "root@jon";
# remote> lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
disko.devices.disk.main = {
device = "/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b448b4aec2929";
specialArgs = { }; # Add arguments to every nix import in here
machines = {
jons-desktop = {
nixpkgs.hostPlatform = "x86_64-linux";
imports = [
clan-core.clanModules.sshd # Add openssh server for Clan management
./configuration.nix
];
};
# There needs to be exactly one controller per clan
clan.networking.zerotier.controller.enable = true;
};
};
};
});
}
);
```
For detailed information about configuring `flake-parts` and the available options within Clan,
refer to the Clan module documentation located [here](https://git.clan.lol/clan/clan-core/src/branch/main/flakeModules/clan.nix).
### Next Steps
With your flake created, explore how to add new machines by reviewing the documentation provided [here](machines.md).
---
## TODO
* How do I use Clan machines install to setup my current machine?
* I probably need the clan-core sshd module for that?
* We need to tell them that configuration.nix of a machine NEEDS to be under the directory CLAN_ROOT/machines/<machine-name> I think?

View File

@ -1,91 +1,57 @@
# Installer
Our installer image simplifies the process of performing remote installations.
We offer a dedicated installer to assist remote installations.
Follow our step-by-step guide to create and transfer this image onto a bootable USB drive.
In this tutorial we will guide you through building and flashing it to a bootable USB drive.
!!! info
If you already have a NixOS machine you can ssh into (in the cloud for example) you can skip this chapter and go directly to [Configure Machines](configure.md).
### Step 0. Prerequisites
## Step 0. Prerequisites
- [x] A free USB Drive with at least 1.5GB (All data on it will be lost)
- [x] Linux/NixOS Machine with Internet
### Step 1. Identify the USB Flash Drive
## Step 1. Identify the USB Flash Drive
1. Insert your USB flash drive into your computer.
2. Identify your flash drive with `lsblk`:
1. Identify your flash drive with `lsblk`:
```shellSession
lsblk
```
```bash
$ lsblk
```
```{.shellSession hl_lines="2" .no-copy}
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb 8:0 1 117,2G 0 disk
└─sdb1 8:1 1 117,2G 0 part /run/media/qubasa/INTENSO
nvme0n1 259:0 0 1,8T 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot
└─nvme0n1p2 259:2 0 1,8T 0 part
└─luks-f7600028-9d83-4967-84bc-dd2f498bc486 254:0 0 1,8T 0 crypt /nix/store
```
```{.console, .no-copy}
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb 8:0 1 117,2G 0 disk
└─sdb1 8:1 1 117,2G 0 part /run/media/qubasa/INTENSO
nvme0n1 259:0 0 1,8T 0 disk
├─nvme0n1p1 259:1 0 512M 0 part /boot
└─nvme0n1p2 259:2 0 1,8T 0 part
└─luks-f7600028-9d83-4967-84bc-dd2f498bc486 254:0 0 1,8T 0 crypt /nix/store /
```
!!! Info "In this case the USB device is `sdb`"
In this case it is `sdb`.
3. Ensure all partitions on the drive are unmounted. Replace `sdb1` in the command below with your device identifier (like `sdc1`, etc.):
```shellSession
```bash
sudo umount /dev/sdb1
```
=== "**Linux OS**"
### Step 2. Flash Custom Installer
Using clan flash enables the inclusion of ssh public keys.
It also allows to set language and keymap currently in the installer image.
### Step 2. Build the Installer
```bash
clan flash --flake git+https://git.clan.lol/clan/clan-core \
--ssh-pubkey $HOME/.ssh/id_ed25519.pub \
--keymap en \
--language en \
--disk main /dev/sd<X> \
flash-installer
```
```bash
nix build git+https://git.clan.lol/clan/clan-core.git#install-iso
```
The `--ssh-pubkey`, `--language` and `--keymap` are optional.
Replace `$HOME/.ssh/id_ed25519.pub` with a path to your SSH public key.
If you do not have an ssh key yet, you can generate one with `ssh-keygen -t ed25519` command.
### Step 3. Flash the Installer to the USB Drive
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss."
Use the `dd` utility to write the NixOS installer image to your USB drive:
The `clan flash` utility will erase the disk. Make sure to specify the correct device
```bash
sudo dd bs=4M conv=fsync oflag=direct status=progress if=./result of=/dev/sd<X>
```
=== "**Other OS**"
### Step 2. Download Generic Installer
```shellSession
wget https://github.com/nix-community/nixos-images/releases/download/nixos-unstable/nixos-installer-x86_64-linux.iso
```
### Step 3. Flash the Installer to the USB Drive
!!! Danger "Specifying the wrong device can lead to unrecoverable data loss."
The `dd` utility will erase the disk. Make sure to specify the correct device (`of=...`)
For example if the USB device is `sdb` use `of=/dev/sdb`.
Use the `dd` utility to write the NixOS installer image to your USB drive:
```shellSession
sudo dd bs=4M conv=fsync oflag=direct status=progress if=./nixos-installer-x86_64-linux.iso of=/dev/sd<X>
```
If your USB device is `sdb` use `of=/dev/sdb`.
### Step 4. Boot and Connect to your network
@ -107,6 +73,8 @@ After writing the installer to the USB drive, use it to boot the target machine.
- **Apple**: Option (Alt) Key (Boot Menu for Mac)
- If your hardware was not listed read the manufacturers instructions how to enter the boot Menu/BIOS Setup.
**During Boot**
Select `NixOS` to boot into the clan installer.
@ -115,12 +83,23 @@ Select `NixOS` to boot into the clan installer.
For deploying your configuration the machine needs to be connected via LAN (recommended).
For connecting via Wifi, please consult the guide below.
## (Optional) Connect to Wifi
---
### Whats next?
- [Configure Machines](configure.md): Customise machine configuration
- [Deploying](machines.md): Deploying a Machine configuration
- [WiFi](#optional-connect-to-wifi): Guide for connecting to Wifi.
---
### (Optional) Connect to Wifi
If you don't have access via LAN the Installer offers support for connecting via Wifi.
```shellSession
```bash
iwctl
```
@ -132,11 +111,10 @@ This will enter `iwd`
Now run the following command to connect to your Wifi:
```{.shellSession .no-copy}
```bash
# Identify your network device.
device list
# Replace 'wlan0' with your wireless device name
# Replace 'wlan0' with your device name
# Find your Wifi SSID.
station wlan0 scan
station wlan0 get-networks
@ -157,17 +135,6 @@ Connected network FRITZ!Box (Your router device)
IPv4 address 192.168.188.50 (Your new local ip)
```
Press ++ctrl+d++ to exit `IWD`.
Press `ctrl-d` to exit `IWD`.
!!! Important
Press ++ctrl+d++ **again** to update the displayed QR code and connection information.
You're all set up
---
## Whats next?
- [Configure Machines](configure.md): Customize machine configuration
---
Press `ctrl-d` **again** to update the displayed QR code and connection information.

View File

@ -0,0 +1,127 @@
# Deploy Machine
Integrating a new machine into your Clan environment is an easy yet flexible process, allowing for a straight forward management of multiple NixOS configurations.
We'll walk you through adding a new computer to your Clan.
## Installing a New Machine
Clan CLI, in conjunction with [nixos-anywhere](https://github.com/nix-community/nixos-anywhere), provides a seamless method for installing NixOS on various machines.
This process involves preparing a suitable hardware and disk partitioning configuration and ensuring the target machine is accessible via SSH.
### Step 0. Prerequisites
=== "**Physical Hardware**"
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
- [x] **Machine configuration**: See our basic [configuration guide](./configure.md)
- [x] **Initialized secrets**: See [secrets](secrets.md) for how to initialize your secrets.
- [x] **USB Flash Drive**: See [Clan Installer](installer.md)
!!! Steps
1. Create a NixOS installer image and transfer it to a bootable USB drive as described in the [installer](./installer.md).
2. Boot the target machine and connect it to a network that makes it reachable from your setup computer.
=== "**Cloud Machines**"
- [x] **Two Computers**: You need one computer that you're getting ready (we'll call this the Target Computer) and another one to set it up from (we'll call this the Setup Computer). Make sure both can talk to each other over the network using SSH.
- [x] **Machine configuration**: See our basic [configuration guide](./configure.md)
- [x] **Initialized secrets**: See [secrets](secrets.md) for how to initialize your secrets.
!!! Steps
- Any cloud machine if it is reachable via SSH and supports `kexec`.
Confirm the machine is reachable via SSH from your setup computer.
```bash
ssh root@<your_target_machine_ip>
```
### Step 1. Deploy the machine
**Finally deployment time!** Use the following command to build and deploy the image via SSH onto your machine.
Replace `<target_host>` with the **target computers' ip address**:
```bash
clan machines install my-machine <target_host>
```
> Note: This may take a while for building and for the file transfer.
!!! success
Your machine is all set up. 🎉 🚀
---
## What's next ?
- [**Update a Machine**](#update-your-machines): Learn how to update an existing machine?
Coming Soon:
- **Join Your Machines in a Private Network:**: Stay tuned for steps on linking all your machines into a secure mesh network with Clan.
---
## Update Your Machines
Clan CLI enables you to remotely update your machines over SSH. This requires setting up a target address for each target machine.
### Setting the Target Host
Replace `host_or_ip` with the actual hostname or IP address of your target machine:
```bash
clan config --machine my-machine clan.networking.targetHost root@host_or_ip
```
> Note: The use of `root@` in the target address implies SSH access as the `root` user.
> Ensure that the root login is secured and only used when necessary.
### Updating Machine Configurations
Execute the following command to update the specified machine:
```bash
clan machines update my-machine
```
You can also update all configured machines simultaneously by omitting the machine name:
```bash
clan machines update
```
### Setting a Build Host
If the machine does not have enough resources to run the NixOS evaluation or build itself,
it is also possible to specify a build host instead.
During an update, the cli will ssh into the build host and run `nixos-rebuild` from there.
```bash
clan config --machine my-machine clan.networking.buildHost root@host_or_ip
```
### Excluding a machine from `clan machine update`
To exclude machines from being updated when running `clan machines update` without any machines specified,
one can set the `clan.deployment.requireExplicitUpdate` option to true:
```bash
clan config --machine my-machine clan.deployment.requireExplicitUpdate true
```
This is useful for machines that are not always online or are not part of the regular update cycle.
---
# TODO:
* TODO: How to join others people zerotier
* `services.zerotier.joinNetworks = [ "network-id" ]`
* Controller needs to approve over webinterface or cli

View File

@ -1,97 +0,0 @@
# Mesh VPN
This guide provides detailed instructions for configuring
[ZeroTier VPN](https://zerotier.com) within Clan. Follow the
outlined steps to set up a machine as a VPN controller (`<CONTROLLER>`) and to
include a new machine into the VPN.
## Concept
By default all machines within one clan are connected via a chosen network technology.
```{.no-copy}
Clan
Node A
<-> (zerotier / mycelium / ...)
Node B
```
If you select multiple network technologies at the same time. e.g. (zerotier + yggdrassil)
You must choose one of them as primary network and the machines are always connected via the primary network.
## 1. Set-Up the VPN Controller
The VPN controller is initially essential for providing configuration to new
peers. Once addresses are allocated, the controller's continuous operation is not essential.
1. **Designate a Machine**: Label a machine as the VPN controller in the clan,
referred to as `<CONTROLLER>` henceforth in this guide.
2. **Add Configuration**: Input the following configuration to the NixOS
configuration of the controller machine:
```nix
clan.networking.zerotier.controller = {
enable = true;
public = true;
};
```
3. **Update the Controller Machine**: Execute the following:
```bash
clan machines update <CONTROLLER>
```
Your machine is now operational as the VPN controller.
## 2. Add Machines to the VPN
To introduce a new machine to the VPN, adhere to the following steps:
1. **Update Configuration**: On the new machine, incorporate the following to its
configuration, substituting `<CONTROLLER>` with the controller machine name:
```nix
{ config, ... }: {
clan.networking.zerotier.networkId = builtins.readFile (config.clanCore.clanDir + "/machines/<CONTROLLER>/facts/zerotier-network-id");
}
```
1. **Update the New Machine**: Execute:
```bash
$ clan machines update <NEW_MACHINE>
```
Replace `<NEW_MACHINE>` with the designated new machine name.
!!! Note "For Private Networks"
1. **Retrieve the ZeroTier ID**: On the `new_machine`, execute:
```bash
$ sudo zerotier-cli info
```
Example Output:
```{.console, .no-copy}
200 info d2c71971db 1.12.1 OFFLINE
```
, where `d2c71971db` is the ZeroTier ID.
2. **Authorize the New Machine on the Controller**: On the controller machine,
execute:
```bash
$ sudo zerotier-members allow <ID>
```
Substitute `<ID>` with the ZeroTier ID obtained previously.
2. **Verify Connection**: On the `new_machine`, re-execute:
```bash
$ sudo zerotier-cli info
```
The status should now be "ONLINE":
```{.console, .no-copy}
200 info d2c71971db 1.12.1 ONLINE
```
!!! success "Congratulations!"
The new machine is now part of the VPN, and the ZeroTier
configuration on NixOS within the Clan project is complete.
## Further
Currently you can only use **Zerotier** as networking technology because this is the first network stack we aim to support.
In the future we plan to add additional network technologies like tinc, head/tailscale, yggdrassil and mycelium.
We chose zerotier because in our tests it was a straight forwards solution to bootstrap.
It allows you to selfhost a controller and the controller doesn't need to be globally reachable.
Which made it a good fit for starting the project.

View File

@ -0,0 +1,76 @@
# Overlay Networks
This guide provides detailed instructions for configuring
[ZeroTier VPN](https://zerotier.com) within Clan. Follow the
outlined steps to set up a machine as a VPN controller (`<CONTROLLER>`) and to
include a new machine into the VPN.
## 1. Setting Up the VPN Controller
The VPN controller is initially essential for providing configuration to new
peers. Once addresses are allocated, the controller's continuous operation is not essential.
### Instructions:
1. **Designate a Machine**: Label a machine as the VPN controller in the clan,
referred to as `<CONTROLLER>` henceforth in this guide.
1. **Add Configuration**: Input the following configuration to the NixOS
configuration of the controller machine:
```nix
clan.networking.zerotier.controller = {
enable = true;
public = true;
};
```
1. **Update the Controller Machine**: Execute the following:
```bash
$ clan machines update <CONTROLLER>
```
Your machine is now operational as the VPN controller.
## 2. Integrating a New Machine to the VPN
To introduce a new machine to the VPN, adhere to the following steps:
### Instructions:
1. **Update Configuration**: On the new machine, incorporate the following to its
configuration, substituting `<CONTROLLER>` with the controller machine name:
```nix
{ config, ... }: {
clan.networking.zerotier.networkId = builtins.readFile (config.clanCore.clanDir + "/machines/<CONTROLLER>/facts/zerotier-network-id");
}
```
1. **Update the New Machine**: Execute:
```bash
$ clan machines update <NEW_MACHINE>
```
Replace `<NEW_MACHINE>` with the designated new machine name.
1. **Retrieve the ZeroTier ID**: On the `new_machine`, execute:
```bash
$ sudo zerotier-cli info
```
Example Output:
```{.console, .no-copy}
200 info d2c71971db 1.12.1 OFFLINE
```
, where `d2c71971db` is the ZeroTier ID.
1. **Authorize the New Machine on the Controller**: On the controller machine,
execute:
```bash
$ sudo zerotier-members allow <ID>
```
Substitute `<ID>` with the ZeroTier ID obtained previously.
1. **Verify Connection**: On the `new_machine`, re-execute:
```bash
$ sudo zerotier-cli info
```
The status should now be "ONLINE":
```{.console, .no-copy}
200 info d2c71971db 1.12.1 ONLINE
```
!!! success "Congratulations!"
The new machine is now part of the VPN, and the ZeroTier
configuration on NixOS within the Clan project is complete.

View File

@ -4,15 +4,13 @@ Clan enables encryption of secrets (such as passwords & keys) ensuring security
Clan utilizes the [sops](https://github.com/getsops/sops) format and integrates with [sops-nix](https://github.com/Mic92/sops-nix) on NixOS machines.
This guide will walk you through:
This documentation will guide you through managing secrets with the Clan CLI
- **Creating a Keypair for Your User**: Learn how to generate a keypair for $USER to securely control all secrets.
- **Creating Your First Secret**: Step-by-step instructions on creating your initial secret.
- **Assigning Machine Access to the Secret**: Understand how to grant a machine access to the newly created secret.
## 1. Initializing Secrets
## Create Your Admin Keypair
### Create Your Master Keypair
To get started, you'll need to create **Your admin keypair**.
To get started, you'll need to create **Your master keypair**.
!!! info
Don't worry — if you've already made one before, this step won't change or overwrite it.
@ -30,20 +28,21 @@ Generated age private key at '/home/joerg/.config/sops/age/keys.txt' for your us
Also add your age public key to the repository with 'clan secrets users add YOUR_USER age1wkth7uhpkl555g40t8hjsysr20drq286netu8zptw50lmqz7j95sw2t3l7' (replace YOUR_USER with your actual username)
```
!!! warning
!!! warning
Make sure to keep a safe backup of the private key you've just created.
If it's lost, you won't be able to get to your secrets anymore because they all need the admin key to be unlocked.
If it's lost, you won't be able to get to your secrets anymore because they all need the master key to be unlocked.
!!! note
!!! note
It's safe to add any secrets created by the clan CLI and placed in your repository to version control systems like `git`.
### Add Your Public Key
```bash
clan secrets users add $USER <your_public_key>
clan secrets users add <your_username> <your_public_key>
```
It's best to choose the same username as on your Setup/Admin Machine that you use to control the deployment with.
!!! note
Choose the same username as on your Setup/Source Machine that you use to control the deployment with.
Once run this will create the following files:
@ -53,39 +52,112 @@ sops/
└── <your_username>/
└── key.json
```
If you followed the quickstart tutorial all necessary secrets are initialized at this point.
---
## 2. Adding Machine Keys
## Whats next?
- [Deployment](deploy.md): How to remotely deploy your machine
---
## More on Secrets
If you want to know more about how to save and share passwords in your clan read further!
### Adding a Secret
```shellSession
clan secrets set mysecret
Paste your secret:
```
### Retrieving a Stored Secret
New machines in Clan come with age keys stored in `./sops/machines/<machine_name>`. To list these machines:
```bash
clan secrets get mysecret
$ clan secrets machines list
```
For existing machines, add their keys:
```bash
$ clan secrets machines add <machine_name> <age_key>
```
### Advanced
To fetch an age key from an SSH host key:
```bash
$ ssh-keyscan <domain_name> | nix shell nixpkgs#ssh-to-age -c ssh-to-age
```
## 3. Assigning Access
By default, secrets are encrypted for your key. To specify which users and machines can access a secret:
```bash
$ clan secrets set --machine <machine1> --machine <machine2> --user <user1> --user <user2> <secret_name>
```
You can add machines/users to existing secrets without modifying the secret:
```bash
$ clan secrets machines add-secret <machine_name> <secret_name>
```
## 4. Adding Secrets
```bash
$ clan secrets set mysecret
Paste your secret:
```
!!! note
As you type your secret won't be displayed. Press Enter to save the secret.
## 5. Retrieving Stored Secrets
```bash
$ clan secrets get mysecret
```
### List all Secrets
```bash
clan secrets list
$ clan secrets list
```
## 6. Groups
Clan CLI makes it easy to manage access by allowing you to create groups.
All users within a group inherit access to all secrets of the group.
This feature eases the process of handling permissions for multiple users.
Here's how to get started:
1. **Creating Groups**:
Assign users to a new group, e.g., `admins`:
```bash
$ clan secrets groups add admins <username>
```
2. **Listing Groups**:
```bash
$ clan secrets groups list
```
3. **Assigning Secrets to Groups**:
```bash
$ clan secrets groups add-secret <group_name> <secret_name>
```
## Further
Secrets in the repository follow this structure:
```{.console, .no-copy}
sops/
├── secrets/
│ └── <secret_name>/
│ ├── secret
│ └── users/
│ └── <your_username>/
```
The content of the secret is stored encrypted inside the `secret` file under `mysecret`.
By default, secrets are encrypted with your key to ensure readability.
### NixOS integration
A NixOS machine will automatically import all secrets that are encrypted for the
@ -104,75 +176,8 @@ In your nixos configuration you can get a path to secrets like this `config.sops
}
```
### Assigning Access
When using `clan secrets set <secret>` without arguments, secrets are encrypted for the key of the user named like your current $USER.
To add machines/users to an existing secret use:
```bash
clan secrets machines add-secret <machine_name> <secret_name>
```
Alternatively specify users and machines while creating a secret:
```bash
clan secrets set --machine <machine1> --machine <machine2> --user <user1> --user <user2> <secret_name>
```
## Advanced
In this section we go into more advanced secret management topics.
### Groups
Clan CLI makes it easy to manage access by allowing you to create groups.
All users within a group inherit access to all secrets of the group.
This feature eases the process of handling permissions for multiple users.
Here's how to get started:
1. **Creating Groups**:
Assign users to a new group, e.g., `admins`:
```bash
clan secrets groups add admins <username>
```
2. **Listing Groups**:
```bash
clan secrets groups list
```
3. **Assigning Secrets to Groups**:
```bash
clan secrets groups add-secret <group_name> <secret_name>
```
### Adding Machine Keys
New machines in Clan come with age keys stored in `./sops/machines/<machine_name>`. To list these machines:
```bash
clan secrets machines list
```
For existing machines, add their keys:
```bash
clan secrets machines add <machine_name> <age_key>
```
To fetch an age key from an SSH host key:
```bash
ssh-keyscan <domain_name> | nix shell nixpkgs#ssh-to-age -c ssh-to-age
```
See the [readme](https://github.com/Mic92/sops-nix) of sops-nix for more
examples.
### Migration: Importing existing sops-based keys / sops-nix
@ -189,136 +194,3 @@ Each member of the group `admins` in this case will be able to decrypt the secre
Since our clan secret module will auto-import secrets that are encrypted for a particular nixos machine,
you can now remove `sops.secrets.<secrets> = { };` unless you need to specify more options for the secret like owner/group of the secret file.
## Indepth Explanation
The secrets system conceptually knows two different entities:
- **Machine**: consumes secrets
- **User**: manages access to secrets
**A Users** Can add or revoke machines' access to secrets.
**A machine** Can decrypt secrets that where encrypted specifically for that machine.
!!! Danger
**Always make sure at least one _User_ has access to a secret**. Otherwise you could lock yourself out from accessing the secret.
### Inherited implications
By default clan uses [sops](https://github.com/getsops/sops) through [sops-nix](https://github.com/Mic92/sops-nix) for managing its secrets which inherits some implications that are important to understand:
- **Public/Private keys**: Entities are identified via their public keys. Each Entity can use their respective private key to decrypt a secret.
- **Public keys are stored**: All Public keys are stored inside the repository
- **Secrets are stored Encrypted**: secrets are stored inside the repository encrypted with the respective public keys
- **Secrets are deployed encrypted**: Fully encrypted secrets are deployed to machines at deployment time.
- **Secrets are decrypted by sops on-demand**: Each machine decrypts its secrets at runtime and stores them at an ephemeral location.
- **Machine key-pairs are auto-generated**: When a machine is created **no user-interaction is required** to setup public/private key-pairs.
- **secrets are re-encrypted**: In case machines, users or groups are modified secrets get re-encrypted on demand.
!!! Important
After revoking access to a secret you should also change the underlying secret. i.e. change the API key, or the password.
---
### Machine and user keys
The following diagrams illustrates how a user can provide a secret (i.e. a Password).
- By using the **Clan CLI** a user encrypts the password with both the **User public-key** and the **machine's public-key**
- The *Machine* can decrypt the password with its private-key on demand.
- The *User* is able to decrypt the password to make changes to it.
```plantuml
@startuml
!include C4_Container.puml
Person(user, "User", "Someone who manages secrets")
ContainerDb(secret, "Secret")
Container(machine, "Machine", "A Machine. i.e. Needs the Secret for a given Service." )
Rel_R(user, secret, "Encrypt", "", "Pubkeys: User, Machine")
Rel_L(secret, user, "Decrypt", "", "user privkey")
Rel_R(secret, machine, "Decrypt", "", "machine privkey" )
@enduml
```
#### User groups
Here we illustrate how machine groups work.
Common use cases:
- **Shared Management**: Access among multiple users. I.e. a subset of secrets/machines that have two admins
```plantuml
@startuml
!include C4_Container.puml
System_Boundary(c1, "Group") {
Person(user1, "User A", "has access")
Person(user2, "User B", "has access")
}
ContainerDb(secret, "Secret")
Container(machine, "Machine", "A Machine. i.e. Needs the Secret for a given Service." )
Rel_R(c1, secret, "Encrypt", "", "Pubkeys: User A, User B, Machine")
Rel_R(secret, machine, "Decrypt", "", "machine privkey" )
@enduml
```
<!-- TODO: See also [Groups Reference](#groups-reference) -->
---
#### Machine groups
Here we illustrate how machine groups work.
Common use cases:
- **Shared secrets**: Among multiple machines such as Wifi passwords
```plantuml
@startuml
!include C4_Container.puml
!include C4_Deployment.puml
Person(user, "User", "Someone who manages secrets")
ContainerDb(secret, "Secret")
System_Boundary(c1, "Group") {
Container(machine1, "Machine A", "Both machines need the same secret" )
Container(machine2, "Machine B", "Both machines need the same secret" )
}
Rel_R(user, secret, "Encrypt", "", "Pubkeys: machine A, machine B, User")
Rel(secret, c1, "Decrypt", "", "Both machine A or B can decrypt using their private key" )
@enduml
```
<!-- TODO: See also [Groups Reference](#groups-reference) -->
See the [readme](https://github.com/Mic92/sops-nix) of sops-nix for more
examples.
---
## Whats next?
- [Deployment](deploy.md): How to remotely deploy your machine
---

View File

@ -1,31 +1,44 @@
# Setup
# Getting Started
Create your own clan with these initial steps and manage a fleet of machines with one single testable git repository!
Welcome to your simple guide on starting a new Clan project.
## What's Inside
We've put together a straightforward guide to help you out:
- [**Starting with a New Clan Project**](#starting-with-a-new-clan-project): Create a new Clan from scratch.
- [**Integrating Clan using Flake-Parts**](getting-started/flake-parts.md)
---
## **Starting with a New Clan Project**
Create your own clan with these initial steps.
### Prerequisites
=== "**Linux**"
#### Linux
Clan depends on nix installed on your system. Run the following command to install nix.
Clan depends on nix installed on your system. Run the following command to install nix.
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```
```bash
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```
=== "**NixOS**"
#### NixOS
If you run NixOS the `nix` binary is already installed.
If you run NixOS the `nix` binary is already installed.
You will also need to enable the `flakes` and `nix-commands` experimental features.
You will also need to enable the `flakes` and `nix-commands` experimental features.
```bash
# /etc/nix/nix.conf or ~/.config/nix/nix.conf
experimental-features = nix-command flakes
```
```bash
# /etc/nix/nix.conf or ~/.config/nix/nix.conf
experimental-features = nix-command flakes
```
=== "**Other**"
#### Other
Clan doesn't offer dedicated support for other operating systems yet.
Clan doesn't offer dedicated support for other operating systems yet.
### Step 1: Add Clan CLI to Your Shell
@ -35,13 +48,6 @@ Add the Clan CLI into your development workflow:
nix shell git+https://git.clan.lol/clan/clan-core#clan-cli
```
You can find reference documentation for the `clan` cli program [here](./reference/cli/index.md).
Alternatively you can check out the help pages directly:
```terminalSession
clan --help
```
### Step 2: Initialize Your Project
Set the foundation of your Clan project by initializing it as follows:
@ -51,14 +57,12 @@ clan flakes create my-clan
```
This command creates the `flake.nix` and `.clan-flake` files for your project.
It will also generate files from a default template, to help show general clan usage patterns.
### Step 3: Verify the Project Structure
Ensure that all project files exist by running:
```bash
cd my-clan
tree
```
@ -97,6 +101,12 @@ sara
### What's Next?
- [**Machine Configuration**](getting-started/configure.md): Declare behavior and configuration of machines.
- [**Deploy Machines**](getting-started/machines.md): Learn how to deploy to any remote machine.
- [**Installer**](getting-started/installer.md): Setting up new computers remotely is easy with an USB stick.
- [**Check out our Templates**](templates/index.md)
---

View File

@ -1 +0,0 @@
/nix/store/8y5h98wk5p94mv1wyb2c4gkrr7bswd19-asciinema-player.css

View File

@ -1 +0,0 @@
/nix/store/w0i3f9qzn9n6jmfnfgiw5wnab2f9ssdw-asciinema-player.min.js

View File

@ -1,13 +0,0 @@
@font-face {
font-family: "Roboto";
src: url(./Roboto-Regular.ttf) format('truetype');
}
@font-face {
font-family: "Fira Code";
src: url(./FiraCode-VF.ttf) format('truetype');
}
:root {
--md-text-font: "Roboto";
--md-code-font: "Fira Code";
}

BIN
docs/site/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,24 @@
# Templates
We provide some starting templates you can easily use one of those via `nix flakes`.
They showcase best practices and guide you through setting up and using Clan's modules
I.e. To use the `new-clan` template run the following command:
```bash
nix flake init -t git+https://git.clan.lol/clan/clan-core#new-clan
```
## Available Templates
We offer the following templates:
To initialize a clan with one of those run:
```bash
nix flake init -t git+https://git.clan.lol/clan/clan-core#[TEMPLATE_NAME]
```
Substitute `[TEMPLATE_NAME]` with the name of the template.
- **new-clan**: Perfect for beginners, this template shows you how to link two machines in a basic setup.

View File

@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1717177033,
"narHash": "sha256-G3CZJafCO8WDy3dyA2EhpUJEmzd5gMJ2IdItAg0Hijw=",
"lastModified": 1712356478,
"narHash": "sha256-kTcEtrQIRnexu5lAbLsmUcfR2CrmsACF1s3ZFw1NEVA=",
"owner": "nix-community",
"repo": "disko",
"rev": "0274af4c92531ebfba4a5bd493251a143bc51f3c",
"rev": "0a17298c0d96190ef3be729d594ba202b9c53beb",
"type": "github"
},
"original": {
@ -27,11 +27,11 @@
]
},
"locked": {
"lastModified": 1717285511,
"narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
@ -42,11 +42,11 @@
},
"nixlib": {
"locked": {
"lastModified": 1712450863,
"narHash": "sha256-K6IkdtMtq9xktmYPj0uaYc8NsIqHuaAoRBaMgu9Fvrw=",
"lastModified": 1711846064,
"narHash": "sha256-cqfX0QJNEnge3a77VnytM0Q6QZZ0DziFXt6tSCV8ZSc=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "3c62b6a12571c9a7f65ab037173ee153d539905f",
"rev": "90b1a963ff84dc532db92f678296ff2499a60a87",
"type": "github"
},
"original": {
@ -55,22 +55,6 @@
"type": "github"
}
},
"nixos-2311": {
"locked": {
"lastModified": 1717017538,
"narHash": "sha256-S5kltvDDfNQM3xx9XcvzKEOyN2qk8Sa+aSOLqZ+1Ujc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "64e468fd2652105710d86cd2ae3e65a5a6d58dec",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixos-generators": {
"inputs": {
"nixlib": "nixlib",
@ -79,11 +63,11 @@
]
},
"locked": {
"lastModified": 1716210724,
"narHash": "sha256-iqQa3omRcHGpWb1ds75jS9ruA5R39FTmAkeR3J+ve1w=",
"lastModified": 1712191720,
"narHash": "sha256-xXtSSnVHURHsxLQO30dzCKW5NJVGV/umdQPmFjPFMVA=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "d14b286322c7f4f897ca4b1726ce38cb68596c94",
"rev": "0c15e76bed5432d7775a22e8d22059511f59d23a",
"type": "github"
},
"original": {
@ -92,34 +76,13 @@
"type": "github"
}
},
"nixos-images": {
"inputs": {
"nixos-2311": "nixos-2311",
"nixos-unstable": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1717040312,
"narHash": "sha256-yI/en4IxuCEClIUpIs3QTyYCCtmSPLOhwLJclfNwdeg=",
"owner": "nix-community",
"repo": "nixos-images",
"rev": "47bfb55316e105390dd761e0b6e8e0be09462b67",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-images",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1717298511,
"narHash": "sha256-9sXuJn/nL+9ImeYtlspTvjt83z1wIgU+9AwfNbnq+tI=",
"lastModified": 1712468661,
"narHash": "sha256-n2gVVBs+rV+HzPv/N3QQv5cdAXqSkjmaObvfeMqnw2c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6634a0509e9e81e980b129435fbbec518ab246d0",
"rev": "298edc8f1e0dfffce67f50375c9f5952e04a6d02",
"type": "github"
},
"original": {
@ -134,7 +97,6 @@
"disko": "disko",
"flake-parts": "flake-parts",
"nixos-generators": "nixos-generators",
"nixos-images": "nixos-images",
"nixpkgs": "nixpkgs",
"sops-nix": "sops-nix",
"treefmt-nix": "treefmt-nix"
@ -148,11 +110,11 @@
"nixpkgs-stable": []
},
"locked": {
"lastModified": 1717297459,
"narHash": "sha256-cZC2f68w5UrJ1f+2NWGV9Gx0dEYmxwomWN2B0lx0QRA=",
"lastModified": 1712458908,
"narHash": "sha256-DMgBS+jNHDg8z3g9GkwqL8xTKXCRQ/0FGsAyrniVonc=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "ab2a43b0d21d1d37d4d5726a892f714eaeb4b075",
"rev": "39191e8e6265b106c9a2ba0cfd3a4dafe98a31c6",
"type": "github"
},
"original": {
@ -168,11 +130,11 @@
]
},
"locked": {
"lastModified": 1717278143,
"narHash": "sha256-u10aDdYrpiGOLoxzY/mJ9llST9yO8Q7K/UlROoNxzDw=",
"lastModified": 1711963903,
"narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3eb96ca1ae9edf792a8e0963cc92fddfa5a87706",
"rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac",
"type": "github"
},
"original": {

View File

@ -8,6 +8,7 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
sops-nix.url = "github:Mic92/sops-nix";
@ -15,8 +16,6 @@
sops-nix.inputs.nixpkgs-stable.follows = "";
nixos-generators.url = "github:nix-community/nixos-generators";
nixos-generators.inputs.nixpkgs.follows = "nixpkgs";
nixos-images.url = "github:nix-community/nixos-images";
nixos-images.inputs.nixos-unstable.follows = "nixpkgs";
flake-parts.url = "github:hercules-ci/flake-parts";
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
treefmt-nix.url = "github:numtide/treefmt-nix";
@ -24,14 +23,10 @@
};
outputs =
inputs@{ flake-parts, self, ... }:
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } (
{ ... }:
{
clan = {
# meta.name = "clan-core";
directory = self;
};
systems = [
"x86_64-linux"
"aarch64-linux"

View File

@ -4,7 +4,6 @@ clan-core:
lib,
flake-parts-lib,
inputs,
self,
...
}:
let
@ -17,38 +16,10 @@ let
cfg = config.clan;
in
{
imports = [
# TODO: figure out how to print the deprecation warning
# "${inputs.nixpkgs}/nixos/modules/misc/assertions.nix"
(lib.mkRenamedOptionModule
[
"clan"
"clanName"
]
[
"clan"
"meta"
"name"
]
)
(lib.mkRenamedOptionModule
[
"clan"
"clanIcon"
]
[
"clan"
"meta"
"icon"
]
)
];
options.clan = {
directory = mkOption {
type = types.path;
description = "The directory containing the clan subdirectory";
default = self; # default to the directory of the flake
};
specialArgs = mkOption {
type = types.attrsOf types.raw;
@ -60,27 +31,15 @@ in
default = { };
description = "Allows to include machine-specific modules i.e. machines.\${name} = { ... }";
};
# Checks are performed in 'buildClan'
# Not everyone uses flake-parts
meta = {
name = lib.mkOption {
type = types.nullOr types.str;
default = null;
description = "Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.";
};
icon = mkOption {
type = types.nullOr types.path;
default = null;
description = "A path to an icon to be used for the clan in the GUI";
};
description = mkOption {
type = types.nullOr types.str;
default = null;
description = "A short description of the clan";
};
clanName = mkOption {
type = types.str;
description = "Needs to be (globally) unique, as this determines the folder name where the flake gets downloaded to.";
};
clanIcon = mkOption {
type = types.nullOr types.path;
default = null;
description = "A path to an icon to be used for the clan, should be the same for all machines";
};
pkgsForSystem = mkOption {
type = types.functionTo types.raw;
default = _system: null;
@ -91,8 +50,7 @@ in
clanInternals = lib.mkOption {
type = lib.types.submodule {
options = {
meta = lib.mkOption { type = lib.types.attrsOf lib.types.unspecified; };
all-machines-json = lib.mkOption { type = lib.types.attrsOf lib.types.unspecified; };
all-machines-json = lib.mkOption { type = lib.types.attrsOf lib.types.str; };
machines = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
machinesFunc = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); };
};
@ -105,8 +63,9 @@ in
directory
specialArgs
machines
clanName
clanIcon
pkgsForSystem
meta
;
};
};

Some files were not shown because too many files have changed in this diff Show More