From cb4782e278344d98249fc0e39d76076372aa609e Mon Sep 17 00:00:00 2001 From: Amir Khakshour Date: Sun, 18 Oct 2026 12:29:30 +0200 Subject: [PATCH] fix(agent): source server-side secrets only from the launchd env file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installing the launchd agent with the default CN_AGENT_ENV_FILE pointed at ~/.contextnest/tenant-auth/server.env silently changed the API's security posture: that file carries CONTEXTNEST_TENANTS_FILE, which flips Registry:: from_env from None to a tenant registry and makes every route require Authorization. The substrate booted healthy and then answered 401 "unauthorized scope" to every client that does not speak tenant auth — the contextnest MCP server (no auth headers at all), the dashboard, and the curl recipes. The file was written for clients, not the server: it also holds CONTEXTNEST_OPERATOR_HEADERS (a curl --header @file path) and CONTEXTNEST_LIBWIT_TOKEN. Meanwhile the agent needs exactly one thing from it: the embedding key. Default the agent at agent.env — server-side secrets only — and say so in the template. The split is documented in both places so a later re-install cannot regress it. Co-Authored-By: Claude Code --- CLAUDE.md | 2 +- Makefile | 10 +++++++++- deploy/ai.sourceshift.contextnest.plist.in | 10 ++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 22d7114..5633d95 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -158,7 +158,7 @@ in order: `api_key` literal → `api_key_env` → `$DEEPINFRA_API_KEY` → | `CONTEXTNEST_RETRIEVE_CANDIDATE_POOL` | unset → `max(20 × top_k, 1000)` | Upper bound on candidates **materialized** into hits. Scoring stays exact over every candidate; only this many get text + metadata pulled from the sidecars. `0` = unbounded (the pre-knob behaviour) — the A/B escape hatch for a global query, which otherwise scores all ~527k active fragments. | | `CONTEXTNEST_EMBEDDING_CACHE_MAX_ENTRIES` | 4096 | LRU bound on the embedding response cache (~16 MB at 1024-d). `0` disables it. | | `CONTEXTNEST_VECTOR_ARENA_DIR` | unset (heap) | Put the operator's vector arena in a file-backed mapping under this dir (derived state, unlinked at boot). Use a volume with free space. | -| `CONTEXTNEST_VOLUME_WATCH` | on | `0` disables the data-volume watchdog. Exits 75 after ~15 s if the data directory's `st_dev`/`st_ino` change (a USB detach + remount revokes every fd and never recovers). See `docs/architecture-honest.md` → "Data-volume resilience". | +| `CONTEXTNEST_VOLUME_WATCH` | on | `0` disables the data-volume watchdog. Exits 75 after ~15 s if the data directory's `st_dev`/`st_ino` change **or** a write through the sentinel descriptor opened at arm time fails (a detach that remounts on the same device number leaves `st_dev` unchanged but revokes every fd, and neither recovers in-process). See `docs/architecture-honest.md` → "Data-volume resilience". | | `CONTEXTNEST_MIN_FREE_MB` | 5120 | Free-space floor on the data volume; below it the process refuses to boot (exit 74). `0` disables. | Full list in `docs/architecture-honest.md`. diff --git a/Makefile b/Makefile index b9d60b9..ed31572 100644 --- a/Makefile +++ b/Makefile @@ -193,7 +193,15 @@ CN_AGENT_LOG ?= /tmp/contextnest-agent.log # DEEPINFRA_API_KEY / OPENAI_API_KEY must live for the agent to embed. launchd # jobs inherit no interactive-shell environment, so a key exported in ~/.zshrc # will NOT reach the agent. -CN_AGENT_ENV_FILE ?= $(HOME)/.contextnest/tenant-auth/server.env +# +# SERVER-SIDE secrets only. Do not point this at a file that also carries client +# credentials: `CONTEXTNEST_TENANTS_FILE` alone flips the API into auth-enforced +# mode, and every client that does not present `Authorization` — the contextnest +# MCP server, the dashboard, the curl recipes in docs/usage.md — starts getting +# `401 unauthorized scope` while the process otherwise looks healthy. The +# operator's `server.env` is a client file (it also holds OPERATOR_HEADERS and +# LIBWIT_TOKEN); the agent gets the embedding keys from `agent.env` instead. +CN_AGENT_ENV_FILE ?= $(HOME)/.contextnest/tenant-auth/agent.env # Absolutised at render time: a launchd job starts with cwd `/`. CN_AGENT_BIN ?= $(abspath $(CN_BIN)) CN_AGENT_WORKDIR ?= $(abspath .) diff --git a/deploy/ai.sourceshift.contextnest.plist.in b/deploy/ai.sourceshift.contextnest.plist.in index a1c4921..250ba6f 100644 --- a/deploy/ai.sourceshift.contextnest.plist.in +++ b/deploy/ai.sourceshift.contextnest.plist.in @@ -7,8 +7,14 @@ ~/Library/LaunchAgents/