Files
org-garden/nix/module.nix
Ignacio Ballesteros 01805dbf39 Add service infrastructure for long-running deployment
- Add configuration system (config/*.exs, OrgGarden.Config)
- Refactor supervision tree with DynamicSupervisor and Registry
- Add OrgGarden.Server for serve mode lifecycle management
- Add health check HTTP endpoints (Bandit/Plug on :9090)
- Add telemetry events for export and watcher operations
- Implement graceful shutdown with SIGTERM handling
- Add Mix Release support with overlay scripts
- Add NixOS module for systemd service deployment
- Update documentation with service usage
2026-02-21 20:38:47 +01:00

149 lines
3.9 KiB
Nix

{ config, lib, pkgs, ... }:
let
cfg = config.services.org-garden;
in
{
options.services.org-garden = {
enable = lib.mkEnableOption "org-garden publishing service";
package = lib.mkOption {
type = lib.types.package;
description = "The org-garden package to use.";
};
notesDir = lib.mkOption {
type = lib.types.path;
description = "Path to org-roam notes directory.";
};
outputDir = lib.mkOption {
type = lib.types.path;
default = "/var/lib/org-garden";
description = "Output directory for generated content.";
};
port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = "HTTP server port.";
};
wsPort = lib.mkOption {
type = lib.types.port;
default = 3001;
description = "WebSocket hot reload port.";
};
healthPort = lib.mkOption {
type = lib.types.port;
default = 9090;
description = "Health check endpoint port.";
};
zoteroUrl = lib.mkOption {
type = lib.types.str;
default = "http://localhost:23119";
description = "Zotero Better BibTeX URL.";
};
bibtexFilePath = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Path to fallback BibTeX file.";
};
citationMode = lib.mkOption {
type = lib.types.enum [ "silent" "warn" "strict" ];
default = "warn";
description = "Citation resolution failure mode.";
};
user = lib.mkOption {
type = lib.types.str;
default = "org-garden";
description = "User to run the service as.";
};
group = lib.mkOption {
type = lib.types.str;
default = "org-garden";
description = "Group to run the service as.";
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to open the firewall for the HTTP port.";
};
};
config = lib.mkIf cfg.enable {
users.users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = cfg.outputDir;
createHome = true;
};
users.groups.${cfg.group} = { };
systemd.services.org-garden = {
description = "Org-Garden Publishing Service";
documentation = [ "https://github.com/ignacio.ballesteros/org-garden" ];
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
environment = {
NOTES_DIR = toString cfg.notesDir;
OUTPUT_DIR = cfg.outputDir;
PORT = toString cfg.port;
WS_PORT = toString cfg.wsPort;
HEALTH_PORT = toString cfg.healthPort;
ZOTERO_URL = cfg.zoteroUrl;
CITATION_MODE = cfg.citationMode;
} // lib.optionalAttrs (cfg.bibtexFile != null) {
BIBTEX_FILE = toString cfg.bibtexFile;
};
serviceConfig = {
Type = "exec";
ExecStart = "${cfg.package}/bin/org-garden serve";
Restart = "on-failure";
RestartSec = 5;
# Directories
StateDirectory = "org-garden";
WorkingDirectory = cfg.outputDir;
# User/Group
User = cfg.user;
Group = cfg.group;
# Hardening
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = "read-only";
ReadWritePaths = [ cfg.outputDir ];
ReadOnlyPaths = [ cfg.notesDir ];
PrivateTmp = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
RestrictNamespaces = true;
LockPersonality = true;
MemoryDenyWriteExecute = false; # Required for BEAM JIT
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
};
};
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ cfg.port ];
};
};
}