Skip to content

feat(desktop): community-scoped env vars keyed by relay URL - #4101

Open
andydoucet wants to merge 1 commit into
block:mainfrom
andydoucet:feat/community-scoped-env-vars
Open

feat(desktop): community-scoped env vars keyed by relay URL#4101
andydoucet wants to merge 1 commit into
block:mainfrom
andydoucet:feat/community-scoped-env-vars

Conversation

@andydoucet

Copy link
Copy Markdown

What

Adds a community-scoped env tier to the global agent config: community_env_vars, a map of relay URL → env map, injected only into agents spawned for that community's effective relay.

// global-agent-config.json
{
  "community_env_vars": {
    "wss://work.example.com":     { "MEMORY_API_KEY": "work-key" },
    "wss://personal.example.com": { "MEMORY_API_KEY": "personal-key" }
  }
}

Layering (low → high): baked floor < runtime metadata < definition < global < community < persona < per-agent — a community entry is a more specific global default; identity-scoped layers still win on collision.

Why

Under agents-everywhere (#2122) any record may spawn into any community, so no existing env tier can carry a community-scoped secret:

Running two communities with separate memory-service accounts on one desktop, a restart cycle handed one community's API key to agents connected to the other community's relay — a silent cross-tenant credential leak. The only always-truthful community signal is the effective spawn relay, so that's what this tier keys on. Closes #4099 and covers #2717's motivating use case (CLAUDE_CONFIG_DIR per community) without needing per-instance UI targeting.

How

  • GlobalAgentConfig gains #[serde(default)] community_env_vars: BTreeMap<String, BTreeMap<String, String>> plus save-time validation (same POSIX/reserved/derived-key rules as the global map, errors prefixed with the offending relay), empty-value stripping, and a community_env_for() lookup with trailing-slash/whitespace normalization — exact match only, no host heuristics.
  • resolve_effective_harness_descriptor / resolve_effective_agent_env take a new relay_url: Option<&str> and inject the community layer between global and persona. Threaded through:
    • spawn_agent_child — the pair's effective relay (the layer lands in real spawns);
    • spawn_config_hash — same relay resolution as spawn, so editing a community's vars marks that community's running agents needs_restart and the existing set_global_agent_config auto-restart flow picks them up;
    • per-pair status/summary/readiness — the pair key's relay;
    • relay-agnostic model probes — None (community layer intentionally absent).
  • set_global_agent_config restart candidates diff old vs new env under the live pair's relay, so community edits restart exactly the affected agents.
  • TS GlobalAgentConfig mirrors the field (and the empty-config constants include it) so UI round-trips don't drop it. No editor UI yet — the config file is the interface, same as several other advanced fields; a Settings editor can follow.

Tests

global_config/tests.rs: validation (accepts valid maps; rejects reserved keys, derived provider/model keys, blank relay keys — errors name the community), strip-on-save (empty values and all-empty communities dropped), lookup normalization (trailing slash both directions, no cross-community match, blank relay never matches), full layering precedence (community overrides global, loses to per-agent; other relay and None get no community layer), and resolve-time reserved-key stripping for hand-edited config files.

Full desktop lib suite passes locally (macOS): 2096 passed, 0 failed. cargo fmt --check clean.

Related

No open PRs touch community/relay-scoped env handling ("none found" per CONTRIBUTING).

🤖 Generated with Claude Code

Under agents-everywhere (block#2122) any managed-agent record may spawn into any
community, so no existing env tier can carry a community-scoped secret:
per-record/persona env_vars are identity-scoped and follow the agent into
whichever community it spawns on, and global env_vars are one value for
every community. Running two communities with separate memory-service
accounts on one desktop, a restart cycle handed one community's API key to
agents connected to the other community's relay.

Add a location-scoped tier: GlobalAgentConfig.community_env_vars, a map of
relay URL -> env map, injected at the single descriptor resolver keyed by
the effective spawn relay. Layering: baked floor < runtime metadata <
definition < global < community < persona < per-agent. Same reserved /
derived / malformed key rules as every other tier; validation errors name
the offending community. spawn_config_hash resolves the same relay as the
spawn path so community edits mark affected running agents needs_restart,
and set_global_agent_config diffs old vs new env under each live pair's
relay so the existing auto-restart flow picks them up. Relay-agnostic model
probes resolve without the community layer.

The TS GlobalAgentConfig mirrors the field so UI round-trips don't drop it;
no editor UI yet - the config file is the interface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Andrew Doucet <65342358+illumin8ca@users.noreply.github.com>
@andydoucet
andydoucet requested a review from a team as a code owner August 1, 2026 11:47
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.

No community-scoped env tier: any record can spawn into any community carrying another community's credentials

1 participant