feat(vega): add Navidrome MCP service #197

Merged
nimmo merged 3 commits from feat/navidrome-mcp into main 2026-08-28 19:39:58 +01:00
Owner

Closes #196

Summary

  • add the independently packaged Navidrome-MCP flake input
  • host a bearer-authenticated Streamable HTTP endpoint on Vega Nebula
  • configure remote Codex and OpenCode MCP clients without sharing the Navidrome password

Validation

  • nix flake check

Remaining live evidence

Deploy to Vega and verify health, authenticated MCP access, a personal playlist operation, and an auto-update no-op before merging.

Closes #196 ## Summary - add the independently packaged Navidrome-MCP flake input - host a bearer-authenticated Streamable HTTP endpoint on Vega Nebula - configure remote Codex and OpenCode MCP clients without sharing the Navidrome password ## Validation - nix flake check ## Remaining live evidence Deploy to Vega and verify health, authenticated MCP access, a personal playlist operation, and an auto-update no-op before merging.
chore(vega): configure Navidrome MCP credentials
All checks were successful
Build and Push Attic Cache / test (pull_request) Successful in 1m34s
Build and Push Attic Cache / build (pull_request) Has been skipped
Build and Push Attic Cache / pin-lifecycle (pull_request) Has been skipped
Build and Push Attic Cache / remediate (pull_request) Has been skipped
Build and Push Attic Cache / notify-remediation (pull_request) Has been skipped
Build and Push Attic Cache / record-remediation-notify-failure (pull_request) Has been skipped
AI pull-request review / review (pull_request) Successful in 1m41s
a1509c17e0
Author
Owner

Automated code review

Reviewed commit: 49b8fa272b3d621dfac55647fdda51aa5570fabf

Verdict: Requires further work

Resolve the blocking or important findings and investigate failed deterministic checks before merging.

Overall assessment

The PR introduces a new Navidrome MCP service on Vega, exposing a Streamable HTTP endpoint over Nebula. It adds the navidrome-mcp flake input, creates SOPS-encrypted secrets for server credentials and client tokens, and configures AI client hosts to consume the remote MCP entry. The service is scoped to Vega with Nebula-only firewall rules, and the auto-update policy is updated.

Blocking findings

None.

Important findings

  • Missing SOPS key for Lyra in navidrome-mcp-clients.yaml (secrets/navidrome-mcp-clients.yaml; high confidence)
    The .sops.yaml creation rule for secrets/navidrome-mcp-clients.yaml$ lists recipients: *electra, *lena, *vega, *lyra, *yubikey-nano5c, *yubikey-5-nfc, *provision-key. However, the encrypted file content only contains age-encrypted blocks for 7 recipients. The Lyra recipient key (age1...) is not present in the SOPS metadata block of the provided diff.
    Impact: If the Lyra host's age key is not included in the encrypted file, sops-nix on Lyra will fail to decrypt the secret during evaluation or deployment, causing a build failure for that host.
    Suggested fix: Verify that the SOPS metadata block in secrets/navidrome-mcp-clients.yaml includes an encrypted block for Lyra's age public key. If missing, re-encrypt the file ensuring all listed recipients are included.

Suggestions

  • Hardcoded Nebula IP in client URL default (modules/services/navidrome-mcp.nix:68; medium confidence)
    default = "http://10.10.0.2:${toString cfg.port}/mcp";
    Impact: The client URL default hardcodes Vega's Nebula IP (10.10.0.2). While this is currently correct for Vega, it reduces portability if the configuration is ever reused for other hosts or if Vega's Nebula IP changes. It relies on cfg.listenAddress being the same as 10.10.0.2.
    Suggested fix: Consider deriving the client URL default from cfg.listenAddress (e.g., "http://${cfg.listenAddress}:${toString cfg.port}/mcp") to ensure consistency and reduce hardcoding.

  • Service dependency on Nebula device unit (modules/services/navidrome-mcp.nix:105; medium confidence)
    wants = [ "network-online.target" "nebula@mesh.service" ]; bindsTo = [ "sys-subsystem-net-devices-nebula.mesh.device" ];
    Impact: The service uses bindsTo on the network device unit. If the Nebula interface goes down, the service will be stopped. This is generally good for correctness but ensure this behavior aligns with operational expectations (e.g., should it restart automatically when Nebula comes back up? Restart = "on-failure" might not trigger on stop due to dependency loss depending on systemd version/config).
    Suggested fix: Verify that Restart = "on-failure" behaves as expected when the service is stopped due to a dependency (Nebula device) being removed. Consider if RestartSec or other restart policies need adjustment for Nebula flakiness.

Tests and validation

  • No configured deterministic checks.

Questions

  • Has the SOPS encryption for secrets/navidrome-mcp-clients.yaml been verified to include all recipients listed in .sops.yaml, specifically Lyra's key?
  • Is the hardcoded Nebula IP 10.10.0.2 in the client URL default acceptable, or should it be derived from cfg.listenAddress?

Review limitations

  • I cannot run nix flake check to validate the configuration.
  • I cannot verify the actual SOPS decryption on Lyra without access to the key material.
  • The diff for secrets/navidrome-mcp-clients.yaml shows encrypted content but does not explicitly list all recipient keys in a human-readable format, requiring inference from the number of encrypted blocks.

Diff coverage

  • .sops.yaml: reviewed — included in a context-limited batch
  • README.md: reviewed — included in a context-limited batch
  • flake.lock: reviewed — included in a context-limited batch
  • flake.nix: reviewed — included in a context-limited batch
  • hosts/vega/default.nix: reviewed — included in a context-limited batch
  • lib/auto-update-policy.nix: reviewed — included in a context-limited batch
  • modules/profiles/ai-agents.nix: reviewed — included in a context-limited batch
  • modules/profiles/ai-runtime.nix: reviewed — included in a context-limited batch
  • modules/services/navidrome-mcp.nix: reviewed — included in a context-limited batch
  • secrets/README.md: reviewed — included in a context-limited batch
  • secrets/navidrome-mcp-clients.yaml: reviewed — included in a context-limited batch
  • secrets/navidrome-mcp.yaml: reviewed — included in a context-limited batch
Review metadata
  • Reviewer: forgejo-ai-review v1
  • Model: qwen3.6:35b-a3b-q8_0
  • Guidance loaded: .forgejo/ai-review.md, .forgejo/ai-review.yml, AGENTS.md, README.md
  • Commits considered: 3
  • Previous automated review: updated
  • Findings: 3
<!-- forgejo-ai-review:v1 --> ## Automated code review Reviewed commit: `49b8fa272b3d621dfac55647fdda51aa5570fabf` ## Verdict: Requires further work Resolve the blocking or important findings and investigate failed deterministic checks before merging. ### Overall assessment The PR introduces a new Navidrome MCP service on Vega, exposing a Streamable HTTP endpoint over Nebula. It adds the `navidrome-mcp` flake input, creates SOPS-encrypted secrets for server credentials and client tokens, and configures AI client hosts to consume the remote MCP entry. The service is scoped to Vega with Nebula-only firewall rules, and the auto-update policy is updated. ### Blocking findings None. ### Important findings - **Missing SOPS key for Lyra in navidrome-mcp-clients.yaml** (secrets/navidrome-mcp-clients.yaml; high confidence) The `.sops.yaml` creation rule for `secrets/navidrome-mcp-clients.yaml$` lists recipients: `*electra`, `*lena`, `*vega`, `*lyra`, `*yubikey-nano5c`, `*yubikey-5-nfc`, `*provision-key`. However, the encrypted file content only contains age-encrypted blocks for 7 recipients. The Lyra recipient key (`age1...`) is not present in the SOPS metadata block of the provided diff. Impact: If the Lyra host's age key is not included in the encrypted file, `sops-nix` on Lyra will fail to decrypt the secret during evaluation or deployment, causing a build failure for that host. Suggested fix: Verify that the SOPS metadata block in `secrets/navidrome-mcp-clients.yaml` includes an encrypted block for Lyra's age public key. If missing, re-encrypt the file ensuring all listed recipients are included. ### Suggestions - **Hardcoded Nebula IP in client URL default** (modules/services/navidrome-mcp.nix:68; medium confidence) `default = "http://10.10.0.2:${toString cfg.port}/mcp";` Impact: The client URL default hardcodes Vega's Nebula IP (`10.10.0.2`). While this is currently correct for Vega, it reduces portability if the configuration is ever reused for other hosts or if Vega's Nebula IP changes. It relies on `cfg.listenAddress` being the same as `10.10.0.2`. Suggested fix: Consider deriving the client URL default from `cfg.listenAddress` (e.g., `"http://${cfg.listenAddress}:${toString cfg.port}/mcp"`) to ensure consistency and reduce hardcoding. - **Service dependency on Nebula device unit** (modules/services/navidrome-mcp.nix:105; medium confidence) `wants = [ "network-online.target" "nebula@mesh.service" ]; bindsTo = [ "sys-subsystem-net-devices-nebula.mesh.device" ];` Impact: The service uses `bindsTo` on the network device unit. If the Nebula interface goes down, the service will be stopped. This is generally good for correctness but ensure this behavior aligns with operational expectations (e.g., should it restart automatically when Nebula comes back up? `Restart = "on-failure"` might not trigger on stop due to dependency loss depending on systemd version/config). Suggested fix: Verify that `Restart = "on-failure"` behaves as expected when the service is stopped due to a dependency (Nebula device) being removed. Consider if `RestartSec` or other restart policies need adjustment for Nebula flakiness. ### Tests and validation - No configured deterministic checks. ### Questions - Has the SOPS encryption for `secrets/navidrome-mcp-clients.yaml` been verified to include all recipients listed in `.sops.yaml`, specifically Lyra's key? - Is the hardcoded Nebula IP `10.10.0.2` in the client URL default acceptable, or should it be derived from `cfg.listenAddress`? ### Review limitations - I cannot run `nix flake check` to validate the configuration. - I cannot verify the actual SOPS decryption on Lyra without access to the key material. - The diff for `secrets/navidrome-mcp-clients.yaml` shows encrypted content but does not explicitly list all recipient keys in a human-readable format, requiring inference from the number of encrypted blocks. ### Diff coverage - `.sops.yaml`: **reviewed** — included in a context-limited batch - `README.md`: **reviewed** — included in a context-limited batch - `flake.lock`: **reviewed** — included in a context-limited batch - `flake.nix`: **reviewed** — included in a context-limited batch - `hosts/vega/default.nix`: **reviewed** — included in a context-limited batch - `lib/auto-update-policy.nix`: **reviewed** — included in a context-limited batch - `modules/profiles/ai-agents.nix`: **reviewed** — included in a context-limited batch - `modules/profiles/ai-runtime.nix`: **reviewed** — included in a context-limited batch - `modules/services/navidrome-mcp.nix`: **reviewed** — included in a context-limited batch - `secrets/README.md`: **reviewed** — included in a context-limited batch - `secrets/navidrome-mcp-clients.yaml`: **reviewed** — included in a context-limited batch - `secrets/navidrome-mcp.yaml`: **reviewed** — included in a context-limited batch <details><summary>Review metadata</summary> - Reviewer: forgejo-ai-review v1 - Model: qwen3.6:35b-a3b-q8_0 - Guidance loaded: .forgejo/ai-review.md, .forgejo/ai-review.yml, AGENTS.md, README.md - Commits considered: 3 - Previous automated review: updated - Findings: 3 </details>
fix(ai): use Navidrome MCP token secret
All checks were successful
Build and Push Attic Cache / test (pull_request) Successful in 1m36s
Build and Push Attic Cache / build (pull_request) Has been skipped
Build and Push Attic Cache / pin-lifecycle (pull_request) Has been skipped
Build and Push Attic Cache / remediate (pull_request) Has been skipped
Build and Push Attic Cache / notify-remediation (pull_request) Has been skipped
Build and Push Attic Cache / record-remediation-notify-failure (pull_request) Has been skipped
AI pull-request review / review (pull_request) Successful in 1m40s
49b8fa272b
nimmo merged commit 6d58926829 into main 2026-08-28 19:39:58 +01:00
nimmo deleted branch feat/navidrome-mcp 2026-08-28 19:39:59 +01:00
Sign in to join this conversation.
No reviewers
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nimmo/nixos-config!197
No description provided.