Compare commits

...

39 commits

Author SHA1 Message Date
a82bb393a2
setup: reset lock file, prevents Error: Failed to install provider
error example:

```
Error while installing hashicorp/external v2.3.4: the local package for
registry.opentofu.org/hashicorp/external 2.3.4 doesn't match any of the
checksums previously recorded in the dependency lock file (this might be
because the available checksums are for packages targeting different
platforms); for more information:
https://opentofu.org/docs/language/files/dependency-lock/#checksum-verification
```
2025-04-22 20:13:17 +02:00
457493c3ee
fix nix-hash 2025-04-22 20:13:17 +02:00
0b11397daa
further purge test machines from nixops 2025-04-22 20:13:17 +02:00
0de70fb9d7
rm corresponding nixops deployments 2025-04-22 20:13:17 +02:00
f184fd3944
add unit test: TF validate 2025-04-22 20:13:17 +02:00
2510b68a68
add further comments 2025-04-22 20:13:17 +02:00
cb69cb41b5
consolidate setup in script 2025-04-22 20:13:17 +02:00
da030f8b4f
move info passed to facilitate passing extra config 2025-04-22 20:13:17 +02:00
9a9285eaa0
fix returncode 2025-04-22 20:13:17 +02:00
7ab8d3b44e
better document TF file 2025-04-22 20:13:17 +02:00
d4128d58c7
split out vars 2025-04-22 20:13:17 +02:00
832fa92fc5
retain nix_path for debugging 2025-04-22 20:13:17 +02:00
c156850fd4
ditch nixpkgs link from /run/current-system 2025-04-22 20:13:17 +02:00
3603b431e0
fix key path 2025-04-22 20:13:17 +02:00
73bc0fd599
add more comments 2025-04-22 20:13:17 +02:00
551de1fb51
Revert "update nixpkgs to ditch opentofu patch"
This reverts commit 7bab876d1b.
2025-04-22 20:13:17 +02:00
f7000ba9d8
update nixpkgs to ditch opentofu patch 2025-04-22 20:13:17 +02:00
23e9712a06
rm unused import 2025-04-22 20:13:17 +02:00
bb94ae3e7d
more comments 2025-04-22 20:13:17 +02:00
06b7cc5ca2
ignore generated tf lock 2025-04-22 20:13:17 +02:00
75addc0c0f
reinstate user group, fixes application-tests 2025-04-22 20:13:17 +02:00
a9475bb68d
rm unused file 2025-04-22 20:13:17 +02:00
fad2f46f82
rm some unused code 2025-04-22 20:13:17 +02:00
8b85c15df1
implicit ssh 2025-04-22 20:13:17 +02:00
e4c1a77353
set env var specific to deployed setting 2025-04-22 20:13:17 +02:00
393e92ffe0
Revert "kill git hook"
This reverts commit f7d050a3a6.
2025-04-22 20:13:17 +02:00
585420e589
filter for git-tracked files 2025-04-22 20:13:17 +02:00
eae3ac83c2
rm tf lock 2025-04-22 20:13:17 +02:00
2ab5b12c73
kill git hook 2025-04-22 20:13:17 +02:00
a00c83c9a6
mv env var 2025-04-22 20:13:17 +02:00
bb0f3df982
rm null from tf 2025-04-22 20:13:17 +02:00
46f00968a3
rm unused provider from lock 2025-04-22 20:13:17 +02:00
71bf8c33ec
buttons works deployed 2025-04-22 20:13:17 +02:00
b851cad8fd
fix npins 2025-04-22 20:08:19 +02:00
725520ddd4 don't track symlinked htmx 2025-04-22 16:34:09 +02:00
061314a062 programmatically place generated files in development environment
this allows to add more generated files later as needed without
cluttering the `shellHook`.
2025-04-22 16:24:12 +02:00
78ecd2db6e Change key for Niols (#316)
Rotate in a new SSH key, fediversity-specific (and not password protected).

Reviewed-on: Fediversity/Fediversity#316
Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
Co-committed-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
2025-04-22 11:32:33 +02:00
b645660118 deployment module: set configuration explicitly (#315)
this change is a no-op (it merely indents the option definitions by one,
by setting `config` explicitly) and prepares an addition of option
declarations that would otherwise be lost in the huge diff.

Reviewed-on: Fediversity/Fediversity#315
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
2025-04-22 11:30:33 +02:00
6fcae1c48c moved proxmox architecture doc to meta repo 2025-04-21 13:23:53 +02:00
61 changed files with 1099 additions and 364 deletions

View file

@ -26,3 +26,9 @@ jobs:
steps:
- uses: actions/checkout@v4
- run: cd panel && nix-build -A tests
check-launch:
runs-on: native
steps:
- uses: actions/checkout@v4
- run: cd launch && nix-build -A tests

View file

@ -1,6 +0,0 @@
# Deployment
This repository contains work to generate a full Fediversity deployment from a
minimal configuration. This is different from [`../services/`](../services) that
focuses on one machine, providing a polished and unified interface to different
Fediverse services.

View file

@ -44,138 +44,140 @@ in
{ providers, ... }:
{
providers = { inherit (nixops4.modules.nixops4Provider) local; };
resources =
let
## NOTE: All of these secrets are publicly available in this source file
## and will end up in the Nix store. We don't care as they are only ever
## used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
makeConfigurationResource = resourceModule: config: {
type = providers.local.exec;
imports = [
nixops4-nixos.modules.nixops4Resource.nixos
resourceModule
config = {
providers = { inherit (nixops4.modules.nixops4Provider) local; };
resources =
let
## NOTE: All of these secrets are publicly available in this source file
## and will end up in the Nix store. We don't care as they are only ever
## used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
mastodonS3KeyConfig =
{ pkgs, ... }:
{
## NOTE: With NixOps4, there are several levels and all of them live
## in the NixOS module system:
##
## 1. Each NixOps4 deployment is a module.
## 2. Each NixOps4 resource is a module. This very comment is
## inside an attrset imported as a module in a resource.
## 3. Each NixOps4 'configuration' resource contains an attribute
## 'nixos.module', itself a NixOS configuration module.
nixos.module =
{ ... }:
{
imports = [
config
fediversity
];
};
}
];
};
in
{
garage-configuration = makeConfigurationResource garageConfigurationResource (
{ pkgs, ... }:
mkIf (panelConfig.mastodon.enable || panelConfig.peertube.enable || panelConfig.pixelfed.enable) {
fediversity = {
inherit (panelConfig) domain;
garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; };
peertube = peertubeS3KeyConfig { inherit pkgs; };
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
}
);
mastodon-configuration = makeConfigurationResource mastodonConfigurationResource (
{ pkgs, ... }:
mkIf panelConfig.mastodon.enable {
fediversity = {
inherit (panelConfig) domain;
temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password;
};
mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
enable = true;
};
temp.cores = 1; # FIXME: should come from NixOps4 eventually
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
}
);
peertube-configuration = makeConfigurationResource peertubeConfigurationResource (
{ pkgs, ... }:
mkIf panelConfig.peertube.enable {
fediversity = {
inherit (panelConfig) domain;
temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password;
};
makeConfigurationResource = resourceModule: config: {
type = providers.local.exec;
imports = [
nixops4-nixos.modules.nixops4Resource.nixos
resourceModule
peertube = peertubeS3KeyConfig { inherit pkgs; } // {
enable = true;
## NOTE: Only ever used for testing anyway.
{
## NOTE: With NixOps4, there are several levels and all of them live
## in the NixOS module system:
##
## FIXME: Generate and store in NixOps4's state.
secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24";
};
};
}
);
## 1. Each NixOps4 deployment is a module.
## 2. Each NixOps4 resource is a module. This very comment is
## inside an attrset imported as a module in a resource.
## 3. Each NixOps4 'configuration' resource contains an attribute
## 'nixos.module', itself a NixOS configuration module.
nixos.module =
{ ... }:
{
imports = [
config
fediversity
];
};
}
];
};
pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource (
{ pkgs, ... }:
mkIf panelConfig.pixelfed.enable {
fediversity = {
inherit (panelConfig) domain;
temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password;
};
in
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
enable = true;
{
garage-configuration = makeConfigurationResource garageConfigurationResource (
{ pkgs, ... }:
mkIf (panelConfig.mastodon.enable || panelConfig.peertube.enable || panelConfig.pixelfed.enable) {
fediversity = {
inherit (panelConfig) domain;
garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; };
peertube = peertubeS3KeyConfig { inherit pkgs; };
};
};
}
);
};
}
);
mastodon-configuration = makeConfigurationResource mastodonConfigurationResource (
{ pkgs, ... }:
mkIf panelConfig.mastodon.enable {
fediversity = {
inherit (panelConfig) domain;
temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password;
};
mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
enable = true;
};
temp.cores = 1; # FIXME: should come from NixOps4 eventually
};
}
);
peertube-configuration = makeConfigurationResource peertubeConfigurationResource (
{ pkgs, ... }:
mkIf panelConfig.peertube.enable {
fediversity = {
inherit (panelConfig) domain;
temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password;
};
peertube = peertubeS3KeyConfig { inherit pkgs; } // {
enable = true;
## NOTE: Only ever used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24";
};
};
}
);
pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource (
{ pkgs, ... }:
mkIf panelConfig.pixelfed.enable {
fediversity = {
inherit (panelConfig) domain;
temp.initialUser = {
inherit (panelConfig.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" panelConfig.initialUser.password;
};
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
enable = true;
};
};
}
);
};
};
}

21
flake.lock generated
View file

@ -571,6 +571,26 @@
"type": "github"
}
},
"home-manager_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1743860185,
"narHash": "sha256-TkhfJ+vH+iGxLQL6RJLObMmldAQpysVJ+p1WnnKyIeQ=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "b5e29565131802cc8adee7dccede794226da8614",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"mk-naked-shell": {
"flake": false,
"locked": {
@ -1215,6 +1235,7 @@
"disko": "disko",
"flake-parts": "flake-parts",
"git-hooks": "git-hooks",
"home-manager": "home-manager_2",
"nixops4": "nixops4",
"nixops4-nixos": "nixops4-nixos",
"nixpkgs": "nixpkgs_7"

View file

@ -3,6 +3,8 @@
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
flake-parts.url = "github:hercules-ci/flake-parts";
git-hooks.url = "github:cachix/git-hooks.nix";
home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
agenix.url = "github:ryantm/agenix";
disko.url = "github:nix-community/disko";
@ -43,7 +45,10 @@
pre-commit.settings.hooks =
let
## Add a directory here if pre-commit hooks shouldn't apply to it.
optout = [ "npins" ];
optout = [
"npins"
"launch/.terraform"
];
excludes = map (dir: "^${dir}/") optout;
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
in

Binary file not shown.

View file

@ -2,7 +2,6 @@
let
inherit (lib) mkDefault;
in
{
imports = [

View file

@ -34,6 +34,7 @@ in
imports = [
inputs.agenix.nixosModules.default
inputs.disko.nixosModules.default
inputs.home-manager.nixosModules.home-manager
./options.nix
./nixos
];

9
infra/dev/.terraform.lock.hcl generated Normal file
View file

@ -0,0 +1,9 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/external" {
version = "2.3.4"
hashes = [
"h1:hBFp4dEKKevoZEsMW32ralBqrO7cTxTPYdWPqc4Ff+s=",
]
}

View file

@ -0,0 +1 @@
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"nixos","Source":"../sync-nix","Dir":"../sync-nix"}]}

View file

@ -0,0 +1,3 @@
[
"/nix/store/zbjjm6vgdj1p2v21qshd9np3ajdnhniy-opentofu-1.9.0/libexec/terraform-providers"
]

View file

@ -0,0 +1 @@
/nix/store/zbjjm6vgdj1p2v21qshd9np3ajdnhniy-opentofu-1.9.0/libexec/terraform-providers/registry.opentofu.org/hashicorp/external/2.3.4/linux_amd64

View file

@ -0,0 +1 @@
{"version":4,"terraform_version":"1.9.0","serial":7,"lineage":"c441e8b7-409b-c7ad-85b9-27549f9ee16d","outputs":{},"resources":[{"module":"module.nixos[\"fedipanel\"]","mode":"data","type":"external","name":"hash","provider":"provider[\"registry.opentofu.org/hashicorp/external\"]","instances":[{"schema_version":0,"attributes":{"id":"-","program":["sh","-c","echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""],"query":null,"result":{"hash":"104476c354d745011877f63e653ae58b"},"working_dir":null},"sensitive_attributes":[]}]},{"module":"module.nixos[\"fedipanel\"]","mode":"managed","type":"terraform_data","name":"nixos","provider":"provider[\"terraform.io/builtin/terraform\"]","instances":[{"schema_version":0,"attributes":{"id":"909398aa-e751-1dc8-a61a-7eb33df17006","input":null,"output":null,"triggers_replace":{"value":[{"hash":"104476c354d745011877f63e653ae58b"},"fedi201","{\n # note interpolations here TF ones\n imports = [\n # shared NixOS config\n ./../common/shared.nix\n # FIXME: separate template options by service\n ./options.nix\n # for service `forgejo` import `forgejo.nix`\n ./../../machines/dev/fedi201/fedipanel.nix\n # FIXME: get VM details from TF\n ./../../machines/dev/fedi201\n ];\n}\n",{"terraform":{"domain":"abundos.eu","hostname":"fedi201"}}],"type":["tuple",[["map","string"],"string","string",["map",["object",{"domain":"string","hostname":"string"}]]]]}},"sensitive_attributes":[],"dependencies":["module.nixos.data.external.hash"]}]}],"check_results":null}

View file

@ -0,0 +1 @@
{"version":4,"terraform_version":"1.9.0","serial":5,"lineage":"c441e8b7-409b-c7ad-85b9-27549f9ee16d","outputs":{},"resources":[{"module":"module.nixos[\"fedipanel\"]","mode":"data","type":"external","name":"hash","provider":"provider[\"registry.opentofu.org/hashicorp/external\"]","instances":[{"schema_version":0,"attributes":{"id":"-","program":["sh","-c","echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""],"query":null,"result":{"hash":"c9bc7d95c831fa099e803674e850b509"},"working_dir":null},"sensitive_attributes":[]}]},{"module":"module.nixos[\"fedipanel\"]","mode":"managed","type":"terraform_data","name":"nixos","provider":"provider[\"terraform.io/builtin/terraform\"]","instances":[{"status":"tainted","schema_version":0,"attributes":{"id":"16edc410-f42a-0489-5326-65fa4b53cf31","input":null,"output":null,"triggers_replace":{"value":[{"hash":"c9bc7d95c831fa099e803674e850b509"},"fedi201","{\n # note interpolations here TF ones\n imports = [\n # shared NixOS config\n ./../common/shared.nix\n # FIXME: separate template options by service\n ./options.nix\n # for service `forgejo` import `forgejo.nix`\n ./../../machines/dev/fedi201/fedipanel.nix\n # FIXME: get VM details from TF\n ./../../machines/dev/fedi201\n ];\n}\n",{"terraform":{"domain":"abundos.eu","hostname":"fedi201"}}],"type":["tuple",[["map","string"],"string","string",["map",["object",{"domain":"string","hostname":"string"}]]]]}},"sensitive_attributes":[],"dependencies":["module.nixos.data.external.hash"]}]}],"check_results":null}

View file

@ -1,12 +1,11 @@
{
self,
inputs,
lib,
...
}:
let
inherit (builtins) readDir readFile fromJSON;
inherit (builtins) readDir;
inherit (lib)
attrNames
mkOption
@ -15,33 +14,17 @@ let
;
inherit (lib.attrsets) genAttrs;
## Given a machine's name and whether it is a test VM, make a resource module,
## Given a machine's name, make a resource module,
## except for its missing provider. (Depending on the use of that resource, we
## will provide a different one.)
makeResourceModule =
{ vmName, isTestVm }:
{ vmName }:
{
_module.args = { inherit inputs; };
imports =
[
./common/resource.nix
]
++ (
if isTestVm then
[
./test-machines/${vmName}
{
nixos.module.users.users.root.openssh.authorizedKeys.keys = [
# allow our panel vm access to the test machines
(import ../keys).panel
];
}
]
else
[
./machines/${vmName}
]
);
imports = [
./common/resource.nix
./machines/${vmName}
];
fediversityVm.name = vmName;
};
@ -58,42 +41,12 @@ let
inputs.nixops4-nixos.modules.nixops4Resource.nixos
(makeResourceModule {
inherit vmName;
isTestVm = false;
})
];
});
};
makeDeployment' = vmName: makeDeployment [ vmName ];
## Given an attrset of test configurations (key = test machine name, value =
## NixOS configuration module), make a deployment with those machines'
## configurations as resources.
makeTestDeployment =
(import ../deployment)
{
inherit lib;
inherit (inputs) nixops4 nixops4-nixos;
inherit (self.nixosModules) fediversity;
}
{
garageConfigurationResource = makeResourceModule {
vmName = "test01";
isTestVm = true;
};
mastodonConfigurationResource = makeResourceModule {
vmName = "test06"; # somehow `test02` has a problem - use test06 instead
isTestVm = true;
};
peertubeConfigurationResource = makeResourceModule {
vmName = "test05";
isTestVm = true;
};
pixelfedConfigurationResource = makeResourceModule {
vmName = "test04";
isTestVm = true;
};
};
nixops4ResourceNixosMockOptions = {
## NOTE: We allow the use of a few options from
## `inputs.nixops4-nixos.modules.nixops4Resource.nixos` such that we can
@ -120,15 +73,15 @@ let
## Given a VM name, make a NixOS configuration for this machine.
makeConfiguration =
isTestVm: vmName:
vmName:
inputs.nixpkgs.lib.nixosSystem {
modules = [
(makeResourceConfig { inherit vmName isTestVm; }).nixos.module
(makeResourceConfig { inherit vmName; }).nixos.module
];
};
makeVmOptions = isTestVm: vmName: {
inherit ((makeResourceConfig { inherit vmName isTestVm; }).fediversityVm)
makeVmOptions = vmName: {
inherit ((makeResourceConfig { inherit vmName; }).fediversityVm)
proxmox
vmId
description
@ -146,35 +99,18 @@ let
listSubdirectories = path: attrNames (filterAttrs (_: type: type == "directory") (readDir path));
machines = listSubdirectories ./machines;
testMachines = listSubdirectories ./test-machines;
in
{
flake.lib.makeInstallerIso = import ./makeInstallerIso.nix;
## - Each normal or test machine gets a NixOS configuration.
## - Each normal or test machine gets a VM options entry.
## - Each normal machine gets a deployment.
## - We add a “default” deployment with all normal machines.
## - We add a “test” deployment with all test machines.
## - Each machine gets a NixOS configuration.
## - Each machine gets a VM options entry.
## - Each machine gets a deployment.
## - We add a “default” deployment with all infra machines.
nixops4Deployments = genAttrs machines makeDeployment' // {
default = makeDeployment machines;
test = makeTestDeployment (
fromJSON (
let
env = builtins.getEnv "DEPLOYMENT";
in
if env != "" then
env
else
builtins.trace "env var DEPLOYMENT not set, falling back to ./test-machines/configuration.json!" (readFile ./test-machines/configuration.json)
)
);
};
flake.nixosConfigurations =
genAttrs machines (makeConfiguration false)
// genAttrs testMachines (makeConfiguration true);
flake.vmOptions =
genAttrs machines (makeVmOptions false)
// genAttrs testMachines (makeVmOptions true);
flake.nixosConfigurations = genAttrs machines makeConfiguration;
flake.vmOptions = genAttrs machines makeVmOptions;
}

View file

@ -37,6 +37,24 @@ in
enable = true;
production = true;
domain = "demo.fediversity.eu";
# FIXME: make it work without this duplication
settings =
let
cfg = config.services.${name};
in
{
STATIC_ROOT = "/var/lib/${name}/static";
DEBUG = false;
ALLOWED_HOSTS = [
cfg.domain
cfg.host
"localhost"
"[::1]"
];
CSRF_TRUSTED_ORIGINS = [ "https://${cfg.domain}" ];
COMPRESS_OFFLINE = true;
LIBSASS_OUTPUT_STYLE = "compressed";
};
secrets = {
SECRET_KEY = config.age.secrets.panel-secret-key.path;
};

9
infra/operator/.terraform.lock.hcl generated Normal file
View file

@ -0,0 +1,9 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/external" {
version = "2.3.4"
hashes = [
"h1:hBFp4dEKKevoZEsMW32ralBqrO7cTxTPYdWPqc4Ff+s=",
]
}

View file

@ -0,0 +1 @@
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"nixos","Source":"../sync-nix","Dir":"../sync-nix"}]}

View file

@ -0,0 +1,3 @@
[
"/nix/store/zbjjm6vgdj1p2v21qshd9np3ajdnhniy-opentofu-1.9.0/libexec/terraform-providers"
]

View file

@ -0,0 +1 @@
/nix/store/zbjjm6vgdj1p2v21qshd9np3ajdnhniy-opentofu-1.9.0/libexec/terraform-providers/registry.opentofu.org/hashicorp/external/2.3.4/linux_amd64

View file

@ -0,0 +1 @@
{"version":4,"terraform_version":"1.9.0","serial":7,"lineage":"b39bdfbe-91a7-d39b-96f5-71d9e0c9d47f","outputs":{},"resources":[{"module":"module.nixos[\"garage\"]","mode":"data","type":"external","name":"hash","provider":"provider[\"registry.opentofu.org/hashicorp/external\"]","instances":[{"schema_version":0,"attributes":{"id":"-","program":["sh","-c","echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""],"query":null,"result":{"hash":"a871537a6a7f4dbf5614ebaff5005606"},"working_dir":null},"sensitive_attributes":[]}]},{"module":"module.nixos[\"garage\"]","mode":"managed","type":"terraform_data","name":"nixos","provider":"provider[\"terraform.io/builtin/terraform\"]","instances":[{"schema_version":0,"attributes":{"id":"79db5288-d7d6-24e4-4f23-5bfc353c97b9","input":null,"output":null,"triggers_replace":{"value":[{"hash":"a871537a6a7f4dbf5614ebaff5005606"},"test01","{\n # note interpolations here TF ones\n imports = [\n # shared NixOS config\n ./../common/shared.nix\n # FIXME: separate template options by service\n ./options.nix\n # for service `mastodon` import `mastodon.nix`\n ./../../machines/operator/test01/garage.nix\n # FIXME: get VM details from TF\n ./../../machines/operator/test01\n ];\n ## FIXME: switch root authentication to users with password-less sudo, see #24\n users.users.root.openssh.authorizedKeys.keys = let\n keys = import ../../keys;\n in builtins.attrValues keys.contributors ++ [\n # allow our panel vm access to the test machines\n keys.panel\n ];\n}\n",{"terraform":{"domain":"fediversity.net","hostname":"test01","initialUser":{"displayName":"Testy McTestface","email":"test@test.com","password":"testtest","username":"test"}}}],"type":["tuple",[["map","string"],"string","string",["map",["object",{"domain":"string","hostname":"string","initialUser":["object",{"displayName":"string","email":"string","password":"string","username":"string"}]}]]]]}},"sensitive_attributes":[],"dependencies":["module.nixos.data.external.hash"]}]},{"module":"module.nixos[\"mastodon\"]","mode":"data","type":"external","name":"hash","provider":"provider[\"registry.opentofu.org/hashicorp/external\"]","instances":[{"schema_version":0,"attributes":{"id":"-","program":["sh","-c","echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""],"query":null,"result":{"hash":"a871537a6a7f4dbf5614ebaff5005606"},"working_dir":null},"sensitive_attributes":[]}]},{"module":"module.nixos[\"mastodon\"]","mode":"managed","type":"terraform_data","name":"nixos","provider":"provider[\"terraform.io/builtin/terraform\"]","instances":[{"schema_version":0,"attributes":{"id":"fde5cd73-a82c-c8ee-becd-51e2822652c4","input":null,"output":null,"triggers_replace":{"value":[{"hash":"a871537a6a7f4dbf5614ebaff5005606"},"test06","{\n # note interpolations here TF ones\n imports = [\n # shared NixOS config\n ./../common/shared.nix\n # FIXME: separate template options by service\n ./options.nix\n # for service `mastodon` import `mastodon.nix`\n ./../../machines/operator/test06/mastodon.nix\n # FIXME: get VM details from TF\n ./../../machines/operator/test06\n ];\n ## FIXME: switch root authentication to users with password-less sudo, see #24\n users.users.root.openssh.authorizedKeys.keys = let\n keys = import ../../keys;\n in builtins.attrValues keys.contributors ++ [\n # allow our panel vm access to the test machines\n keys.panel\n ];\n}\n",{"terraform":{"domain":"fediversity.net","hostname":"test06","initialUser":{"displayName":"Testy McTestface","email":"test@test.com","password":"testtest","username":"test"}}}],"type":["tuple",[["map","string"],"string","string",["map",["object",{"domain":"string","hostname":"string","initialUser":["object",{"displayName":"string","email":"string","password":"string","username":"string"}]}]]]]}},"sensitive_attributes":[],"dependencies":["module.nixos.data.external.hash"]}]}],"check_results":null}

View file

@ -0,0 +1 @@
{"version":4,"terraform_version":"1.9.0","serial":4,"lineage":"b39bdfbe-91a7-d39b-96f5-71d9e0c9d47f","outputs":{},"resources":[{"module":"module.nixos[\"garage\"]","mode":"data","type":"external","name":"hash","provider":"provider[\"registry.opentofu.org/hashicorp/external\"]","instances":[{"schema_version":0,"attributes":{"id":"-","program":["sh","-c","echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""],"query":null,"result":{"hash":"02bdde9db2e3d40aaabcb0fd5bf5939b"},"working_dir":null},"sensitive_attributes":[]}]},{"module":"module.nixos[\"garage\"]","mode":"managed","type":"terraform_data","name":"nixos","provider":"provider[\"terraform.io/builtin/terraform\"]","instances":[{"status":"tainted","schema_version":0,"attributes":{"id":"618c1781-9035-8b5a-573a-53ff5407256b","input":null,"output":null,"triggers_replace":{"value":[{"hash":"02bdde9db2e3d40aaabcb0fd5bf5939b"},"test01","{\n # note interpolations here TF ones\n imports = [\n # shared NixOS config\n ./../common/shared.nix\n # FIXME: separate template options by service\n ./options.nix\n # for service `mastodon` import `mastodon.nix`\n ./../../machines/operator/test01/garage.nix\n # FIXME: get VM details from TF\n ./../../machines/operator/test01\n ];\n ## FIXME: switch root authentication to users with password-less sudo, see #24\n users.users.root.openssh.authorizedKeys.keys = let\n keys = import ../keys;\n in builtins.attrValues keys.contributors ++ [\n # allow our panel vm access to the test machines\n keys.panel\n ];\n}\n",{"terraform":{"domain":"fediversity.net","hostname":"test01","initialUser":{"displayName":"Testy McTestface","email":"test@test.com","password":"testtest","username":"test"}}}],"type":["tuple",[["map","string"],"string","string",["map",["object",{"domain":"string","hostname":"string","initialUser":["object",{"displayName":"string","email":"string","password":"string","username":"string"}]}]]]]}},"sensitive_attributes":[],"dependencies":["module.nixos.data.external.hash"]}]},{"module":"module.nixos[\"mastodon\"]","mode":"data","type":"external","name":"hash","provider":"provider[\"registry.opentofu.org/hashicorp/external\"]","instances":[{"schema_version":0,"attributes":{"id":"-","program":["sh","-c","echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""],"query":null,"result":{"hash":"02bdde9db2e3d40aaabcb0fd5bf5939b"},"working_dir":null},"sensitive_attributes":[]}]},{"module":"module.nixos[\"mastodon\"]","mode":"managed","type":"terraform_data","name":"nixos","provider":"provider[\"terraform.io/builtin/terraform\"]","instances":[{"status":"tainted","schema_version":0,"attributes":{"id":"00be3f3d-b7c2-1356-a62c-8eb3106fc7d5","input":null,"output":null,"triggers_replace":{"value":[{"hash":"02bdde9db2e3d40aaabcb0fd5bf5939b"},"test06","{\n # note interpolations here TF ones\n imports = [\n # shared NixOS config\n ./../common/shared.nix\n # FIXME: separate template options by service\n ./options.nix\n # for service `mastodon` import `mastodon.nix`\n ./../../machines/operator/test06/mastodon.nix\n # FIXME: get VM details from TF\n ./../../machines/operator/test06\n ];\n ## FIXME: switch root authentication to users with password-less sudo, see #24\n users.users.root.openssh.authorizedKeys.keys = let\n keys = import ../keys;\n in builtins.attrValues keys.contributors ++ [\n # allow our panel vm access to the test machines\n keys.panel\n ];\n}\n",{"terraform":{"domain":"fediversity.net","hostname":"test06","initialUser":{"displayName":"Testy McTestface","email":"test@test.com","password":"testtest","username":"test"}}}],"type":["tuple",[["map","string"],"string","string",["map",["object",{"domain":"string","hostname":"string","initialUser":["object",{"displayName":"string","email":"string","password":"string","username":"string"}]}]]]]}},"sensitive_attributes":[],"dependencies":["module.nixos.data.external.hash"]}]}],"check_results":null}

View file

@ -0,0 +1 @@
{"agenix":"/nix/store/glsqq1xn5al7d528hvlbm4hl3ladxmka-source","disko":"/nix/store/7wf9q0mb1i43x9dr1qlyfaraq15n6sii-source","flake-inputs":"/nix/store/fqln0bcp6mp75k4sl0cav2f0np60lwhj-source","git-hooks":"/nix/store/8bh3jgq1riy3jxm07vy4xxzvk9xd74pc-source","gitignore":"/nix/store/g5v3sgqy6a0fsmas7mnapc196flrplix-source","home-manager":"/nix/store/cq3b3cx5rv9d0zj57kch9wmxzc2rm8dc-source","htmx":"/nix/store/mwqqk0qmldzvv4xj9kq2lbah2flhc44z-source","nix-unit":"/nix/store/4g1vvy7bhwh16cyd2r8ibq7n6ygk1wvk-source","nixpkgs":"/nix/store/g1bajdwbkcmms8cqd9s8zbq8zxhkyx91-source"}

9
infra/sync-nix/.terraform.lock.hcl generated Normal file
View file

@ -0,0 +1,9 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/external" {
version = "2.3.4"
hashes = [
"h1:hBFp4dEKKevoZEsMW32ralBqrO7cTxTPYdWPqc4Ff+s=",
]
}

View file

@ -0,0 +1,3 @@
[
"/nix/store/zbjjm6vgdj1p2v21qshd9np3ajdnhniy-opentofu-1.9.0/libexec/terraform-providers"
]

View file

@ -0,0 +1 @@
/nix/store/zbjjm6vgdj1p2v21qshd9np3ajdnhniy-opentofu-1.9.0/libexec/terraform-providers/registry.opentofu.org/hashicorp/external/2.3.4/linux_amd64

View file

@ -1 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEElREJN0AC7lbp+5X204pQ5r030IbgCllsIxyU3iiKY niols@wallace
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICJj4L5Yt9ABdQeEkJI6VuJEyUSVbCHMxYLdvVcB/pXh niols@wallace/fediversity

10
launch/.envrc Normal file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
# the shebang is ignored, but nice for editors
# shellcheck shell=bash
if type -P lorri &>/dev/null; then
eval "$(lorri direnv --flake .)"
else
echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]'
use flake
fi

7
launch/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# generated
.auto.tfvars.json
.npins.json
.terraform/
.terraform.lock.hcl
.terraform.tfstate.lock.info
terraform.tfstate*

28
launch/README.md Normal file
View file

@ -0,0 +1,28 @@
# service deployment
deploys [NixOS](https://nixos.org/) templates using [OpenTofu](https://opentofu.org/).
## requirements
- [nix](https://nix.dev/)
## usage
### development
before using other commands, if not using direnv:
```sh
nix-shell
```
then to initialize, or after updating pins or TF providers:
```sh
setup
```
## implementing
proper documentation TODO.
until then, a reference implementation may be found in [`panel/`](https://git.fediversity.eu/Fediversity/Fediversity/src/branch/main/panel).

41
launch/default.nix Normal file
View file

@ -0,0 +1,41 @@
{
system ? builtins.currentSystem,
sources ? import ../npins,
# match the same versions we deploy locally
inputs ? import sources.flake-inputs {
root = ../.;
},
# match the same version of opentofu that is deployed by the root flake
pkgs ? import inputs.nixpkgs {
inherit system;
},
}:
let
inherit (pkgs) lib;
setup = pkgs.writeScriptBin "setup" ''
echo '${lib.strings.toJSON sources}' > .npins.json
rm -f .terraform.lock.hcl
rm -rf .terraform/
tofu init
'';
in
{
# shell for testing TF directly
shell = pkgs.mkShellNoCC {
packages = [
(import ./tf.nix { inherit lib pkgs; })
pkgs.jaq
setup
];
};
tests = pkgs.callPackage ./tests.nix { };
# re-export inputs so they can be overridden granularly
# (they can't be accessed from the outside any other way)
inherit
sources
system
pkgs
;
}

34
launch/garage.nix Normal file
View file

@ -0,0 +1,34 @@
{ pkgs, ... }:
let
## NOTE: All of these secrets are publicly available in this source file
## and will end up in the Nix store. We don't care as they are only ever
## used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
in
{
fediversity = {
garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; };
peertube = peertubeS3KeyConfig { inherit pkgs; };
};
}

159
launch/main.tf Normal file
View file

@ -0,0 +1,159 @@
locals {
system = "x86_64-linux"
# dependency paths pre-calculated from npins
pins = jsondecode(file("${path.root}/.npins.json"))
# nix path: expose pins, use nixpkgs in flake commands (`nix run`)
nix_path = "${join(":", [for name, path in local.pins : "${name}=${path}"])}:flake=${local.pins["nixpkgs"]}:flake"
# user-facing applications
application_configs = {
# FIXME: wrap applications at the interface to grab them in one go?
mastodon = {
cfg = var.mastodon
hostname = "test06"
}
pixelfed = {
cfg = var.pixelfed
hostname = "test04"
}
peertube = {
cfg = var.peertube
hostname = "test05"
}
}
# services shared between applications
peripherals = { for name, inst in {
garage = "test01"
} : name => {
hostname = inst
cfg = {
# enable if any user applications are enabled
enable = anytrue([for _, app in local.application_configs: app.cfg.enable])
}
}
}
}
# hash of our code directory, used in dev to trigger re-deploy
# FIXME settle for pwd when in /nix/store?
# FIXME calculate separately to reduce false positives
data "external" "hash" {
program = ["sh", "-c", "echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""]
}
# TF resource to build and deploy NixOS instances.
resource "terraform_data" "nixos" {
for_each = {for name, inst in merge(
local.peripherals,
local.application_configs,
) : name => inst if inst.cfg.enable}
# trigger rebuild/deploy if (FIXME?) any potentially used config/code changed,
# preventing these (20+s, build being bottleneck) when nothing changed.
# terraform-nixos separates these to only deploy if instantiate changed,
# yet building even then - which may be not as bad using deploy on remote.
# having build/deploy one resource reflects wanting to prevent no-op rebuilds
# over preventing (with less false positives) no-op deployments,
# as i could not find a way to do prevent no-op rebuilds without merging them:
# - generic resources cannot have outputs, while we want info from the instantiation (unless built on host?).
# - `data` always runs, which is slow for deploy and especially build.
triggers_replace = [
data.external.hash.result,
var.domain,
var.initialUser,
local.system,
each.key,
each.value,
]
provisioner "local-exec" {
# directory to run the script from. we use the TF project root dir,
# here as a path relative from where TF is run from.
# note that absolute paths can cause false positives in triggers,
# so are generally discouraged in TF.
working_dir = path.root
environment = {
# nix path used on build, lets us refer to e.g. nixpkgs like `<nixpkgs>`
NIX_PATH = local.nix_path
}
# TODO: refactor back to command="ignoreme" interpreter=concat([]) to protect sensitive data from error logs?
# TODO: build on target?
command = <<-EOF
set -euo pipefail
# INSTANTIATE
command=(
nix-instantiate
--expr
'let
os = import <nixpkgs/nixos> {
system = "${local.system}";
configuration = {
# note interpolations here TF ones
imports = [
# shared NixOS config
${path.root}/shared.nix
# FIXME: separate template options by service
${path.root}/options.nix
# for service `mastodon` import `mastodon.nix`
${path.root}/${each.key}.nix
# FIXME: get VM details from TF
${path.root}/../infra/test-machines/${each.value.hostname}
];
# nix path for debugging
nix.nixPath = [ "${local.nix_path}" ];
## FIXME: switch root authentication to users with password-less sudo, see #24
users.users.root.openssh.authorizedKeys.keys = let
keys = import ../keys;
in attrValues keys.contributors ++ [
# allow our panel vm access to the test machines
keys.panel
];
} //
# template parameters passed in from TF thru json
builtins.fromJSON "${replace(jsonencode({
terraform = {
domain = var.domain
hostname = each.value.hostname
initialUser = var.initialUser
}
}), "\"", "\\\"")}";
};
in
# info we want to get back out
{
substituters = builtins.concatStringsSep " " os.config.nix.settings.substituters;
trusted_public_keys = builtins.concatStringsSep " " os.config.nix.settings.trusted-public-keys;
drv_path = os.config.system.build.toplevel.drvPath;
out_path = os.config.system.build.toplevel;
}'
)
# instantiate the config in /nix/store
"$${command[@]}" -A out_path
# get the other info
json="$("$${command[@]}" --eval --strict --json)"
# DEPLOY
declare substituters trusted_public_keys drv_path
# set our variables using the json object
eval "export $(echo $json | jaq -r 'to_entries | map("\(.key)=\(.value)") | @sh')"
# FIXME: de-hardcode domain
host="root@${each.value.hostname}.abundos.eu" # FIXME: #24
buildArgs=(
--option extra-binary-caches https://cache.nixos.org/
--option substituters $substituters
--option trusted-public-keys $trusted_public_keys
)
sshOpts=(
-o BatchMode=yes
-o StrictHostKeyChecking=no
)
# get the realized derivation to deploy
outPath=$(nix-store --realize "$drv_path" "$${buildArgs[@]}")
# deploy the config by nix-copy-closure
NIX_SSHOPTS="$${sshOpts[*]}" nix-copy-closure --to "$host" "$outPath" --gzip --use-substitutes
# switch the remote host to the config
ssh "$${sshOpts[@]}" "$host" "nix-env --profile /nix/var/nix/profiles/system --set $outPath; $outPath/bin/switch-to-configuration switch"
EOF
}
}

17
launch/mastodon.nix Normal file
View file

@ -0,0 +1,17 @@
{ pkgs, ... }:
let
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
in
{
fediversity = {
mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
enable = true;
};
temp.cores = 1; # FIXME: should come from NixOps4 eventually
};
}

54
launch/options.nix Normal file
View file

@ -0,0 +1,54 @@
# TODO: could (part of) this be generated somehow? c.f #275
{
lib,
...
}:
let
inherit (lib) types mkOption;
inherit (types) str enum submodule;
in
{
options.terraform = {
domain = mkOption {
type = enum [
"fediversity.net"
];
description = ''
Apex domain under which the services will be deployed.
'';
default = "fediversity.net";
};
hostname = mkOption {
type = str;
description = ''
Internal name of the host, e.g. test01
'';
};
initialUser = mkOption {
description = ''
Some services require an initial user to access them.
This option sets the credentials for such an initial user.
'';
type = submodule {
options = {
displayName = mkOption {
type = str;
description = "Display name of the user";
};
username = mkOption {
type = str;
description = "Username for login";
};
email = mkOption {
type = str;
description = "User's email address";
};
password = mkOption {
type = str;
description = "Password for login";
};
};
};
};
};
}

20
launch/peertube.nix Normal file
View file

@ -0,0 +1,20 @@
{ pkgs, ... }:
let
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
in
{
fediversity = {
peertube = peertubeS3KeyConfig { inherit pkgs; } // {
enable = true;
## NOTE: Only ever used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24";
};
};
}

16
launch/pixelfed.nix Normal file
View file

@ -0,0 +1,16 @@
{ pkgs, ... }:
let
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
in
{
fediversity = {
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
enable = true;
};
};
}

43
launch/resource.nix Normal file
View file

@ -0,0 +1,43 @@
{
lib,
config,
...
}:
let
inherit (lib) attrValues elem mkDefault;
inherit (lib.attrsets) concatMapAttrs optionalAttrs;
inherit (lib.strings) removeSuffix;
secretsPrefix = ../secrets;
secrets = import (secretsPrefix + "/secrets.nix");
keys = import ../keys;
in
{
fediversityVm.hostPublicKey = mkDefault keys.systems.${config.fediversityVm.name};
## The configuration of the machine. We strive to keep in this file only the
## options that really need to be injected from the resource. Everything else
## should go into the `./nixos` subdirectory.
imports = [
../infra/common/options.nix
../infra/common/nixos
];
## Read all the secrets, filter the ones that are supposed to be readable
## with this host's public key, and add them correctly to the configuration
## as `age.secrets.<name>.file`.
age.secrets = concatMapAttrs (
name: secret:
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) {
${removeSuffix ".age" name}.file = secretsPrefix + "/${name}";
}
) secrets;
## FIXME: switch root authentication to users with password-less sudo, see #24
users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors ++ [
# allow our panel vm access to the test machines
keys.panel
];
}

26
launch/shared.nix Normal file
View file

@ -0,0 +1,26 @@
{
pkgs,
config,
...
}:
let
inherit (config.terraform) hostname domain initialUser;
in
{
imports = [
<disko/module.nix>
<agenix/modules/age.nix>
../services/fediversity
./resource.nix
];
fediversityVm.name = hostname;
fediversity = {
inherit domain;
temp.initialUser = {
inherit (initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" initialUser.password;
};
};
}

1
launch/shell.nix Normal file
View file

@ -0,0 +1 @@
(import ./. { }).shell

26
launch/tests.nix Normal file
View file

@ -0,0 +1,26 @@
{ lib, pkgs }:
let
defaults = {
virtualisation = {
memorySize = 2048;
cores = 2;
};
};
tf = pkgs.callPackage ./tf.nix { };
tfEnv = pkgs.callPackage ./tf-env.nix { };
in
lib.mapAttrs (name: test: pkgs.testers.runNixOSTest (test // { inherit name; })) {
tf-validate = {
inherit defaults;
nodes.server = {
environment.systemPackages = [
tf
tfEnv
];
};
testScript = ''
server.wait_for_unit("multi-user.target")
server.succeed("${lib.getExe tf} -chdir='${tfEnv}/launch' validate")
'';
};
}

34
launch/tf-env.nix Normal file
View file

@ -0,0 +1,34 @@
{
lib,
pkgs,
sources ? import ../npins,
...
}:
pkgs.stdenv.mkDerivation {
name = "tf-repo";
src =
with lib.fileset;
toSource {
root = ../.;
# don't copy ignored files
fileset = intersection (gitTracked ../.) ../.;
};
buildInputs = [
(import ./tf.nix { inherit lib pkgs; })
];
buildPhase = ''
runHook preBuild
pushd launch/
# calculated pins
echo '${lib.strings.toJSON sources}' > .npins.json
# generate TF lock for nix's TF providers
tofu init -input=false
popd
runHook postBuild
'';
installPhase = ''
runHook preInstall
cp -r . $out
runHook postInstall
'';
}

24
launch/tf.nix Normal file
View file

@ -0,0 +1,24 @@
# FIXME: use overlays so this gets imported just once?
{
lib,
pkgs,
...
}:
let
tofuProvider =
provider:
provider.override (oldArgs: {
provider-source-address =
lib.replaceStrings [ "https://registry.terraform.io/providers" ] [ "registry.opentofu.org" ]
oldArgs.homepage;
});
tf = pkgs.opentofu;
tfPlugins = (
p: [
p.external
]
);
in
# tf.withPlugins tfPlugins
# https://github.com/NixOS/nixpkgs/pull/358522
tf.withPlugins (p: pkgs.lib.lists.map tofuProvider (tfPlugins p))

51
launch/variables.tf Normal file
View file

@ -0,0 +1,51 @@
# TODO: (partially) generate, say from nix modules, c.f. #275
variable "domain" {
type = string
default = "fediversity.net"
}
variable "mastodon" {
type = object({
enable = bool
})
default = {
enable = false
}
}
variable "pixelfed" {
type = object({
enable = bool
})
default = {
enable = false
}
}
variable "peertube" {
type = object({
enable = bool
})
default = {
enable = false
}
}
variable "initialUser" {
type = object({
displayName = string
username = string
email = string
# TODO: mark (nested) credentials as sensitive
# https://discuss.hashicorp.com/t/is-it-possible-to-mark-an-attribute-of-an-object-as-sensitive/24649/2
password = string
})
# FIXME: remove default when the form provides this value, see #285
default = {
displayName = "Testy McTestface"
username = "test"
email = "test@test.com"
password = "testtest"
}
}

View file

@ -1,5 +1,47 @@
{
"pins": {
"agenix": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "ryantm",
"repo": "agenix"
},
"branch": "main",
"submodules": false,
"revision": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
"url": "https://github.com/ryantm/agenix/archive/e600439ec4c273cf11e06fe4d9d906fb98fa097c.tar.gz",
"hash": "006ngydiykjgqs85cl19h9klq8kaqm5zs0ng51dnwy7nzgqxzsdr"
},
"disko": {
"type": "GitRelease",
"repository": {
"type": "GitHub",
"owner": "nix-community",
"repo": "disko"
},
"pre_releases": false,
"version_upper_bound": null,
"release_prefix": null,
"submodules": false,
"version": "v1.11.0",
"revision": "cdf8deded8813edfa6e65544f69fdd3a59fa2bb4",
"url": "https://api.github.com/repos/nix-community/disko/tarball/v1.11.0",
"hash": "13brimg7z7k9y36n4jc1pssqyw94nd8qvgfjv53z66lv4xkhin92"
},
"flake-inputs": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "fricklerhandwerk",
"repo": "flake-inputs"
},
"branch": "main",
"submodules": false,
"revision": "559574c9cbb8af262f3944b67d60fbf0f6ad03c3",
"url": "https://github.com/fricklerhandwerk/flake-inputs/archive/559574c9cbb8af262f3944b67d60fbf0f6ad03c3.tar.gz",
"hash": "0gbhmp6x2vdzvfnsvqzal3g8f8hx2ia6r73aibc78kazf78m67x6"
},
"htmx": {
"type": "GitRelease",
"repository": {

View file

@ -12,21 +12,32 @@ let
manage = pkgs.writeScriptBin "manage" ''
exec ${pkgs.lib.getExe pkgs.python3} ${toString ./src/manage.py} $@
'';
package = pkgs.callPackage ./nix/package.nix { };
in
{
shell = pkgs.mkShellNoCC {
inputsFrom = [ (pkgs.callPackage ./nix/package.nix { }) ];
inputsFrom = [ package ];
packages = [
pkgs.npins
manage
];
env = import ./env.nix { inherit lib pkgs; } // {
NPINS_DIRECTORY = toString ../npins;
CREDENTIALS_DIRECTORY = toString ./.credentials;
DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3";
};
env =
let
inherit (builtins) toString;
in
import ./env.nix { inherit lib pkgs; }
// {
NPINS_DIRECTORY = toString ../npins;
CREDENTIALS_DIRECTORY = toString ./.credentials;
DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3";
# locally: use a fixed relative reference, so we can use our newest files without copying to the store
REPO_DIR = toString ../.;
};
shellHook = ''
ln -sf ${sources.htmx}/dist/htmx.js src/panel/static/htmx.min.js
${lib.concatStringsSep "\n" (
map (file: "ln -sf ${file.from} ${toString ./src/${file.to}}") package.generated
)}
# in production, secrets are passed via CREDENTIALS_DIRECTORY by systemd.
# use this directory for testing with local secrets
mkdir -p $CREDENTIALS_DIRECTORY

View file

@ -3,16 +3,14 @@
pkgs,
...
}:
let
inherit (builtins) toString;
in
{
REPO_DIR = toString ../.;
# explicitly use nix, as e.g. lix does not have configurable-impure-env
BIN_PATH = lib.makeBinPath [
# explicitly use nix, as e.g. lix does not have configurable-impure-env
pkgs.nix
# nixops error maybe due to our flake git hook: executing 'git': No such file or directory
pkgs.lix
pkgs.bash
pkgs.coreutils
pkgs.openssh
pkgs.git
pkgs.jaq # tf
(import ../launch/tf.nix { inherit lib pkgs; })
];
}

View file

@ -29,6 +29,7 @@ let
((pkgs.formats.pythonVars { }).generate "settings.py" cfg.settings)
(builtins.toFile "extra-settings.py" cfg.extra-settings)
];
REPO_DIR = import ../../launch/tf-env.nix { inherit lib pkgs; };
};
python-environment = pkgs.python3.withPackages (
@ -157,9 +158,7 @@ in
};
};
users.users.${name} = {
isNormalUser = true;
};
users.users.${name}.isNormalUser = true;
users.groups.${name} = { };
systemd.services.${name} = {
@ -167,6 +166,7 @@ in
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [
pkgs.openssh
python-environment
manage-service
];

View file

@ -1,5 +1,6 @@
{
lib,
pkgs,
sqlite,
python3,
sources ? import ../../npins,
@ -11,7 +12,7 @@ let
root = ../src;
fileset = intersection (gitTracked ../../.) ../src;
};
pyproject = with lib; fromTOML pyproject-toml;
pyproject = fromTOML pyproject-toml;
# TODO: define this globally
name = "panel";
# TODO: we may want this in a file so it's easier to read statically
@ -25,6 +26,12 @@ let
packages = [ "${name}" ]
include-package-data = true
'';
generated = [
{
from = "${sources.htmx}/dist/htmx.min.js";
to = "./panel/static/htmx.min.js";
}
];
in
python3.pkgs.buildPythonPackage {
pname = name;
@ -54,11 +61,19 @@ python3.pkgs.buildPythonPackage {
]
++ pythonPackages;
passthru = {
inherit generated;
};
postInstall = ''
mkdir -p $out/bin
cp -v ${src}/manage.py $out/bin/manage.py
chmod +x $out/bin/manage.py
wrapProgram $out/bin/manage.py --prefix PYTHONPATH : "$PYTHONPATH"
cp ${sources.htmx}/dist/htmx.min.js* $out/${python3.sitePackages}/panel/static/
wrapProgram $out/bin/manage.py \
--set REPO_DIR "${import ../../launch/tf-env.nix { inherit lib pkgs; }}" \
--prefix PYTHONPATH : "$PYTHONPATH"
${lib.concatStringsSep "\n" (
map (file: "cp ${file.from} $out/${python3.sitePackages}/${file.to}") generated
)}
'';
}

View file

@ -1,3 +1,4 @@
# TODO upstream, see #248
{
lib,
buildPythonPackage,

View file

@ -171,6 +171,53 @@ COMPRESS_PRECOMPILERS = [
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"filters": {
"require_debug_false": {
"()": "django.utils.log.RequireDebugFalse",
},
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue",
},
},
"formatters": {
"django.server": {
"()": "django.utils.log.ServerFormatter",
"format": "[{server_time}] {message}",
"style": "{",
},
"standard": {
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
},
},
"handlers": {
"console": {
"level": "INFO",
# "filters": ["require_debug_true"],
"class": "logging.StreamHandler",
"formatter": "standard",
},
"django.server": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "django.server",
},
"mail_admins": {
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "django.utils.log.AdminEmailHandler",
},
},
"loggers": {
"": {
"handlers": ["console"],
"level": "DEBUG" if DEBUG else "INFO",
},
},
}
# Customization via user settings
# This must be at the end, as it must be able to override the above
# TODO(@fricklerhandwerk):

View file

@ -1 +0,0 @@
/nix/store/mwqqk0qmldzvv4xj9kq2lbah2flhc44z-source/dist/htmx.js

View file

@ -1,6 +1,7 @@
from enum import Enum
import json
import subprocess
import logging
import os
from django.urls import reverse_lazy
@ -13,6 +14,8 @@ from django.shortcuts import render
from panel import models, settings
from panel.configuration import forms
logger = logging.getLogger(__name__)
class Index(TemplateView):
template_name = 'index.html'
@ -134,25 +137,26 @@ class DeploymentStatus(ConfigurationForm):
},
}
# serialize back and forth now we still need to manually inject the dummy user
deployment_params = json.dumps(dummy_user | json.loads(submission))
deployment_params = dummy_user | json.loads(submission)
env = {
"PATH": settings.bin_path,
# "TF_LOG": "info",
} | {
# pass in form info to our deployment
"DEPLOYMENT": deployment_params,
# FIXME: ensure sensitive info is protected
f"TF_VAR_{k}": v if isinstance(v, str) else json.dumps(v) for k, v in deployment_params.items()
}
logger.info("env: %s", env)
cwd = f"{settings.repo_dir}/launch"
cmd = [
"nix",
"develop",
"--extra-experimental-features",
"configurable-impure-env",
"--command",
"nixops4",
"tofu",
# f"-chdir={cwd}",
"apply",
"test",
f"-state={cwd}/terraform.tfstate", # FIXME: separate users' state
"--auto-approve",
"-lock=false",
"-parallelism=1" # limit OOM risk
]
deployment_result = subprocess.run(
cmd,
cwd=settings.repo_dir,
env=env,
)
return deployment_result, json.loads(deployment_params)
deployment_result = subprocess.run(cmd, cwd=cwd, env=env)
logger.debug("deployment_result: %s", deployment_result)
return deployment_result, deployment_params

View file

@ -1,17 +1,17 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A JzLWMEH98I5/A8O55mKUMy5zo2kg3Qk8SfXnHvkjwT4
8f7zDHSp3AHoAQy0dVWMa1TurCBLnsHNtbNjaD++7ow
-> ssh-ed25519 BAs8QA eCD3saYXdv1bjAoQghmyVqHjMBu/o2lWgu7grk1vgRs
//pOnkzqQTK3xmeCjruo46ju2X136KEt6DpsegMouFQ
-> ssh-ed25519 ofQnlg ePjq7GmM36qaGxcJ0qnW8FdKDjwlXtFqOBK8OgWY3Co
gVmsDP9rMcQD/B6BpNhCn+avdgjhyyohNUXlatXpXo0
-> ssh-ed25519 COspvA lrQB/NEmMUR2RWxfRzE2iTDkjMYsrIaiKn8thxZR+RA
MU23Z28v+cNk2VxpAYaYoFb53js2Zr9/KAM9uMe6+EA
-> ssh-ed25519 2XrTgw z1ixx5dYCNbgw6wWV45b4wn69X/5/4MzesTomWa4WB4
eNSlP6+nUW9rpsGyzqOEQ+7IVpGeU3UcZpyfB9XT2/4
-> ssh-ed25519 1MUEqQ c6ps9RB6Dw9JtR0+4eB1NDx44uUes8YjLrY7RCpD0jg
GwVRqR5t07ctbWhwH76T+SAe2Y6Vv1uY/AHkzd/gw/c
-> ssh-ed25519 Fa25Dw jTqtV2RWsXBH4zgWAYr9tBGC/BbXKBvr3uyL8IgmI1o
qBirnzIpi9hB61xwyS+5U6XBobAquEJrV3cleDtG8/4
--- j/vJgDV+47UmKokdvztXntBIhCLEyUm2aYoGJ2WMKbU
¢¹ŽÀi±ËQõf §¥ÐÅ·DN§àB"—ÍvsëB6QžorF‡<46>Å
-> ssh-ed25519 Jpc21A 9edPaA2tT4SeYNTPzF0E157daC2o+JH/WQQCT+vLbFg
C48EtLdhB75TTzfEZTw1DypicHiVlSmFzjfbqfO9N/8
-> ssh-ed25519 BAs8QA T+kXpZg1v0XRkub5DWir7vYwO7KaOJLZBNYxxXiBUCw
zBRwMTDpyI7twEwUGsmJYyYPw9btBx5Kakj1yT+XY8U
-> ssh-ed25519 ofQnlg 4UoEDY/tdKz8LrX1BkBU1/cn+vSaYLUl7xX9YmzANBY
8CACq1n3AJgD9IyPN23iRvThqsfQFF5+jmkKnhun24U
-> ssh-ed25519 COspvA HxcbkqHL+LpVmwb+Fo5JuUU+C+Pxzdxtb0yZHixwuzM
7FIhxdbjHJlgQQgjrHHUK5cecqs5aT7X3I8TWf8c2gc
-> ssh-ed25519 2XrTgw R6Ia8MVIZKPnNZ0rspZ34EqoY8fOLeB9H7vnvNBLg1g
55NUqz5Yygt6FKJ3bR5iHxQp8G7S2gyFwrJNX1Pb/2Y
-> ssh-ed25519 awJeHA hJdTuAScoewVMt7HWiisSkL0zSeClFzYzzKL84G893o
ou780VLrW1s4d6L+lEVu3kXaGn4dvtFPA31supwEL50
-> ssh-ed25519 Fa25Dw mJcqnXA3fQeoKrG7RJ7nVeLxPvrxqbj+lJdx6jQ9IR8
f5Q7mrQSSDsm1Z/uSAnvx66mgnRC3XaBLQrVL9f/Ijs
--- W/KmboXTLV12X6WtVQKHNe+ZHvS2q9EHUZwofSgJSE8
^kûÚ h©0ÔkÇ ¢¸_Ç·ûQÞm7\òÖ}÷Áë?½qø<ÿm

View file

@ -1,17 +1,18 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A l6Xwv4JBlTeRTC7RgjxY9gDrCk96atMUH/62P+u55Qs
CrYsLZgDFAiR8up87lhGZqsbAEZtOXG+l5IzLh2uaqg
-> ssh-ed25519 BAs8QA lgtmfoc4vKfRpI/XbIS258BMyIB4mTdquEx/Kxm5OTI
3gQL8Rnqc7JfqsRmKYU3rD0cWMKdnIeVXbY3eFM07RU
-> ssh-ed25519 ofQnlg 0vwuCrduMLjssA3CK3gfVPMSPYKO9cF7HH1JF/oJv18
2KrZgQmpvw/tNDJrDArinnbEjopkkmuG8s7t6klBXcQ
-> ssh-ed25519 COspvA NT+/h2KsiZN2XbaWAlrTlDwyAPmHWrwgr6f0uhSbEGs
QpoAd+69VYrZwAC0LwDm1m/zfslVgzxpVFihQWDcqzE
-> ssh-ed25519 2XrTgw QoJ/74FOqYFxHJYXJEkyzbGY0xptSjorNvnyUS1p6zk
0sJ2F6IFuTrRvXO5ND1QL4CZ2lr1BAU3iQffC6Uc3h4
-> ssh-ed25519 1MUEqQ xxgEUIhvWN/ZfRMGfu3fKQ+fWM5WSz8OexXPm6jaXDk
RXe0JMZ0sYMdQvrbi+zAs9F3d98ocRFnsSGUuUWccRk
-> ssh-ed25519 Fa25Dw tw4sqQcO86Gh0FGUD+O3bJ+8OcaN5rm8R6qocXvDbRg
7hiWa4qznHTV45kvC7ucj7j7FbPrqYK5OcCcByrcSxg
--- kvZDYq5n/OXu7xe2Kf5vGN0zosl9fgH4CAf3K0Tq3U4
¤D!´Ój]2ï݃T+• ÃUó¬æcgÝoP”‰ ˆF.Þ¿_vbÕÓÔdª9
-> ssh-ed25519 Jpc21A ZyJtpgzBrA3WFa1+uiNLZieP6GAqYzAtjmUkgbqKwyA
TeO/ghRNVJQrdeFAvgQITC6MC7x7IHDtFYqHVloVfAQ
-> ssh-ed25519 BAs8QA zIfPuCmSZyosDfWvL6A5DstWeBORDkjqb6Hxjx2dYgE
Nq/YU6oDlIC+ROtM2P5aBeF6iJsxUbrU2LnLDRux35Y
-> ssh-ed25519 ofQnlg dm1bj8BJxD1MpFqdDkfDL4wWV7mjmb51hVqkk2uRfwE
N7Bwhw1agAbNGaF57VmFKwIWeUibaYRSu2Ke1ZlXPLQ
-> ssh-ed25519 COspvA /DYoVVG4SOFgrIbMvEP0U7QpX6VK9otJVBYrj4I0dlo
5EWys6IcMgrwW2p5fFeJEgDqandyiS1RzMvqGsN1X0I
-> ssh-ed25519 2XrTgw YW5gzijsL1oRa5vQ3PY+8o3iNZIji1BZfB9dGcg9QHA
0WWu3F1Rpo9mi4YU2r3Jydr6Fg7bqWMuTtMJOEOj3NU
-> ssh-ed25519 awJeHA 8xl0RpXAJ7X5pe6qzDKXHJvMnjYEN85BS+maFytsSDQ
MQYrdbh7w7TwGk2Wivja8acQYkmYZ2YWMkE/YA/5K3g
-> ssh-ed25519 Fa25Dw koR3KjKzS3Uei58I68qYkgxE0ifhIXKTblpD4cFAVhQ
X7mQgsMY02VHyxJUVN5ml/QRVTjBDm7BD4w0g3Jwmq4
--- E6iHgNV3cjQrhNcZa6uqUwvGxf0ouNZpPXhet3Vicrw
ïò”F Q/Gíù°ʤT]ns&[›ýëú&œæ
Q(6pòM$È™ü Çõ

View file

@ -1,16 +1,15 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A 7QzPABl0IuQlZ9nsqfdbmWT9zb6iHGEOQiJ5sBpuLWU
zusphKpt0yVxQj0zh4AGtYFORIhUoJHJBYfEAHb/VN0
-> ssh-ed25519 BAs8QA PBfmSc0pag5zCqB/EKvirDXyleNi2sgMZ0xTAMiANFo
gugtjl9TSsGQuPypQiKZZqr7JfMKSdwVHGGKevyfF+k
-> ssh-ed25519 ofQnlg RQd9jPyn8nv1zCLbETa9/JB39fwX3X0X9gElcHWEfCA
7ZEBE4qaZUtrXLc0caZ/tfJFYT9UDYkVvuaBc0SVBhc
-> ssh-ed25519 COspvA plM6PJykVR0NuSrOkRkA6ucOzUpijFaKbe99jaVrQSw
7AkcdijjOSckx3GxXwXo1K34ReU3x0yWamlxdaA1FWA
-> ssh-ed25519 2XrTgw +2ZOwvZRUmnuHVV8poyMR6eIvPQoxWQRngKJdL2kVHc
rzWJIuZUtuurvIdV/47N2CNu8x4T/vca8IeRqi+mk/I
-> ssh-ed25519 1MUEqQ 9U13b5LO4pSKhlvWEtkdrjTmVO5oGqcdT3Ime1AHjH4
8wC87WZZYMZaR2YlG3oEt79QcZMA86TrUbvTekAUkqw
--- X8Szdu5EyyPKR6xaL/8uKdHRc/D7wVGizk5k0XanreQ
¯<EFBFBD>欿Æj{Ò“¦!q-ò˜G:£ÂjQò[_Ä„=ò<>#ÇŸd¾(i@/f3ºn°¦ý
r :Ú‰ ¾×txð¬|<<øX¡ÜºÊ¼™¨
-> ssh-ed25519 Jpc21A N/T7HaInZ13IlJfzeli5nRz5pdBQETO6D1P8X42IHRw
q431ZtsodQ9NgcWTjmS0Kx4ATwVFp2nkm+MHe7aXTZU
-> ssh-ed25519 BAs8QA +VUHgmz2oNG6L1FgZy3uGVMs6qUGirFHK8Ts2ghNLHs
sjQu78xqM6KLmRiYd2o2uK/PjYLnyZihzVoCV7qKBX4
-> ssh-ed25519 ofQnlg cBfd95Ir33ggt1J1P2TkFRULr2uYPVuyrQ5XpjBxEW0
TWFVHboXr95cFm5yjQ7gn7hjbSmVBfB/9dldsoga/9Q
-> ssh-ed25519 COspvA RMW9FlDmiQUu7cg0fKir55VqrDRCoYVVZMOcMHyrMj8
qeXkWdKFJN7APgYh7AjyJLeQI2CAEaGAcXiVaBaOJwY
-> ssh-ed25519 2XrTgw BRobowRWZ9giVL2dFyGvzzF7gyWUQd1ounMQBtsM/lM
dFyli2skTgzVWGVolLG2GuGNh/Xu3IaJsznOkcWqKGc
-> ssh-ed25519 awJeHA Cu7fiv+SL71oho/xoJMw/Lztf4WkNKmImVS/8xyLiTo
3sB/t0squi1crjHFBaN6btrvGUeWaKfmGa7yxREvy2o
--- SqPDTJ/XV26nNG1ib5phNNRdQi5+Wk0cxhqUr1ygjGw
Æt”OÊá<C3A1>âåYöª¶^´×U<C397>j†ë!Y.<2E>^<5E>}Xúôæ¡3¿ÖŽ"kE×í šú¾s¥,0l+¾ýn‡;fW®

View file

@ -1,17 +1,17 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A fBhVzGFs61K63QtA8RdOuuGfHFjMe/Dp0M6TXGLGWDU
qppnUZ+LQCXhuMCFMYv2D2CkmEfjb7mpmJufIeVjjaM
-> ssh-ed25519 BAs8QA PNicZCWLkbvM4ih77/F4z6FzHomL9EsJCuSCjbdRTwA
qIpTl/v7Xl08qBB//dFeW9qQiZg10YrYLnfyQrgDRfQ
-> ssh-ed25519 ofQnlg 9/vSN3V25ysXBOvS4UJQEzm0734zqO0gXjhgzX63tTs
AH9Q1lWr+RgICfW3h+D2SgCTFr+azI0x3J3eFnaz/XA
-> ssh-ed25519 COspvA IB1nWOMaVZVcvEog6UaqCak2fcKxIUN2yXvvRSTDxGw
Ti7JuBgU6phlI+oXfDDvx42dRu95kTwesRUKu4QsXZ4
-> ssh-ed25519 2XrTgw 7S9ZhJvUFMw9tDCc0HvkRsRqjvmn47GFGVg/jkxIy1I
cj27gqqihSZG3Jcab9h9FyNJ1J8FjlUiyVlDot+sbWQ
-> ssh-ed25519 1MUEqQ l9mVTLD9rZXisBEz0sU2AdFNrJQ/+zuFTiIod5R/HCI
2q3csSEvMW5vtzqGHYTtZ1nZ0J1vT23bjhuj9HTsdWk
-> ssh-ed25519 kXy85Q BCrDvkPZLvx2Kvgapa3BT+AmpS6Fa5kpkgBnRVso2BE
ZBi+x/2ilJIzhzGipdZQJoGOjSqCuAttsqCDVFlYJ8Y
--- iWtseKyfUMBkQTUl9QzwXXLQcodEJeZt1Wuj5sR18yY
+2,エ ゚恝Jrメィ><7F>z?ホi<EFBE8E><69><EFBFBD>x<EFBFBD>0z<30>ヤ智メ劬呱^ゥネコ食・濤礫+1」ァ<EFBDA3>
-> ssh-ed25519 Jpc21A 98RNGhrNW+Pg5EeQ6wOgRNaPqauBrI0hnUGDyCpFsQ4
5ZCq4vkp6N9/KR4Uf0e9MtNM1SHD3Pr97B31or6y8Xc
-> ssh-ed25519 BAs8QA znlRV7zbTUMsnDY/TiNgeCaqzi87jL/r/5dhc7bJ7hw
9pwl5mcmbHaO39jmiuOkaC9mpyiS3xQSCd7q17gH6OU
-> ssh-ed25519 ofQnlg Wr8M7IpBjvNatOlfmRodoHicPjSjyMVqkC+R+18SYjk
Th18PUYoo1TvP+d+6aXLvipsm3QPW+DKQSv8rJqeblk
-> ssh-ed25519 COspvA spxBYfEWDeKhaSsufZ4GDtIMKz8XznD4kS7Zjb4BLFU
B2wT7+bXgWezmUIj0mpVPPjKOoIj7cDH16uvW4ujbss
-> ssh-ed25519 2XrTgw lY+t4jvdSgZ5ZKMemAN0u32fPUAraaGu+ExMEsR/c0w
KfOBcGrhIztEnKKmsv6ZD6K9TleZRgIRWbOlG8Tcvaw
-> ssh-ed25519 awJeHA 1VVLZa0l8LX79LqZqlYRfXmKVIi9zpLcoysi0NQ3L0o
m79gHUkQ87zFoB8Awlcxt2GrCMrwr5KSfyiSqa6kEko
-> ssh-ed25519 kXy85Q uaEXsQeApgXqzWZRL0AtsPqjt5qOWxoQjN1Keiii1Ds
LtHUNkV3n/jgeAcEIyq03z84KKa5qQoAo1aaJeK6Duk
--- 6s4nA95ds+3slR3QtHQmAkTEBNlSOQusQzjaY/3M5Ds
tÑ:ƒX´¯=Ï´ù8€OoùxþÔ;ì“ÀyIý²ˆµÌˆõêJ/oW0Ô¼; x$£tŠœ‡,7£-aý

Binary file not shown.

View file

@ -1,18 +1,18 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A BfHJN3vILbsfY91kEjSQ+STrn6vQfn83Fx3cBCNshRQ
0O8GJYfF8WFS4Xsgj5v1cly4JP1MgSN40OgRdW/i0rA
-> ssh-ed25519 BAs8QA Ue0NLMpmZDSTGvwZ8lhzes7pcmit9F6uwzeT4XhiwC0
jsvvuOW344i8GR4B139SX0LwTqzKQEgBvsy8oRppqBU
-> ssh-ed25519 ofQnlg 9iSMQeTJn1OUqTF+M2sHpp69lblb8E6TVbgZs7vgD2U
uMQI1gTTMvYW7ea9xBAln118JEeNvv3nqbq32zJoat8
-> ssh-ed25519 COspvA YxCyfe0li23JoI2q4XFVUx4vrWApLwSnJD31PHXuPBg
8xuT9+W2mnTag9tm6F6LXzHkIh2Nou/8lgxd64OpvWk
-> ssh-ed25519 2XrTgw jEzw0A9Wd1b1Zoryzp/W/QZ6bd99E7sySnr/W2xcnDs
IyMrojJ3AChS6lhj599caNM+02i16qtpc6cocln14b4
-> ssh-ed25519 1MUEqQ haiI/5EkuTZ2YHxsqSVlqfM0VVR24DIDrMS3RmXwAhU
qVIAvLp2qG4A3f3OKUqAKqH1eOicJz54nfblPSUKrSw
-> ssh-ed25519 dgBsjw /vCnznu73U99onCWcM0aQlW0azscyUe4BB2kKeZvtHs
MPnvXR/WVsl/tJ1YPoc7nk2Ls2x9bbtJdNp3CQTuuWI
--- OzkqKlw4xu3McMk20orQN0h+VPYfUUSDC+DsgRU1tSw
ü^@ÃmŽÚé`B˜³#{¡Ÿ‚ GUu´|¹Á œ¡ rÚïjb¥:Ô“d²ù] GØÎ©¦ú²-«ÂMÁ³ŸÜálÙ3mÌí)½š@¢—±e?¯ªêe¤üZ
Y}ÿ!fÐ
-> ssh-ed25519 Jpc21A EuMYAiZX+4A12eu19mIY7u+WYF7NJ9qJosQSVlxR6n8
bK5CMXAmP23t1p9bgmqoVg4Qcu2qYKGc4t36v8e9eow
-> ssh-ed25519 BAs8QA IwRyitDNTzUPzQAUbDNEKjFiF8WPD/OyztOZQeoTEzw
OwiTWvk4NmUgExav0uH6HlThDNU5hsKXfR6KHsFOV3I
-> ssh-ed25519 ofQnlg 3TcMbLX1JsQL8+Gqy7IFZwykZr2BspvPCuZT1SHtnQQ
Ci5OeBj2aiC8ut9jIEUMt3qfYH+cJrnVud6AH54Ndn8
-> ssh-ed25519 COspvA 0t9f3Wu3ILv4QTJhwT619y+7XFrryCLbpIZC6aE+qQI
oPQP48F6oO/tkqLZDdjkGtIap7KHiAknbpTNL6/yLaU
-> ssh-ed25519 2XrTgw YOZsaYQH9vMH0QqSXGh8GyhRV4MbcBGPFfFaKpo3Ckk
kUShJbADA+6bpx2adxvzlI/0jSM5bIBfZfdSE/7Vm5Y
-> ssh-ed25519 awJeHA dF3m0hQWX9c0EezDr56Kt/F4d1Uim7NwvIX6zRws0Eo
pst243yrARODwrnyz8cJAzgDxdPOUsRbs7yPZePABFs
-> ssh-ed25519 dgBsjw PUYHcP/tgNnKyvlIoJRcNcW3zabVV1iHXIWfKqgW9xc
tXNjSuVH/g/oN5o75FPkFFpviF7SeFSN9kbqURvgMDE
--- wHgBAN9c6F6T5hFJGo8uH8zqDkQDwx3/jVNKUtQ3arE
«Ñ¢Á
ò@µú¡fÃ`m;ÕcæäU²€ùò£Íd…eSèyfv¿»¡€J?ø `œfj£Äa}lÃó ¿Úxç²BÇt2èfìôm08ÓoÝtRál9˜èx¤¢ŒÅžæ÷

View file

@ -1,17 +1,17 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A RFzPu3fD28STex7ND5lE9bfCxQq/xeHEb7h7BFt9pVA
8K+ECDGs71V91jEVQjrRVQNbdTzBb6W9jkp0+K1trzw
-> ssh-ed25519 BAs8QA F2L9Eh9OItaPfAcR4qNnOQnvCyTeGdR5lSu0WqXiuU0
3jLlt4qAL3/VKyfbP7R0/7SUwwPpWf5YUWwzjDONEy8
-> ssh-ed25519 ofQnlg BLdBNuJExNlYED/XFU5zmYPtO1bxumuyPPgcs8qSLEY
wIi31st4WS9O2a7VJmYpE8PimgzLvwU6zWkvHCy84yQ
-> ssh-ed25519 COspvA GmC8YZVv3ZwidaDKUkLhx0l8UOmRw5ZBiM8r4/Ub7xA
wK994Zs1aLspqY84Ik77qdMaEsjs0ZFNuKQDOGXsnmM
-> ssh-ed25519 2XrTgw s62q7KOHZRqimCTwazX9LUvnpcYuzxwflumXe6NVF38
WnOpHI9ejvRrZrQuasTEYyqP8ny3Hx9Q9bJzbK0pOI4
-> ssh-ed25519 1MUEqQ 9NLHR5OwOngiLRguTkf5KnUHrc80mambCw19dPPKPQ8
GDg9yRoRUaP1KOa/pOCFiLCCAxuFsuCIiDP/ERl8YLA
-> ssh-ed25519 dgBsjw NulCMPtc2miJlHYpXMjQHUlc/HIaX4AqzxXZxt8cWkE
tCh3WD91A89258F8THeddXvab77tTIjNjGxYNDVoaBQ
--- oIUm35maOqmHL0nifKpyEvLpHSKmthxIT5DDueCVZDc
ÈaT~œú£Úôf4÷„Ô$\—ì;¿@Äý`¦<18>(<28>3{VWgM}#<1A>ï9aû6¢½ª^öúT6éÞ!Þ %¿
-> ssh-ed25519 Jpc21A TUGMeCK5ZehSdtfH2xpEEvMp4jZxJ2fTogXSkcf6blg
BY6cKeB3MV84KvYThJsU0TSW5kMUJpbVVvccVtwtEIg
-> ssh-ed25519 BAs8QA 9LgEP0N4MF4uEXLXGWRLgJzC9llYpKiP2lwAgN0ObxA
cOI90Sc2ORgvFZD2I+oYcRa1Y47X2tuvFFqqKFo78nk
-> ssh-ed25519 ofQnlg LYTsntB2zI1EJ8yyYOH6BvHXEOv+zX7QYrd7leXPeDA
uqhuPl2udjxDMIdMd88xpQuAZ/QMXSLgxOeOhHyTjW8
-> ssh-ed25519 COspvA geel/7vCPHmBT6Sg0bTqeUV4rA/i6w0NvFjGk8rF/VA
8eIF+oWuM+16j1n//ndImjTPxPYqZ56WA8K5uybMbTo
-> ssh-ed25519 2XrTgw uCKoYMidpNwfo2YDb2jyjONZvExK2wrZXXvv+ywrCCA
X0E+2LuftCF3oAHp8z18WRZePAYt6tEYjnNAgMmfUNo
-> ssh-ed25519 awJeHA 4cTdqU2X1gXyFxMHGHPgUq9g8XUM8sgbNa/NMl2GZmI
J7cWCuKrf+r/nE0FjGj/DQDv7hBqbn0NidjY2m1mwGw
-> ssh-ed25519 dgBsjw gyI/oiqvAzqxtism9yCygXYLZzCynTdAlPBcwZhryCw
pKCMRUv6dsH/cS2Tm/gU0mxH6TsF6UGj0Fx4BNvYv9o
--- MBLz1UcLi1SCQ2+tA1Zmv+2ZEiw0Ag2C4/gk7lzqJ0Q
Ú<EFBFBD>©ËƒŽ£Ð Ó1ÁÞ™òȲ=HK€Ÿ?G §g=R!Ì”2t+Ü€!“ɈUGÞ¶±ïôM°?ö<02>OÉÀÅÊ

View file

@ -1,18 +1,17 @@
age-encryption.org/v1
-> ssh-ed25519 Jpc21A yazxa1xfjFav7HWIvegBhcLIaYnQw4noH+2d9VqyCk8
LWgk9DdjH5ktQN0A9RVQN8PB2LlXsEVihwJpU4AFHDY
-> ssh-ed25519 BAs8QA XnRG+IYDe3WOBNeNXF4KGCHGVtZg3T2kXJtwmVABjVw
dfJcTZmDfL/2KuE0XRgO2UYlssaY0T2beTqUTKinhos
-> ssh-ed25519 ofQnlg bVBe7VBynJr7wdVWajiKlVkAogJCsFE+NkVQcOFV3w0
AUnc6BwNS2YPz1qFBkDGaXlEp+VzlceGllLctHhq1HM
-> ssh-ed25519 COspvA dEY4MqMIfidzNBe9tUMZTpJNwYNlq+ngp+RLNEWN4Vg
P+Yf/6NAoIFCfKOd4ykDiY62G/3TTk4jtYJkrYY/SBk
-> ssh-ed25519 2XrTgw +FkRMJR2M2KrnR9n8RMrt1m60h4piX4q6c2PuNaIyko
AdpWCLZ/zLdmYyeWAX3/lQJ6h2bMGwF84KJVLoxm4aA
-> ssh-ed25519 1MUEqQ mLU9wlidwicRLBbhSa9Adwn1t3PlMwnCVGZveFSWBEM
1tiHDboRihGgz+xaylQAdbXQqyCVT0l3xYIbF6to0pE
-> ssh-ed25519 dgBsjw 9t+XYjncpp7OPAo915xXHNfgnPsIuqL/RV1JyF/f/hk
GhuVPIyAcVy18vHG5LSREzCe/zLkZDXNNNuIwOsZ9Go
--- 66KHbxaXGMDinv6CbC2dL5vHNzR9EJdAYdzoUzN5nKI
ìŠpH:rØkMµ¨¡mU:GiæÁL#%/©ÀuÌ<75>ÒílzÙ¶/Èí9¨ù|Ô þûAѬ»2Aü{­A 1Vc
Êš€R¯€ájŒŸSéÐ¥Ò
-> ssh-ed25519 Jpc21A EvnCvH6ER6Pied87i9okxBLCx38fNP1fX3wMziJ5PXI
b7vN/9pxRWuUrug/bkZIZD+2wSplAGLK+ayO+YYDlCs
-> ssh-ed25519 BAs8QA I7dMk+pvKe/bNkIr98muyaOKqTdBitIdrRr5bVN3+DA
5D5IGeF6rqF+HNSdDZE/gj0DPHISR3Tzj3Fystm++04
-> ssh-ed25519 ofQnlg 3XMrmWBHw6+xfYIn8fZ44ryp1dHS7qVTK0cTJZGjXV0
PxuU0/w27wW/335mteby+kNTr83K6SrjvNEsxBFHOEI
-> ssh-ed25519 COspvA 5yfMt71p+fx9aIT8ubgsHZa9XEJE6trsOOqgC0VxxBY
dEIuIERcnw6dFb7IMmdzM5b4ySmdg7qtR50X5gQTd+8
-> ssh-ed25519 2XrTgw ZpXWuF2jRWpxlK9HuYZoZyN144cvaNZubVuLBUEq5kc
692sos5focQy0TnUfvz+cLktK2a6tyNS8AWbuDgb4/c
-> ssh-ed25519 awJeHA ZXx69b4ZvWbwtwnSFjK031mIpK8lDN1nwxq0N+FgREY
f7GrJm2N908qtPEYY8T22JGhfWHXbUTAdRuEFHGLOKo
-> ssh-ed25519 dgBsjw mD/FfN+J2djG/3hR98oPHznAhTyVz5Z8jnJNlQXzWSo
KULWl9PeO3qwbUNQJix/Zr07l9lsBhPZTR6aeOiyx0M
--- EUYAk4OeiL39nwZ7YIlLucUMfBghaegrtgqH+sOUEfI
<>6Q‡äŸ‡7á·™B+Òpûµe³úµ({<ˆ8í‡My|³¿]˜Ãk¾h:žæZŒ§ãde#Äü úû>3„“€K<4B>éÀ¬á®øq^uG