diff --git a/config/machines/example-machine/configuration.nix b/config/machines/example-machine/configuration.nix index 26514b4..c48bd02 100644 --- a/config/machines/example-machine/configuration.nix +++ b/config/machines/example-machine/configuration.nix @@ -1,13 +1,12 @@ { config, pkgs, ... }: - +let + domain = "example.com"; +in { - # Import shared configuration and machine-specific modules imports = [ - # Import shared.nix with your domain parameter - # Replace "example.com" with your actual domain + { _module.args = { inherit domain; }; } (import /var/src/config-shared { - inherit config pkgs; - domain = "example.com"; + inherit config pkgs domain; }) # Import hardware-specific configuration diff --git a/config/machines/example-machine/example-service.nix b/config/machines/example-machine/example-service.nix index 283b697..0808f1d 100644 --- a/config/machines/example-machine/example-service.nix +++ b/config/machines/example-machine/example-service.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, domain, ... }: { # Example: WireGuard VPN Service diff --git a/config/shared.nix b/config/shared.nix index 20eeed5..2b23604 100644 --- a/config/shared.nix +++ b/config/shared.nix @@ -2,8 +2,10 @@ { imports = [ + # Note: 'domain' is made available via _module.args in the machine's configuration.nix + # It's passed to this module and propagated to all imports automatically + /var/src/config-nginx - { _module.args = { inherit domain; }; } # passes for nginx virtualHosts /var/src/config-pict-rs /var/src/config-lnbits ]; diff --git a/docs/lnbits-flake-explanation.md b/docs/lnbits-flake-explanation.md new file mode 100644 index 0000000..78c8f68 --- /dev/null +++ b/docs/lnbits-flake-explanation.md @@ -0,0 +1,264 @@ +# How the LNBits Flake Works + +## Overview + +This document explains how the LNBits flake deployment works, particularly how it achieves the equivalent of running `uv run lnbits` on the deployed NixOS machine. + +## Architecture + +The LNBits flake uses `uv2nix` to convert `uv`'s lock file into a reproducible Nix build, creating a Python virtual environment that can be deployed as a NixOS service. + +## Key Components + +### 1. uv2nix: Converting uv.lock to Nix + +The flake uses `uv2nix` to read the `uv.lock` file and create a reproducible Nix build: + +```nix +# Read uv.lock and pyproject.toml +workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; + +# Create overlay preferring wheels (faster than building from source) +uvLockedOverlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; }; +``` + +This converts the uv-managed dependencies into Nix packages. + +### 2. Building a Python Virtual Environment + +Instead of `uv` creating a venv at runtime, Nix creates one during the build: + +```nix +# Build venv with all dependencies from uv.lock +runtimeVenv = pythonSet.mkVirtualEnv "${projectName}-env" workspace.deps.default; +``` + +This creates an immutable virtual environment in `/nix/store/...-lnbits-env` with all Python packages installed from the locked dependencies. + +### 3. The Wrapper Script (Equivalent to `uv run`) + +The flake creates a wrapper that mimics `uv run lnbits`: + +```nix +lnbitsApp = pkgs.writeShellApplication { + name = "lnbits"; + text = '' + export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt + export REQUESTS_CA_BUNDLE=$SSL_CERT_FILE + export PYTHONPATH="$PWD:${PYTHONPATH:-}" + exec ${runtimeVenv}/bin/lnbits "$@" + ''; +}; +``` + +This wrapper: +- Sets up SSL certificates for HTTPS requests +- Adds the current directory to `PYTHONPATH` (so it can find local source files) +- Executes the `lnbits` binary from the built venv + +### 4. The NixOS Service Module + +The service module (`lnbits-service.nix`) configures systemd to run LNBits: + +```nix +# The actual command that runs +ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.port} --host ${cfg.host}"; + +# Environment variables +environment = { + LNBITS_DATA_FOLDER = "${cfg.stateDir}"; # /var/lib/lnbits + LNBITS_EXTENSIONS_PATH = "${cfg.stateDir}/extensions"; + LNBITS_PATH = "${cfg.package.src}"; # Points to source +} +``` + +Key points: +- `cfg.package` = the venv from the flake (`runtimeVenv`) +- `lib.getExe cfg.package` = extracts the executable path: `/nix/store/xxx-lnbits-env/bin/lnbits` +- `cfg.package.src` = points back to the LNBits source directory for templates/static files + +### 5. Flake Outputs + +The flake exposes the venv as a package: + +```nix +packages.default = runtimeVenv; +packages.${projectName} = runtimeVenv; # packages.lnbits = runtimeVenv +``` + +## How Your Deployment Uses It + +In your `config/lnbits.nix`: + +```nix +package = (builtins.getFlake "path:/var/src/lnbits-src").packages.${pkgs.system}.lnbits; +``` + +This breaks down as: + +1. `builtins.getFlake "path:/var/src/lnbits-src"` - Loads the flake from the deployed source +2. `.packages` - Accesses the packages output from the flake +3. `.${pkgs.system}` - Selects the right system architecture (e.g., `x86_64-linux`) +4. `.lnbits` - Gets the `lnbits` package (which equals `runtimeVenv`) + +## Understanding Flake References + +The **flake reference format** is crucial to understanding how this works: + +### Local Path Reference + +```nix +builtins.getFlake "path:/var/src/lnbits-src" +``` + +- Uses files from the local filesystem at `/var/src/lnbits-src` +- The `.src` attribute points to `/var/src/lnbits-src` +- Files are mutable - you can edit them +- Requires deploying the full source tree via krops + +### GitHub Reference + +```nix +builtins.getFlake "github:lnbits/lnbits/main" +``` + +- Nix fetches the repository from GitHub +- Stores it in `/nix/store/xxx-source/` (read-only) +- The `.src` attribute points to `/nix/store/xxx-source` +- Files are immutable +- No need to deploy source separately + +### Comparison + +| Aspect | `path:/var/src/lnbits-src` | `github:lnbits/lnbits` | +|--------|---------------------------|------------------------| +| **Source location** | `/var/src/lnbits-src` | `/nix/store/xxx-source` | +| **Mutable?** | Yes - can edit files | No - read-only | +| **Deployment** | Deploy via krops | Built-in to Nix | +| **Updates** | Redeploy source | Change flake ref | +| **Local changes** | Supported | Not possible | + +## Why Deploy the Full Source? + +The entire `lnbits` folder must be copied to `/var/src/lnbits-src` because: + +### 1. Build Time Requirements + +The flake needs these files to build the venv: +- `flake.nix` - Defines how to build the venv +- `uv.lock` - Contains locked dependency versions +- `pyproject.toml` - Defines project metadata + +### 2. Runtime Requirements + +LNBits needs the source tree at runtime for: +- Python modules in `lnbits/` +- HTML templates +- Static files (CSS, JavaScript, images) +- Extension loading system + +## Directory Structure + +### On the Deployed Machine + +``` +/var/src/lnbits-src/ ← Full source deployed by krops +├── flake.nix ← Used to build venv +├── uv.lock ← Used to build venv +├── pyproject.toml ← Used to build venv +└── lnbits/ ← Used at runtime + ├── templates/ + ├── static/ + └── ... + +/nix/store/xxx-lnbits-env/ ← Built venv (Python packages only) +├── bin/lnbits ← Executable +└── lib/python3.12/... ← Dependencies +``` + +### At Runtime + +The systemd service: +- Runs: `/nix/store/xxx-lnbits-env/bin/lnbits` +- With: `LNBITS_PATH=/var/src/lnbits-src` (to find templates/static/etc) +- With: `WorkingDirectory=/var/src/lnbits-src` + +## Comparison: `uv run` vs Nix Flake + +### Traditional `uv run lnbits` + +```bash +cd /path/to/lnbits +uv run lnbits --port 5000 --host 0.0.0.0 +``` + +This: +1. Reads `uv.lock` +2. Creates/updates a venv in `.venv/` +3. Installs dependencies if needed +4. Runs `lnbits` from the venv +5. Uses current directory for source files + +### Nix Flake Approach + +```nix +package = (builtins.getFlake "path:/var/src/lnbits-src").packages.${pkgs.system}.lnbits; +``` + +This: +1. ✅ Reads `uv.lock` via `uv2nix` +2. ✅ Creates a venv in `/nix/store` (immutable) +3. ✅ All dependencies are locked and reproducible +4. ✅ Runs `/nix/store/xxx-lnbits-env/bin/lnbits` +5. ✅ Sets `LNBITS_PATH` to source directory for templates/static/extensions +6. ✅ Runs as a systemd service with proper user/permissions +7. ✅ No runtime dependency on `uv` itself + +### Key Differences + +| Aspect | `uv run` | Nix Flake | +|--------|----------|-----------| +| **Venv location** | `.venv/` in source | `/nix/store/xxx-env` | +| **Mutability** | Mutable | Immutable | +| **Reproducibility** | Lock file only | Full Nix derivation | +| **Service management** | Manual | systemd integration | +| **Dependency on uv** | Required at runtime | Only at build time | + +## The `.src` Attribute Mystery + +A common question: where is `cfg.package.src` defined? + +### Answer: It's Automatic + +The `.src` attribute is **not explicitly defined** - it's automatically set by Nix when loading a flake: + +```nix +# When you do this: +builtins.getFlake "path:/var/src/lnbits-src" + +# Nix automatically: +# 1. Reads the flake at /var/src/lnbits-src +# 2. Evaluates it and builds outputs +# 3. Adds .src = /var/src/lnbits-src to the package +``` + +This is a built-in Nix flake feature - packages inherit the source location from where the flake was loaded. + +## Summary + +The LNBits flake deployment: + +1. **Converts uv dependencies to Nix** using `uv2nix` +2. **Builds an immutable venv** in `/nix/store` +3. **Deploys full source** to `/var/src/lnbits-src` via krops +4. **Loads the flake** from the deployed source using `path:/var/src/lnbits-src` +5. **Runs as a systemd service** with proper environment variables pointing to the source + +This provides: +- ✅ **Reproducibility** - exact same dependencies every time +- ✅ **Declarative configuration** - everything in `configuration.nix` +- ✅ **Source mutability** - can edit files in `/var/src/lnbits-src` +- ✅ **No uv dependency** - service doesn't need `uv` at runtime +- ✅ **Proper service management** - systemd integration with user permissions + +The key insight is that **`path:` vs `github:` in the flake reference** determines whether you use local deployed files or Nix fetches from a remote repository.