feat(vikunja): deploy Vikunja with Keycloak SSO and PostgreSQL #31
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/vikunja-keycloak-sso"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
nidaros, including its service module, public URL, and reverse proxy onpi4.Validation
nix fmton the changed Nix filesnh os build . -H nidarossucceeded with PostgreSQL enabled for Vikunjafeat(vikunja): deploy Vikunja with Keycloak SSOto feat(vikunja): deploy Vikunja with Keycloak SSO and PostgreSQL{
"summary": "# Review Summary\n\nWell-integrated addition that follows the repo's conventions:
vikunja.nixmatches the AGENTS.md module template (cfgalias,mkEnableOption,lib.mkIf, camelCase, sentence descriptions), and the "Adding a New Self-Hosted Service" checklist is fully covered — registry entry, module import, host enable, SOPS secret, and the pi4 reverse proxy. Reachability checks out: Vikunja bindssystemConfig.address.private, pi4'slocalAddressproxies to that same IP, port 8094 is unused anywhere else, and nidaros' firewall already accepts the whole192.168.10.0/24range, so no port opening is needed. Registering the domain inweb-services.nixalso auto-enrolls a blackbox health probe. The root-owned SOPS secret used as a systemdEnvironmentFileis correct for aDynamicUserservice (systemd reads it as root; setting a service owner would break activation).\n\nBlocking:shared/secrets/secrets.yamlis re-indented from 2 to 4 spaces across the entire file. The repo has no yamlfmt config, so treefmt's yamlfmt defaults (indent 2) apply; as committed this file is no longer formatter-stable, sonix flake checkwill fail its formatting check, and the churn also buries the real change (one new key + re-encrypted MAC). Runnix fmtafter the sops edit — re-indenting a sops file is safe (the MAC covers values, not whitespace).\n\nNotes:\n- The env file must provide exactlyVIKUNJA_AUTH_OPENID_PROVIDERS_KEYCLOAK_CLIENTSECRET(the provider key iskeycloak). Vikunja treatsclientsecretas a required provider key, and withauth.local.enabled = falsea wrong or missing name means no login method at all — worth a runtime smoke test, since the build can't catch it.\n- The PR description says the OIDC client-secret file is excluded/user-managed, butvikunja/oidc-environmentis present insecrets.yaml(encrypted, correct recipients). Align the description.\n- Remaining out-of-band step: creating the Keycloak clientvikunjawith redirect URIhttps://tasks.martials.no/auth/openid/keycloak(and post-logout redirect URI).","verdict": "changes",
"comments": [
{
"file": "shared/secrets/secrets.yaml",
"line": 2,
"side": "new",
"severity": "style",
"message": "The whole file was re-indented from 2 to 4 spaces. The repo has no yamlfmt config, so treefmt applies yamlfmt's default
indent: 2— as committed this file will failnix flake check's formatting check, and the churn hides the actual one-key addition. Please runnix fmt/just fmtafter the sops edit; reformatting is safe for sops (the MAC covers values, not whitespace)."},
{
"file": "shared/base/modules/services/vikunja.nix",
"line": 43,
"side": "new",
"severity": "bug",
"message": "
service.secretis not set anywhere, so Vikunja generates a random one on every boot — upstream docs warn that all issued JWTs (and refresh flows, for clients that don't recover) become invalid after each restart. Since everynixos-rebuild switchrestarts the service, users get their sessions invalidated on every deploy. Consider addingVIKUNJA_SERVICE_SECRET(stable 32-byte value) to the same user-managedvikunja/oidc-environmentfile so sessions survive restarts."},
{
"file": "shared/base/modules/services/vikunja.nix",
"line": 61,
"side": "new",
"severity": "style",
"message": "This module configures
ensureDatabases/ensureUsersbut never setsservices.postgresql.enable = true. It works on nidaros only becausehosts/nidaros/postgres.nix(and keycloak'screateLocally) enable PostgreSQL elsewhere.freshrss.nixenables it inside its own module — doing the same here keeps the module self-contained if it's ever enabled on another host."},
{
"file": "shared/base/modules/services/vikunja.nix",
"line": 71,
"side": "new",
"severity": "bug",
"message": "The SOPS secret is read only via
EnvironmentFile, so rotating the OIDC client secret deploys the new file but never reaches the running process — the old value stays in the service environment until the next unrelated restart. AddrestartUnits = [ \"vikunja.service\" ](sops-nix option) so secret updates take effect."}
]
}
Objective
feat(vikunja): deploy Vikunja with Keycloak SSO and PostgreSQL) inmartin/nixos-configuration, output as a single JSON object withsummary,verdict, andcommentsfields only (no markdown fences, no extra text).Important Details
{summary, verdict ("approve"|"changes"|"comment"), comments:[{file, line, side, severity ("bug"|"security"|"performance"|"style"), message}]}; omitsidefor new-file lines; emptycommentsarray if no findings.nidaros, dedicated PostgreSQL DB/role, Keycloak OIDC with local login disabled, reverse proxy onpi4, port8094, domaintasks.martials.no.shared/secrets/secrets.yamlDOES includevikunja/oidc-environment(encrypted) — description mismatch noted.treefmt.nixhasyamlfmt.enable = truewith no config → yamlfmt defaultindent: 2; newsecrets.yamluses 4-space indent →nix flake checkformatting check will fail..gitignoreexclusions for secrets.8094unused elsewhere (8093 = woodpeckerMetrics, 8095 = recurring-events hardcoded in nginx).hosts/nidaros/security/firewall.nixaccepts all input from192.168.10.0/24(common.localIpRange), nftables enabled → port 8094 reachable from pi4 without opening it;systems.nix: pi4 =192.168.10.188, nidaros =192.168.10.228.hosts/nidaros/postgres.nix:services.postgresql.enable = true, authlocal all all trust→ unix-socket DB connection works without password.shared/base/modules/services/vikunja.nixdoes NOT setservices.postgresql.enable = true(unlikefreshrss.nix), relies on host config — portability nit.sops.secrets."vikunja/oidc-environment" = { };(line 71) lacksrestartUnits = [ "vikunja.service" ]— secret rotation won't reach the running process (env file only read at start).EnvironmentFileis correct for aDynamicUserservice — NOT a bug (confirmed via research).services.vikunjamodule (fetched from GitHub): optionsenvironmentFiles(list of path),frontendScheme,frontendHostname,address,port,settings,database.typeenum sqlite/mysql/postgres,database.host/user/database,DynamicUser = true.setConfigFromEnvsplitsVIKUNJA_keys on_→ env var must be exactlyVIKUNJA_AUTH_OPENID_PROVIDERS_KEYCLOAK_CLIENTSECRET; provider keykeycloakexists in config file (required for env-based provider values).clientsecretas required per provider (getProviderFromMap); withauth.local.enabled = false, missing secret = no login method at all — runtime smoke test needed.service.secretnot set → random per-boot secret → all issued JWTs invalid after every restart/switch./auth/openid/keycloak).bash,grep,globtools all broken (ChildProcess.spawnNotFound /ripgrep execution failed);readandwebfetch/websearchwork; atask(explore subagent) was used successfully.Work State
Completed
verdict: "changes"and 4 comments:shared/secrets/secrets.yaml:2(style) — whole-file 2→4 space re-indent failsnix flake checkfmt check; runnix fmt.shared/base/modules/services/vikunja.nix:43(bug) — noservice.secret→ JWTs invalidated every restart; suggestVIKUNJA_SERVICE_SECRETin the env file.shared/base/modules/services/vikunja.nix:61(style) — missingservices.postgresql.enable = true(freshrss.nix sets it; only works on nidaros viapostgres.nix).shared/base/modules/services/vikunja.nix:71(bug) — SOPS secret lacksrestartUnits = [ "vikunja.service" ]; rotations never reach the running process.Active
Blocked
Next Move
Relevant Files
/woodpecker/src/code.martials.no/martin/nixos-configuration/.tmpnToj7n/pr.diff: the PR diff under review (6 files).shared/base/modules/services/vikunja.nix: new module; lines 43 (environmentFiles), 61 (services.postgresql), 71 (sops.secretsdeclaration) — comment anchors.shared/secrets/secrets.yaml: whole-file re-indentation; comment anchor line 2.treefmt.nix: confirms yamlfmt enabled with no indent config → default 2.hosts/nidaros/security/firewall.nix: LAN range accept rule → port reachability verified.hosts/nidaros/postgres.nix: PostgreSQL enabled on nidaros withlocal ... trust.web-services.nix: vikunja entry (domaintasks.martials.no, port 8094).hosts/pi4/nginx.nix:mkWsReverseProxy webServices.vikunja→tasks.${domain}.shared/base/modules/services/default.nix: import list (alphabetical,./vikunja.nixadded).shared/base/modules/services/freshrss.nix: reference pattern (setspostgresql.enable = true)..sops.yaml: creation rule forshared/secrets/secrets.yamlwith 4 age recipients.Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed.
The PR #31 review is complete — final JSON (verdict:
changes, 4 comments) was already delivered. Nothing more to do. Let me know if you want it re-run or a comment posted to Forgejo.*🤖 Review by opencode (opencode/mimo-v2.6-flash-free) — verdict: comment
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.