Rename grapho to pal, add onboard command, fix interactive input
- Rename grapho → pal across entire codebase (CLI, NixOS module, flake, docs, config paths) - Add `pal onboard` interactive setup wizard with 4 steps: device, config repo, sync, backups - Shows current setup summary when re-running onboard on an existing installation with warnings about what will change - Fix askConfirm/askInput to read from /dev/tty so interactive prompts work correctly - Remove || operator usage in askConfirm (not reliable in Lux) - Add pal package to nix develop shell - Document ~/.config/pal/ directory structure in README Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,39 +1,39 @@
|
||||
# Grapho Module
|
||||
# Pal Module
|
||||
#
|
||||
# Unified personal data infrastructure module for NixOS.
|
||||
# Sets up Syncthing (isolated) + Restic backup + directory structure.
|
||||
#
|
||||
# Usage:
|
||||
# services.grapho.enable = true;
|
||||
# services.grapho.user = "youruser";
|
||||
# services.pal.enable = true;
|
||||
# services.pal.user = "youruser";
|
||||
#
|
||||
# This creates:
|
||||
# - ~/.config/grapho/ directory structure
|
||||
# - ~/.config/pal/ directory structure
|
||||
# - Isolated Syncthing on port 8385 (separate from system Syncthing)
|
||||
# - Restic backup timer for grapho data
|
||||
# - Restic backup timer for pal data
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.grapho;
|
||||
cfg = config.services.pal;
|
||||
home = config.users.users.${cfg.user}.home;
|
||||
graphoDir = "${home}/.config/grapho";
|
||||
palDir = "${home}/.config/pal";
|
||||
in {
|
||||
options.services.grapho = {
|
||||
enable = mkEnableOption "grapho personal data infrastructure";
|
||||
options.services.pal = {
|
||||
enable = mkEnableOption "pal personal data infrastructure";
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
description = "User to run grapho services as.";
|
||||
description = "User to run pal services as.";
|
||||
example = "alice";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "users";
|
||||
description = "Group to run grapho services as.";
|
||||
description = "Group to run pal services as.";
|
||||
};
|
||||
|
||||
# Syncthing options
|
||||
@@ -41,7 +41,7 @@ in {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable grapho's isolated Syncthing instance.";
|
||||
description = "Enable pal's isolated Syncthing instance.";
|
||||
};
|
||||
|
||||
guiPort = mkOption {
|
||||
@@ -65,7 +65,7 @@ in {
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Open firewall for grapho's Syncthing ports.";
|
||||
description = "Open firewall for pal's Syncthing ports.";
|
||||
};
|
||||
|
||||
devices = mkOption {
|
||||
@@ -120,14 +120,14 @@ in {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable restic backup of grapho data.";
|
||||
description = "Enable restic backup of pal data.";
|
||||
};
|
||||
|
||||
repository = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = "Restic repository location (e.g., 'sftp:server:/backups/grapho').";
|
||||
example = "sftp:backup-server:/backups/grapho";
|
||||
description = "Restic repository location (e.g., 'sftp:server:/backups/pal').";
|
||||
example = "sftp:backup-server:/backups/pal";
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
@@ -195,27 +195,27 @@ in {
|
||||
isNormalUser = true;
|
||||
};
|
||||
|
||||
# Create grapho directory structure
|
||||
# Create pal directory structure
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${graphoDir} 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/config-repo 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/syncthing/config 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/syncthing/db 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/sync 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/sync/notes 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/sync/documents 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/sync/dotfiles 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/restic/cache 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${graphoDir}/server 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir} 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/config-repo 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/syncthing/config 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/syncthing/db 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/sync 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/sync/notes 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/sync/documents 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/sync/dotfiles 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/restic/cache 0755 ${cfg.user} ${cfg.group} -"
|
||||
"d ${palDir}/server 0755 ${cfg.user} ${cfg.group} -"
|
||||
];
|
||||
|
||||
# Isolated Syncthing for grapho
|
||||
# Isolated Syncthing for pal
|
||||
services.syncthing = mkIf cfg.syncthing.enable {
|
||||
enable = true;
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
dataDir = "${graphoDir}/sync";
|
||||
configDir = "${graphoDir}/syncthing/config";
|
||||
dataDir = "${palDir}/sync";
|
||||
configDir = "${palDir}/syncthing/config";
|
||||
guiAddress = "127.0.0.1:${toString cfg.syncthing.guiPort}";
|
||||
|
||||
overrideDevices = true;
|
||||
@@ -228,24 +228,24 @@ in {
|
||||
}) cfg.syncthing.devices;
|
||||
|
||||
folders = {
|
||||
# Default grapho folders
|
||||
"grapho-notes" = {
|
||||
path = "${graphoDir}/sync/notes";
|
||||
id = "grapho-notes";
|
||||
# Default pal folders
|
||||
"pal-notes" = {
|
||||
path = "${palDir}/sync/notes";
|
||||
id = "pal-notes";
|
||||
devices = attrNames cfg.syncthing.devices;
|
||||
type = "sendreceive";
|
||||
fsWatcherEnabled = true;
|
||||
};
|
||||
"grapho-documents" = {
|
||||
path = "${graphoDir}/sync/documents";
|
||||
id = "grapho-documents";
|
||||
"pal-documents" = {
|
||||
path = "${palDir}/sync/documents";
|
||||
id = "pal-documents";
|
||||
devices = attrNames cfg.syncthing.devices;
|
||||
type = "sendreceive";
|
||||
fsWatcherEnabled = true;
|
||||
};
|
||||
"grapho-dotfiles" = {
|
||||
path = "${graphoDir}/sync/dotfiles";
|
||||
id = "grapho-dotfiles";
|
||||
"pal-dotfiles" = {
|
||||
path = "${palDir}/sync/dotfiles";
|
||||
id = "pal-dotfiles";
|
||||
devices = attrNames cfg.syncthing.devices;
|
||||
type = "sendreceive";
|
||||
fsWatcherEnabled = true;
|
||||
@@ -271,15 +271,15 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
# Firewall for grapho Syncthing
|
||||
# Firewall for pal Syncthing
|
||||
networking.firewall = mkIf (cfg.syncthing.enable && cfg.syncthing.openFirewall) {
|
||||
allowedTCPPorts = [ cfg.syncthing.syncPort ];
|
||||
allowedUDPPorts = [ cfg.syncthing.syncPort cfg.syncthing.discoveryPort ];
|
||||
};
|
||||
|
||||
# Restic backup service
|
||||
systemd.services.grapho-backup = mkIf (cfg.backup.enable && cfg.backup.repository != "") {
|
||||
description = "Grapho data backup";
|
||||
systemd.services.pal-backup = mkIf (cfg.backup.enable && cfg.backup.repository != "") {
|
||||
description = "Pal data backup";
|
||||
wants = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
|
||||
@@ -288,18 +288,18 @@ in {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
ExecStart = let
|
||||
paths = [ "${graphoDir}/sync" ] ++ cfg.backup.extraPaths;
|
||||
paths = [ "${palDir}/sync" ] ++ cfg.backup.extraPaths;
|
||||
pathArgs = concatMapStringsSep " " (p: "'${p}'") paths;
|
||||
in ''
|
||||
${pkgs.restic}/bin/restic backup \
|
||||
--cache-dir ${graphoDir}/restic/cache \
|
||||
--cache-dir ${palDir}/restic/cache \
|
||||
${optionalString (cfg.backup.passwordFile != null) "--password-file ${cfg.backup.passwordFile}"} \
|
||||
-r ${cfg.backup.repository} \
|
||||
${pathArgs}
|
||||
'';
|
||||
ExecStartPost = ''
|
||||
${pkgs.restic}/bin/restic forget \
|
||||
--cache-dir ${graphoDir}/restic/cache \
|
||||
--cache-dir ${palDir}/restic/cache \
|
||||
${optionalString (cfg.backup.passwordFile != null) "--password-file ${cfg.backup.passwordFile}"} \
|
||||
-r ${cfg.backup.repository} \
|
||||
${concatStringsSep " " cfg.backup.pruneOpts}
|
||||
@@ -307,8 +307,8 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.grapho-backup = mkIf (cfg.backup.enable && cfg.backup.repository != "") {
|
||||
description = "Grapho backup timer";
|
||||
systemd.timers.pal-backup = mkIf (cfg.backup.enable && cfg.backup.repository != "") {
|
||||
description = "Pal backup timer";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = cfg.backup.schedule;
|
||||
@@ -318,7 +318,7 @@ in {
|
||||
};
|
||||
|
||||
# Server mount (NFS)
|
||||
fileSystems."${graphoDir}/server" = mkIf (cfg.server.enable && cfg.server.type == "nfs" && cfg.server.host != "") {
|
||||
fileSystems."${palDir}/server" = mkIf (cfg.server.enable && cfg.server.type == "nfs" && cfg.server.host != "") {
|
||||
device = "${cfg.server.host}:${cfg.server.remotePath}";
|
||||
fsType = "nfs";
|
||||
options = [
|
||||
@@ -330,7 +330,7 @@ in {
|
||||
];
|
||||
};
|
||||
|
||||
# Install grapho CLI and dependencies
|
||||
# Install pal CLI and dependencies
|
||||
environment.systemPackages = with pkgs; [
|
||||
syncthing
|
||||
restic
|
||||
Reference in New Issue
Block a user