feat: add Trilium MCP over Nebula #69

Merged
nimmo merged 2 commits from feat/trilium-mcp into main 2026-07-18 23:53:09 +01:00
Owner

Summary

  • Package perfectra1n/triliumnext-mcp v0.2.7 in Nix.
  • Run it on Lyra using the SOPS-backed Trilium ETAPI token.
  • Expose the Streamable HTTP endpoint only through a Nebula-bound proxy at 10.10.0.3:8781.
  • Configure both Codex and OpenCode to use the shared remote MCP endpoint.
  • Document the service and secret ownership.

Validation

  • nix build .#nixosConfigurations.lyra.config.system.build.toplevel --no-link
  • nix flake check

Both passed. The service has not been deployed yet; live validation remains after merge.

## Summary - Package `perfectra1n/triliumnext-mcp` v0.2.7 in Nix. - Run it on Lyra using the SOPS-backed Trilium ETAPI token. - Expose the Streamable HTTP endpoint only through a Nebula-bound proxy at `10.10.0.3:8781`. - Configure both Codex and OpenCode to use the shared remote MCP endpoint. - Document the service and secret ownership. ## Validation - `nix build .#nixosConfigurations.lyra.config.system.build.toplevel --no-link` - `nix flake check` Both passed. The service has not been deployed yet; live validation remains after merge.
feat: add Trilium MCP over Nebula
All checks were successful
Build and Push Attic Cache / test (pull_request) Successful in 37s
Build and Push Attic Cache / build (pull_request) Has been skipped
f988ccd88f
fix: authenticate Trilium MCP clients
All checks were successful
Build and Push Attic Cache / test (pull_request) Successful in 34s
Build and Push Attic Cache / build (pull_request) Has been skipped
df53e1164f
Author
Owner

Review follow-up: the initial implementation relied on Nebula membership as the sole trust boundary, while leaving trilium/mcp-gateway-token dormant. During review we decided the MCP server's read/write power justified application-layer defense in depth as well.

Resolved in df53e11:

  • Lyra loads the gateway bearer through a systemd credential and starts triliumnext-mcp with bearer authentication enabled.
  • The ETAPI token remains root-only on Lyra; AI client hosts receive only the separate gateway bearer.
  • Codex stores only the bearer environment-variable name. Its repo-managed wrapper reads the SOPS secret at process startup, covering interactive, desktop, and Paseo-launched Codex.
  • OpenCode uses a file-substituted Authorization header with OAuth auto-discovery disabled.
  • Neither token is placed in process arguments or generated plaintext configuration.

Validation completed:

  • git diff --check
  • Isolated codex mcp add/get round-trip confirmed bearer_token_env_var = TRILIUM_MCP_GATEWAY_TOKEN
  • Generated Lyra systemd credentials, Codex activation, wrapper, and OpenCode MCP configuration inspected
  • nix flake check
  • nix build .#nixosConfigurations.lyra.config.system.build.toplevel --no-link

Live endpoint validation remains deployment evidence: confirm a headerless /mcp request returns 401, then perform an authenticated MCP read/write smoke test.

Review follow-up: the initial implementation relied on Nebula membership as the sole trust boundary, while leaving `trilium/mcp-gateway-token` dormant. During review we decided the MCP server's read/write power justified application-layer defense in depth as well. Resolved in `df53e11`: - Lyra loads the gateway bearer through a systemd credential and starts `triliumnext-mcp` with bearer authentication enabled. - The ETAPI token remains root-only on Lyra; AI client hosts receive only the separate gateway bearer. - Codex stores only the bearer environment-variable name. Its repo-managed wrapper reads the SOPS secret at process startup, covering interactive, desktop, and Paseo-launched Codex. - OpenCode uses a file-substituted `Authorization` header with OAuth auto-discovery disabled. - Neither token is placed in process arguments or generated plaintext configuration. Validation completed: - `git diff --check` - Isolated `codex mcp add/get` round-trip confirmed `bearer_token_env_var = TRILIUM_MCP_GATEWAY_TOKEN` - Generated Lyra systemd credentials, Codex activation, wrapper, and OpenCode MCP configuration inspected - `nix flake check` - `nix build .#nixosConfigurations.lyra.config.system.build.toplevel --no-link` Live endpoint validation remains deployment evidence: confirm a headerless `/mcp` request returns 401, then perform an authenticated MCP read/write smoke test.
Author
Owner

Pre-merge live canary completed on Lyra at df53e1164fa9fe96c5ca5970d8c9b3349c52bfbe using just test under an auto-update hold.

Live evidence:

  • Running configuration revision is df53e11.
  • trilium-mcp, trilium-mcp-proxy, paseo, and opencode are active; Lyra has no failed systemd units.
  • Server startup reports gateway_auth="bearer (1 token(s))".
  • /health returns 200 both directly and through 10.10.0.3:8781.
  • Missing bearer: HTTP 401 unauthorized.
  • Incorrect bearer: HTTP 401 unauthorized.
  • SOPS-backed client bearer: HTTP 200 initialization with an MCP session ID.
  • Authenticated tools/list: HTTP 200 with all 19 tools.
  • Authenticated read-only search_notes call for a deliberately nonexistent canary title: HTTP 200, isError=false, empty result set.
  • Activated Codex config points to the Trilium URL with bearer_token_env_var=TRILIUM_MCP_GATEWAY_TOKEN.
  • opencode mcp list reports trilium connected.

No Trilium data was created, changed, or deleted during validation. The Lyra auto-update hold remains active pending restoration/merge handling.

Pre-merge live canary completed on Lyra at `df53e1164fa9fe96c5ca5970d8c9b3349c52bfbe` using `just test` under an auto-update hold. Live evidence: - Running configuration revision is `df53e11`. - `trilium-mcp`, `trilium-mcp-proxy`, `paseo`, and `opencode` are active; Lyra has no failed systemd units. - Server startup reports `gateway_auth="bearer (1 token(s))"`. - `/health` returns 200 both directly and through `10.10.0.3:8781`. - Missing bearer: HTTP 401 `unauthorized`. - Incorrect bearer: HTTP 401 `unauthorized`. - SOPS-backed client bearer: HTTP 200 initialization with an MCP session ID. - Authenticated `tools/list`: HTTP 200 with all 19 tools. - Authenticated read-only `search_notes` call for a deliberately nonexistent canary title: HTTP 200, `isError=false`, empty result set. - Activated Codex config points to the Trilium URL with `bearer_token_env_var=TRILIUM_MCP_GATEWAY_TOKEN`. - `opencode mcp list` reports `trilium connected`. No Trilium data was created, changed, or deleted during validation. The Lyra auto-update hold remains active pending restoration/merge handling.
nimmo merged commit 4de6e5e694 into main 2026-07-18 23:53:09 +01:00
nimmo deleted branch feat/trilium-mcp 2026-07-18 23:53:10 +01:00
Sign in to join this conversation.
No reviewers
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!69
No description provided.