diff --git a/docs/how-to/authentik/authentik-python-backend-derivation.md b/docs/how-to/authentik/authentik-python-backend-derivation.md index 6ff6d27..6704f6f 100644 --- a/docs/how-to/authentik/authentik-python-backend-derivation.md +++ b/docs/how-to/authentik/authentik-python-backend-derivation.md @@ -20,26 +20,28 @@ Authentik 2026.2.0 requires Python 3.14 (`requires-python = "==3.14.*"`). The ni Instead of carrying individual overrides for each broken package, we use **`uv`** to install Python dependencies from PyPI, where upstream maintainers have already published Python 3.14-compatible wheels. Nix provides only the Python interpreter and system libraries. -## Approach: uv + fixed-output derivation +## Approach: uv sync FOD + autoPatchelfHook Nix builds are sandboxed with no network access. The pattern is: -1. **Fixed-output derivation (FOD)** — downloads all wheels/sdists from PyPI. FODs are allowed network access because the output hash is declared upfront (like `fetchurl`). Locked by authentik's `uv.lock`. -2. **Main derivation** — builds a Python venv from the downloaded packages using `uv pip install --find-links --no-index`. No network needed. +1. **Fixed-output derivation (FOD)** — `uv sync --frozen` fetches and installs all dependencies into a venv. FODs are allowed network access because the output hash is declared upfront. Compiled `.so` files reference Nix store paths (RPATHs to libxml2, krb5, etc.), which FODs must not contain, so we strip references with `remove-references-to` and delete `bin/` and `.pyc` files. +2. **Main derivation** — copies the FOD's `lib/python3.14/site-packages/`, recreates `bin/` with proper python symlinks, restores `pyvenv.cfg`, and runs `autoPatchelfHook` to re-link `.so` files against the correct Nix store libraries. -This aligns with how authentik upstream builds (they use `uv` as their package manager). +**Why not `uv pip download` + `uv pip install --no-index`?** `uv pip download` does not exist in uv 0.9.29 (nixpkgs). And the download-only approach has further complications with sdist-only packages (psycopg-c, gssapi) that must be compiled anyway. ## What to Do -1. Create a FOD that runs `uv pip download` to fetch all Python dependencies locked by `uv.lock` -2. Create the main derivation that: - - Sets up a Python 3.14 venv via `uv` - - Installs all dependencies from the FOD (offline, `--no-index`) - - Installs the 4 in-tree packages from the monorepo source (`ak-guardian`, `django-channels-postgres`, `django-dramatiq-postgres`, `django-postgres-cache`) - - Installs authentik itself -3. Apply `substituteInPlace` patches for Nix store paths in `settings.py`, `default.yml`, `email/utils.py`, `files/backends/file.py` -4. Copy lifecycle scripts, `manage.py`, blueprints, and web assets into the output -5. Verify: `python -c "import authentik"` succeeds +1. Create the FOD (`python-deps.nix`) that runs `uv sync --frozen --no-install-project --no-install-workspace --no-dev`, then strips all Nix store references from the output +2. Create the main derivation (`authentik-django.nix`) that: + - Copies the FOD's site-packages + - Recreates venv `bin/` and `pyvenv.cfg` + - Runs `autoPatchelfHook` to restore `.so` RPATHs + - Copies 4 in-tree workspace packages directly into site-packages + - Copies `authentik/` and `lifecycle/` into site-packages + - Copies `opencontainers` from `fetchFromGitHub` into site-packages +3. Apply `substituteInPlace` patches for Nix store paths in `settings.py`, `default.yml`, `email/utils.py` +4. Copy lifecycle scripts, `manage.py`, blueprints into the output +5. Verify: `$out/bin/python3.14 -c "import authentik"` succeeds ## Key Details @@ -50,6 +52,21 @@ This aligns with how authentik upstream builds (they use `uv` as their package m - The 4 in-tree packages are installed from monorepo source, not PyPI - Standard `djangorestframework` 3.16.1 from PyPI (no longer forked as of 2026.2.0) +## Lessons Learned (WIP) + +Build issues encountered and resolved: + +| Issue | Fix | +|-------|-----| +| `pg_config` not found for psycopg-c | Use `pkgs.postgresql.pg_config` (separate derivation), not `pkgs.postgresql` | +| gssapi `gss_acquire_cred_impersonate_name` undeclared | `NIX_CFLAGS_COMPILE="-include gssapi/gssapi_ext.h"` — function is in `gssapi_ext.h`, not auto-included | +| xmlsec linker error `-lltdl` | Add `pkgs.libtool` to buildInputs (provides libltdl) | +| psycopg-c needs `libpq` | Add `pkgs.libpq` to buildInputs | +| FOD references 19 store paths | Strip with `remove-references-to`, delete `bin/` and `.pyc` files, placeholder `pyvenv.cfg` | +| Still 6 residual store refs | Need to add `glibc.dev` and other `-dev` outputs to `refTargets` list — WIP | + +The `uv sync` completes in ~3.5 minutes. Reference stripping reduced 19 → 6 remaining store refs. Next iteration needs to capture all remaining dev/lib output paths in the `refTargets` list. + ## Related - [[build-authentik-from-source]] — Parent goal