{ config, lib, pkgs, ... }: with lib; let inherit (pkgs) ipfs runCommand makeWrapper; cfg = config.services.ipfs; ipfsFlags = toString ([ (optionalString cfg.autoMount "--mount") #(optionalString cfg.autoMigrate "--migrate") (optionalString cfg.enableGC "--enable-gc") (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false") (optionalString (cfg.defaultMode == "offline") "--offline") (optionalString (cfg.defaultMode == "norouting") "--routing=none") ] ++ cfg.extraFlags); defaultDataDir = if versionAtLeast config.system.stateVersion "17.09" then "/var/lib/ipfs" else "/var/lib/ipfs/.ipfs"; # Wrapping the ipfs binary with the environment variable IPFS_PATH set to dataDir because we can't set it in the user environment wrapped = runCommand "ipfs" { buildInputs = [ makeWrapper ]; } '' mkdir -p "$out/bin" makeWrapper "${ipfs}/bin/ipfs" "$out/bin/ipfs" \ --set IPFS_PATH ${cfg.dataDir} \ --prefix PATH : /run/wrappers/bin ''; commonEnv = { environment.IPFS_PATH = cfg.dataDir; path = [ wrapped ]; serviceConfig.User = cfg.user; serviceConfig.Group = cfg.group; }; baseService = recursiveUpdate commonEnv { wants = [ "ipfs-init.service" ]; # NB: migration must be performed prior to pre-start, else we get the failure message! preStart = '' ipfs repo fsck # workaround for BUG #4212 (https://github.com/ipfs/go-ipfs/issues/4214) ipfs --local config Addresses.API ${cfg.apiAddress} ipfs --local config Addresses.Gateway ${cfg.gatewayAddress} '' + optionalString cfg.autoMount '' ipfs --local config Mounts.FuseAllowOther --json true ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir} ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir} '' + concatStringsSep "\n" (collect isString (mapAttrsRecursive (path: value: # Using heredoc below so that the value is never improperly quoted '' read value <ipfs config, every time the daemon starts. These are applied last, so may override configuration set by other options in this module. Keep in mind that this configuration is stateful; i.e., unsetting anything in here does not reset the value to the default! ''; default = {}; example = { Datastore.StorageMax = "100GB"; Discovery.MDNS.Enabled = false; Bootstrap = [ "/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu" "/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm" ]; Swarm.AddrFilters = null; }; }; extraFlags = mkOption { type = types.listOf types.str; description = "Extra flags passed to the IPFS daemon"; default = []; }; serviceFdlimit = mkOption { type = types.nullOr types.int; default = null; description = "The fdlimit for the IPFS systemd unit or null to have the daemon attempt to manage it"; example = 64*1024; }; }; }; ###### implementation config = mkIf cfg.enable { environment.systemPackages = [ wrapped ]; environment.etc."fuse.conf" = mkIf cfg.autoMount { text = '' user_allow_other ''; }; users.extraUsers = mkIf (cfg.user == "ipfs") { ipfs = { group = cfg.group; home = cfg.dataDir; createHome = false; uid = config.ids.uids.ipfs; description = "IPFS daemon user"; }; }; users.extraGroups = mkIf (cfg.group == "ipfs") { ipfs.gid = config.ids.gids.ipfs; }; systemd.services.ipfs-init = recursiveUpdate commonEnv { description = "IPFS Initializer"; after = [ "local-fs.target" ]; before = [ "ipfs.service" "ipfs-offline.service" "ipfs-norouting.service" ]; preStart = '' install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.dataDir} '' + optionalString cfg.autoMount '' install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.ipfsMountDir} install -m 0755 -o ${cfg.user} -g ${cfg.group} -d ${cfg.ipnsMountDir} ''; script = '' if [[ ! -f ${cfg.dataDir}/config ]]; then ipfs init ${optionalString cfg.emptyRepo "-e"} fi ''; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; PermissionsStartOnly = true; }; }; # TODO These 3 definitions possibly be further abstracted through use of a function # like: mutexServices "ipfs" [ "", "offline", "norouting" ] { ... shared conf here ... } systemd.services.ipfs = recursiveUpdate baseService { description = "IPFS Daemon"; wantedBy = mkIf (cfg.defaultMode == "online") [ "multi-user.target" ]; after = [ "network.target" "local-fs.target" "ipfs-init.service" ]; conflicts = [ "ipfs-offline.service" "ipfs-norouting.service"]; }; systemd.services.ipfs-offline = recursiveUpdate baseService { description = "IPFS Daemon (offline mode)"; wantedBy = mkIf (cfg.defaultMode == "offline") [ "multi-user.target" ]; after = [ "local-fs.target" "ipfs-init.service" ]; conflicts = [ "ipfs.service" "ipfs-norouting.service"]; }; systemd.services.ipfs-norouting = recursiveUpdate baseService { description = "IPFS Daemon (no routing mode)"; wantedBy = mkIf (cfg.defaultMode == "norouting") [ "multi-user.target" ]; after = [ "local-fs.target" "ipfs-init.service" ]; conflicts = [ "ipfs.service" "ipfs-offline.service"]; }; }; }