This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / configs / rad.nix
6.0 kB 158 lines
1{ 2 self, 3 config, 4 pkgs, 5 ... 6}: 7# Per-machine Radicle identity and node for day-to-day development 8# (rad clone/push/patch), as opposed to the public seed in ./radicle.lassul.us.nix. 9# 10# One radicle-node = one keypair = one Node ID; a node cannot serve several 11# identities. So every machine gets its own key (generator `rad`) and hence 12# its own DID, and repos that should accept pushes from all machines list each 13# machine's DID as a delegate (`rad id update --delegate did:key:<nid>`). 14# 15# Radicle has no multi-user mode: the `rad` CLI and git-remote-rad write into 16# the profile's storage themselves, signing with the key in keys/radicle. To 17# make one identity usable by several accounts the profile lives outside any 18# home, in /var/lib/rad, group-shared: the node runs as the `rad` system user, 19# everything is setgid `rad` with group rwx, and the CLI/remote-helper wrappers 20# below force umask 002 so files one side creates stay writable for the other. 21# RAD_HOME points there for login shells; membership in `rad` is what grants 22# use of the identity (mainUser is a member, add others per machine). 23# Keys and config.json are symlinks into the clan vars secret dir / nix store, 24# maintained by tmpfiles; storage and the control socket are ordinary state. 25# 26# The node only dials out (no listen address): the laptops sit behind NAT and 27# the public seed neoprism relays for them, so nothing needs a port opened. 28let 29 # nixpkgs marks radicle-node insecure: private repos are not encrypted 30 # between nodes. Clear the flag on the package we use instead of allowing 31 # it globally. 32 radicle-node = self.lib.secureify pkgs.radicle-node; 33 generator = config.clan.core.vars.generators.rad; 34 radHome = "/var/lib/rad"; 35 36 settings = { 37 publicExplorer = "https://app.radicle.xyz/nodes/$host/$rid$path"; 38 preferredSeeds = [ 39 # our own seed (configs/radicle.lassul.us.nix on neoprism) 40 "z6MkhTe9WWbqNdRAnWLHxL23gedRQWdhjajRbNAt5fCpae6o@radicle.lassul.us:8776" 41 "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.xyz:8776" 42 "z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@iris.radicle.network:8776" 43 ]; 44 cli.hints = true; 45 node = { 46 alias = config.networking.hostName; 47 listen = [ ]; 48 network = "main"; 49 log = "INFO"; 50 relay = "auto"; 51 # Only seed what this machine explicitly initialised, cloned or `rad seed`ed. 52 seedingPolicy.default = "block"; 53 }; 54 }; 55 56 # Validate with `rad config` at build time (same trick as the nixpkgs seed 57 # module) so a typo does not leave every machine's node crash-looping. 58 configFile = 59 pkgs.runCommand "rad-config.json" 60 { 61 nativeBuildInputs = [ (self.lib.secureify pkgs.buildPackages.radicle-node) ]; 62 json = builtins.toJSON settings; 63 passAsFile = [ "json" ]; 64 preferLocalBuild = true; 65 } 66 '' 67 install -D -m 644 "$jsonPath" $out 68 ln -s $out config.json 69 install -D -m 644 /dev/stdin keys/radicle.pub <<<"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBgFMhajUng+Rjj/sCFXI9PzG8BQjru2n7JgUVF1Kbv5 snakeoil" 70 RAD_HOME=$PWD rad config >/dev/null 71 ''; 72 73 # `rad` and `git-remote-rad` (what git runs for rad:// remotes) forced to 74 # umask 002; first in the join so they shadow the plain radicle-node ones. 75 wrap = 76 bin: 77 pkgs.writeShellScriptBin bin '' 78 # ${radHome} is shared with the rad-node service via group rad. 79 umask 002 80 exec ${radicle-node}/bin/${bin} "$@" 81 ''; 82 radicle-shared = pkgs.symlinkJoin { 83 name = "radicle-shared"; 84 paths = [ 85 (wrap "rad") 86 (wrap "git-remote-rad") 87 radicle-node 88 ]; 89 }; 90in 91{ 92 clan.core.vars.generators.rad = { 93 files."radicle.key" = { 94 owner = "rad"; 95 group = "rad"; 96 mode = "0440"; 97 }; 98 files."radicle.pub".secret = false; 99 runtimeInputs = [ pkgs.openssh ]; 100 script = '' 101 ssh-keygen -t ed25519 -N "" -C "radicle ${config.networking.hostName}" -f "$out/radicle.key" 102 mv "$out/radicle.key.pub" "$out/radicle.pub" 103 ''; 104 }; 105 106 users.users.rad = { 107 isSystemUser = true; 108 group = "rad"; 109 home = radHome; 110 }; 111 users.groups.rad = { }; 112 users.users.mainUser.extraGroups = [ "rad" ]; 113 114 environment.systemPackages = [ radicle-shared ]; 115 environment.variables.RAD_HOME = radHome; 116 117 systemd.tmpfiles.rules = [ 118 "d ${radHome} 2770 rad rad -" 119 "d ${radHome}/cobs 2770 rad rad -" 120 "d ${radHome}/keys 2770 rad rad -" 121 "d ${radHome}/node 2770 rad rad -" 122 "d ${radHome}/storage 2770 rad rad -" 123 "L+ ${radHome}/keys/radicle - - - - ${generator.files."radicle.key".path}" 124 "L+ ${radHome}/keys/radicle.pub - - - - ${generator.files."radicle.pub".path}" 125 "L+ ${radHome}/config.json - - - - ${configFile}" 126 # SQLite creates databases 0644 regardless of umask, which would lock the 127 # second party out ("attempt to write a readonly database"). Pre-create 128 # them empty and group-writable (an empty file is a valid empty database; 129 # -wal/-shm copy the main file's mode). Adjusts mode on every boot too. 130 "f ${radHome}/cobs/cache.db 0664 rad rad -" 131 "f ${radHome}/node/node.db 0664 rad rad -" 132 "f ${radHome}/node/notifications.db 0664 rad rad -" 133 "f ${radHome}/node/policies.db 0664 rad rad -" 134 ]; 135 136 systemd.services.rad-node = { 137 description = "Radicle node (machine identity, ${radHome})"; 138 documentation = [ "man:radicle-node(1)" ]; 139 wantedBy = [ "multi-user.target" ]; 140 wants = [ "network-online.target" ]; 141 after = [ "network-online.target" ]; 142 environment = { 143 # rad stats $HOME/.gitconfig 144 HOME = radHome; 145 RAD_HOME = radHome; 146 }; 147 path = [ pkgs.gitMinimal ]; 148 serviceConfig = { 149 User = "rad"; 150 Group = "rad"; 151 UMask = "0002"; 152 # --force: reclaim a control socket left behind by an unclean shutdown. 153 ExecStart = "${radicle-node}/bin/radicle-node --force"; 154 Restart = "on-failure"; 155 RestartSec = 5; 156 }; 157 }; 158}