swarm-archive serves restic repositories with rclone, append-only, one login and one repository per agent. swarm-controller writes each placed agent's login and restic password to the store, replaces them on a move and removes them when the agent is destroyed. Every hive backs its agents up hourly under its own store identity. The archive host prunes by retention.
129 lines
4.9 KiB
Nix
129 lines
4.9 KiB
Nix
# Glue: where the store and one of its readers share a host, the reader waits
|
|
# for the unit that writes the cert-auth role it logs in with, and for any
|
|
# other policy unit whose rules it relies on.
|
|
#
|
|
# ONE PAIRING PER FILE — the store's policy units ← the readers that rely
|
|
# on them, and nothing else. Deleting this leaves every reader as it is on a
|
|
# host whose store is remote: it may log in before its role exists, and its own
|
|
# retries are what carry it past that.
|
|
#
|
|
# ⚠️ Gated on BOTH the store and that reader being here. Off the store's host
|
|
# there is no local policy unit to order against. On the store's host without
|
|
# the reader, setting `systemd.services.<reader>.after` would define a unit
|
|
# with no ExecStart, so each gate below restates the one the reader's own
|
|
# module puts on it. A reader whose gate changes must change here too.
|
|
#
|
|
# Ordering, never a requirement: a policy unit that failed still counts as
|
|
# done, and the reader's own retries carry it past that. `wants` as well as
|
|
# `after`, so a reader started on its own pulls its policy unit into the same
|
|
# transaction.
|
|
{
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
hyperhiveCfg = config.services.hyperhive;
|
|
deployCfg = hyperhiveCfg.deploy;
|
|
baoDeploy = deployCfg.bao;
|
|
|
|
havePair = cert: key: cert != null && key != null;
|
|
|
|
# Reader unit → the gate its own module defines it under.
|
|
readersHere = {
|
|
# ./swarm-grafana.nix
|
|
swarm-bao-grafana-oidc = deployCfg.grafana.enable;
|
|
# ./swarm-otel.nix
|
|
swarm-bao-otel-oidc =
|
|
deployCfg.swarm-otel.enable
|
|
&& havePair baoDeploy.otelOidcClientCertFile baoDeploy.otelOidcClientKeyFile;
|
|
# ./swarm-bao.nix: its block is gated on the store, which `orderAfterPolicies`
|
|
# already checks.
|
|
swarm-bao-forwarder-oidc = true;
|
|
# ./swarm-nats.nix: the queue's TLS leaf, not a secret, but the same wait.
|
|
swarm-bao-nats-tls = deployCfg.nats.enable;
|
|
# ./swarm-ci-runner.nix
|
|
swarm-bao-ci-runner = deployCfg.ci-runner.enable;
|
|
};
|
|
|
|
# Readers that log in under another unit's principal: reader unit → the
|
|
# gate its own module defines it under, and the policy units it waits for.
|
|
readersOfOtherPolicy = {
|
|
# ./swarm-nats.nix, under the queue responder's leaf. Also after the
|
|
# publisher's policy, which denies the publisher the callout seeds' paths:
|
|
# until it is written the publisher may create them, and this unit keeps
|
|
# a seed it finds there.
|
|
swarm-bao-nats-callout-xkey = {
|
|
here = deployCfg.nats.enable;
|
|
policies = [
|
|
"swarm-bao-nats-auth-policy"
|
|
"swarm-bao-secret-publisher-policy"
|
|
];
|
|
};
|
|
# ./swarm-grafana.nix, under Grafana's leaf, which may create the admin
|
|
# password, the metrics bearer, the datasource key and the controller's
|
|
# Grafana token only once that policy is written.
|
|
swarm-bao-grafana-admin = {
|
|
here = deployCfg.grafana.enable;
|
|
policies = [ "swarm-bao-grafana-oidc-policy" ];
|
|
};
|
|
swarm-bao-grafana-metrics-bearer = {
|
|
here = deployCfg.grafana.enable;
|
|
policies = [ "swarm-bao-grafana-oidc-policy" ];
|
|
};
|
|
swarm-bao-grafana-secret-key = {
|
|
here = deployCfg.grafana.enable;
|
|
policies = [ "swarm-bao-grafana-oidc-policy" ];
|
|
};
|
|
swarm-bao-grafana-controller-token = {
|
|
here = deployCfg.grafana.enable;
|
|
policies = [ "swarm-bao-grafana-oidc-policy" ];
|
|
};
|
|
# ./swarm-attic.nix, under the cache's leaf, which may create the signing
|
|
# key, the two tokens and the cache's public facts only once that policy is
|
|
# written.
|
|
swarm-bao-attic-secret = {
|
|
here = deployCfg.attic.enable;
|
|
policies = [ "swarm-bao-attic-policy" ];
|
|
};
|
|
swarm-bao-attic-tokens = {
|
|
here = deployCfg.attic.enable;
|
|
policies = [ "swarm-bao-attic-policy" ];
|
|
};
|
|
# ./swarm-archive.nix, under the archive's leaf, which reads the users and the
|
|
# repositories' passwords only once that policy is written.
|
|
swarm-archive-users = {
|
|
here = deployCfg.archive.enable;
|
|
policies = [ "swarm-bao-archive-policy" ];
|
|
};
|
|
swarm-archive-prune = {
|
|
here = deployCfg.archive.enable;
|
|
policies = [ "swarm-bao-archive-policy" ];
|
|
};
|
|
# ./hive-matrix.nix, under matrix-ctl's leaf.
|
|
swarm-bao-matrix-oidc = {
|
|
here = deployCfg.matrix.enable;
|
|
policies = [ "swarm-bao-matrix-ctl-policy" ];
|
|
};
|
|
# ./hive-forge/default.nix, under the forge publisher's leaf.
|
|
swarm-bao-forge-oidc = {
|
|
here = deployCfg.forgejo.enable;
|
|
policies = [ "swarm-bao-forge-publisher-policy" ];
|
|
};
|
|
};
|
|
|
|
orderAfterPolicies =
|
|
reader: here: policies:
|
|
lib.mkIf (baoDeploy.enable && here) {
|
|
systemd.services.${reader} = {
|
|
after = map (p: "${p}.service") policies;
|
|
wants = map (p: "${p}.service") policies;
|
|
};
|
|
};
|
|
in
|
|
{
|
|
config = lib.mkMerge (
|
|
lib.mapAttrsToList (reader: here: orderAfterPolicies reader here [ "${reader}-policy" ]) readersHere
|
|
++ lib.mapAttrsToList (reader: r: orderAfterPolicies reader r.here r.policies) readersOfOtherPolicy
|
|
);
|
|
}
|