Skip to content

fix(agent): source server-side secrets only from the launchd env file - #231

Merged
amirkhakshour merged 1 commit into
mainfrom
fix/cn-agent-env-split
Oct 18, 2026
Merged

amirkhakshour merged 1 commit into
mainfrom
fix/cn-agent-env-split

Conversation

@amirkhakshour

Copy link
Copy Markdown
Contributor

Problem

Installing the launchd agent with the default CN_AGENT_ENV_FILE
(~/.contextnest/tenant-auth/server.env) silently changed the API's security
posture. That file contains CONTEXTNEST_TENANTS_FILE, and
Registry::from_env — let Ok(file) = std::env::var(...) else { return Ok(None) }
— turns that into tenant mode: every route now requires Authorization.

Observed live after the agent install: the process booted, armed the watchdog,
restored 543 k fragments, and then answered

GET /api/v1/substrate/health → HTTP 401 {"error":"unauthorized scope"}

to the contextnest MCP server (which sends no auth headers at all), and would
do the same to the dashboard and the docs/usage.md curl recipes.

server.env is a client file: besides the tenant registry it holds
CONTEXTNEST_OPERATOR_HEADERS (a curl --header @file path) and
CONTEXTNEST_LIBWIT_TOKEN. The agent needs exactly one thing from it — the
embedding key.

Change

  • CN_AGENT_ENV_FILE now defaults to ~/.contextnest/tenant-auth/agent.env,
    documented as server-side secrets only.
  • The plist template says the same, and names the failure mode so a future
    install cannot regress it.
  • CLAUDE.md's CONTEXTNEST_VOLUME_WATCH row now describes the sentinel
    descriptor from fix(watchdog): detect a remount that keeps its device number #230, not just st_dev/st_ino.

Operators who want tenant mode can still point CN_AGENT_ENV_FILE at a
tenant-bearing file — but then every client must be taught to present the
operator header first.

Verification

  • Live: agent re-installed with agent.env; /api/v1/substrate/health → 200
    without headers; cn_health MCP tool returns the subtree counts again.
  • The rendered plist passes plutil -lint.
  • No Rust changed; CI runs the usual gates.

🤖 Generated with Claude Code

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 <noreply@anthropic.com>
@amirkhakshour
amirkhakshour merged commit 4b0be40 into main Oct 18, 2026
7 checks passed
@amirkhakshour
amirkhakshour deleted the fix/cn-agent-env-split branch October 18, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant