Skip to content

feat(channels): per-instance sidecar secrets so each agent can own its own handle#6217

Merged
houko merged 5 commits into
mainfrom
feat/6169-per-instance-sidecar-secrets
Jun 19, 2026
Merged

feat(channels): per-instance sidecar secrets so each agent can own its own handle#6217
houko merged 5 commits into
mainfrom
feat/6169-per-instance-sidecar-secrets

Conversation

@houko

@houko houko commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements #6169 — let individual agents own their own Matrix handle (generalizes to any sidecar adapter).

Root cause (see the issue comment for the full trace)

A Matrix sidecar's identity is its MATRIX_ACCESS_TOKEN, not MATRIX_USER_ID. Two [[sidecar_channels]] instances that share the one global token in secrets.env authenticate as the same account. Inbound routing already isolates per instance (account_id = block name), so the only gap is the credential side.

Change

  • crates/librefang-channels/src/sidecar.rsbuild_spawn_env now takes the instance name and resolves a <NAME>__KEY entry in secrets.env to the bare KEY for that instance (name uppercased, non-alphanumerics → _; e.g. block agent-a reads AGENT_A__MATRIX_ACCESS_TOKEN). Precedence, low→high: global secrets.env (parent env wins) < per-instance secret (beats global + parent env) < inline [sidecar_channels.env]. Another instance's namespaced secret is never exposed to this child.
  • Two unit tests: per-instance override beats global + parent; cross-instance isolation (agent-b's secret doesn't leak to agent-a).
  • librefang.toml.example documents the convention.

Operators keep tokens in secrets.env (not plaintext config.toml). Without a prefix, all instances share the global secret exactly as before — fully backward-compatible.

Verification

⚠️ Not built locally (host disk full / Docker down, per the maintainer's instruction to stop local builds). Statically reviewed: signature change + one new caller arg (&ctx.name, an existing SpawnCtx field), strip_prefix uses &str, the four existing build_spawn_env tests are updated for the new arg, types check by hand. CI is the verifiercargo test -p librefang-channels, cargo check --workspace --lib.

…ecrets.env

Two instances of the same sidecar adapter had to share one global credential, so e.g. two Matrix sidecars logged in as the same account (identity is the MATRIX_ACCESS_TOKEN, and that lived in the shared store). The workaround was to inline each token in config.toml, which puts credentials in plaintext.
build_spawn_env now resolves a <NAME>__KEY entry in the store to bare KEY for the matching instance (name uppercased, non-alphanumerics to _). The per-instance value overrides the global bare key and the parent env, and a different instance's namespaced credential is never exposed to this child. Without a prefix, all instances share the global value as before, and inline [sidecar_channels.env] still wins as the most explicit override.
Tests cover the override precedence and cross-instance isolation; librefang.toml.example documents the convention.

Closes #6169
@github-actions github-actions Bot added size/M 50-249 lines changed area/docs Documentation and guides area/channels Messaging channel adapters labels Jun 18, 2026
github-actions Bot and others added 3 commits June 18, 2026 07:28
…reserved `__` delimiter

The single-line if/else closure body in instance_secret_prefix failed
cargo fmt --check in CI; apply rustfmt's block form. Also document, in
code and librefang.toml.example, that `__` is the reserved instance
namespace delimiter so any global secrets.env key containing it is
intentionally withheld from every child.
@github-actions github-actions Bot added the has-conflicts PR has merge conflicts that need resolution label Jun 18, 2026
# Conflicts:
#	.secrets.baseline
@houko
houko enabled auto-merge (squash) June 19, 2026 04:51
@cloudflare-workers-and-pages

Copy link
Copy Markdown

@github-actions github-actions Bot added ready-for-review PR is ready for maintainer review and removed has-conflicts PR has merge conflicts that need resolution labels Jun 19, 2026
@houko
houko merged commit 8ee68d5 into main Jun 19, 2026
34 checks passed
@houko
houko deleted the feat/6169-per-instance-sidecar-secrets branch June 19, 2026 05:32
GQAdonis pushed a commit to GQAdonis/librefang that referenced this pull request Jun 19, 2026
Merge upstream/main into the BossFang fork; origin/main was 15 commits behind.

Notable upstream changes:
- librefang#6225 scope the compaction-summary banner to the compacted session (adds canonical_sessions.compacted_summary_session_id).
- librefang#6196 deny WASM fs_write to the audit anchor via a capability deny-list.
- librefang#6226 / librefang#6227 add agent label and exit-reason metrics to the agent loop.
- librefang#6215 context-window usage indicator + honest quota-error classification.
- librefang#6217 per-instance sidecar secrets so each agent owns its own handle.
- librefang#6194 global Auto-Dream on/off switch on the Memory tab.
- librefang#6211 / librefang#6214 / librefang#6212 token/context cap fixes; librefang#6208 refuse to delete the active prompt version.
- librefang#6218 browser_tools.rs ToolError migration; librefang#6203 launchctl let-binding build fix; librefang#6224 docs dep bumps; librefang#6193 drop five orphaned email deps.

Conflict resolution:
- deny.toml: keep our RUSTSEC-2025-0141 (bincode 2.0.x) ignore entry (take ours).
- deny.toml: ignore three pre-existing unmaintained advisories newly tripped by the RustSec DB (all transitive via the UAR provider chain, none introduced by this merge): RUSTSEC-2024-0384 (instant), RUSTSEC-2024-0436 (paste), RUSTSEC-2025-0119 (number_prefix).

BossFang preservation (SurrealDB schema parity for upstream SQLite v46 / librefang#6225):
- Add crates/librefang-storage/src/migrations/sql/032_canonical_sessions_compacted_summary_session_id.surql declaring the new field on the SCHEMAFULL canonical_sessions table (SCHEMAFULL silently drops undefined fields on write), registered as version 32 in migrations/mod.rs.
- migrate/sqlite_to_surreal.rs: copy the new column so a SQLite to SurrealDB migration preserves the owning-session pointer rather than dropping it.
- backends/surreal_session.rs: preserve compacted_summary_session_id across canonical appends, since the upsert replaces the whole record.
- Cargo.lock: pick up the workspace version bump (beta.19 to beta.20) the merge introduced.

Verification:
- cargo check --workspace --lib — clean.
- cargo check -p librefang-storage -p librefang-memory -p librefang-uar-spec — clean.
- cargo test -p librefang-storage migration — ok (migration ordering / SurrealDB-3 flexible-syntax invariants).
- cargo test -p librefang-memory --lib session — 50 passed (incl. upstream's store_llm_summary round-trip test).
- cargo clippy -p librefang-storage -p librefang-memory — clean.
- python3 scripts/enforce-branding.py --check — clean; Tauri desktop audit and URL-drift scan both clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/channels Messaging channel adapters area/docs Documentation and guides ready-for-review PR is ready for maintainer review size/M 50-249 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant