Agent bottle settings #88

Closed
opened 2026-05-27 23:20:41 -04:00 by didericis · 2 comments
Owner

Since the agent-bottle instance relationship is 1-1, we don’t really need to have a separate config for each. However a separate bottle config is still useful for holding shared configuration that should apply to multiple agents.

Allow for specifying a bottle config in the agent for cases when we DONT want to reference a shared bottle. In these cases, there should be a “bottle_config” key which gets merged with the config from any specified bottle to provision/launch the bottle. bottle_config in the agent should take precedence for any conflicts.

I’m not 100% sold on this design: weigh pros and cons in the comments here

Since the agent-bottle instance relationship is 1-1, we don’t really need to have a separate config for each. However a separate bottle config is still useful for holding shared configuration that should apply to multiple agents. Allow for specifying a bottle config in the agent for cases when we DONT want to reference a shared bottle. In these cases, there should be a “bottle_config” key which gets merged with the config from any specified bottle to provision/launch the bottle. bottle_config in the agent should take precedence for any conflicts. I’m not 100% sold on this design: weigh pros and cons in the comments here
didericis added the Kind/Enhancement label 2026-05-27 23:20:41 -04:00
didericis-claude was assigned by didericis 2026-05-27 23:20:41 -04:00
Collaborator

Weighing the design here. The proposal makes sense ergonomically — single-use agents shouldn't need a whole separate bottle file just to add one env var or egress route — but I think it conflicts with a load-bearing invariant in the current model that's worth pulling apart before deciding.

The trust-boundary concern

From the README's manifest section, emphasis original: "This is the trust boundary: bottle infrastructure — credentials, egress allowlists, git remotes — comes from your home directory only. A cloned repo cannot redirect a host env var to an attacker-named upstream because it has no way to declare a bottle."

Agents currently can live under $CWD/.claude-bottle/agents/ (a cloned repo can ship them). Bottles can ONLY live under $HOME/.claude-bottle/bottles/. The filesystem layout is the security model.

If bottle_config: in an agent file can override / extend egress routes, env vars with token_ref, or git remotes, then a cloned repo's agent file can add *.attacker.com to the egress allowlist, point an existing env var at an attacker-named upstream, or insert a git remote that exfils on push. That's the exact attack the current layout prevents.

Pros

  • Less boilerplate for one-off agents.
  • Locality: agent-specific tweaks sit next to the agent definition.
  • Matches the 1-1 framing for one-off agents.

Cons

  • Erodes the home-only bottle invariant unless restricted.
  • Merge semantics are non-trivial (list-valued fields like egress.routes, git[], env{} — append? override? by-name match?).
  • Two paths for the same outcome (full bottle vs. inline merge) increases the surface for surprising precedence bugs.

Alternatives that preserve the trust boundary

  1. Restrict bottle_config: to $HOME agents only. $CWD agents get a parse-time die if they declare it. Keeps the convenience for the operator's own home-resident agents; the cloned-repo attack stays blocked. Simple rule, easy to enforce in _load_agents_from_dir.
  2. Allow only safe fields in agent bottle_config:. E.g., env literals (no ?prompt resolution to host env), skills, prompt. Forbid egress, git, git_user, supervise. Narrows the blast radius but adds field-by-field allowlist maintenance.
  3. Bottle composition / extends:. Bottle B can extends: A to inherit + override A's config. Bottles stay home-only. Same convenience without touching the agent surface. Most aligned with the current architecture.

Recommendation

Option 3 (extends:) if the underlying pain is "I don't want to duplicate a 50-line bottle for one tweak" — solves the problem without weakening the security model. Option 1 (restrict to $HOME) if the pain is specifically about co-locating one-off agent + bottle config; it's the most minimal-deviation path. Avoid the as-proposed unrestricted form — the convenience win isn't worth a documented trust-boundary regression.

Honest rating of the as-proposed design: 4/10 — the use case is real but the security tradeoff is silently steep. The two alternatives are 8/10 (option 3) and 7/10 (option 1).

Weighing the design here. The proposal makes sense ergonomically — single-use agents shouldn't need a whole separate bottle file just to add one env var or egress route — but I think it conflicts with a load-bearing invariant in the current model that's worth pulling apart before deciding. ## The trust-boundary concern From the README's manifest section, emphasis original: *"**This is the trust boundary**: bottle infrastructure — credentials, egress allowlists, git remotes — comes from your home directory only. A cloned repo cannot redirect a host env var to an attacker-named upstream because it has no way to declare a bottle."* Agents currently can live under `$CWD/.claude-bottle/agents/` (a cloned repo can ship them). Bottles can ONLY live under `$HOME/.claude-bottle/bottles/`. The filesystem layout *is* the security model. If `bottle_config:` in an agent file can override / extend egress routes, env vars with `token_ref`, or git remotes, then a cloned repo's agent file can add `*.attacker.com` to the egress allowlist, point an existing env var at an attacker-named upstream, or insert a git remote that exfils on push. That's the exact attack the current layout prevents. ## Pros - Less boilerplate for one-off agents. - Locality: agent-specific tweaks sit next to the agent definition. - Matches the 1-1 framing for one-off agents. ## Cons - Erodes the home-only bottle invariant unless restricted. - Merge semantics are non-trivial (list-valued fields like `egress.routes`, `git[]`, `env{}` — append? override? by-name match?). - Two paths for the same outcome (full bottle vs. inline merge) increases the surface for surprising precedence bugs. ## Alternatives that preserve the trust boundary 1. **Restrict `bottle_config:` to `$HOME` agents only.** $CWD agents get a parse-time die if they declare it. Keeps the convenience for the operator's own home-resident agents; the cloned-repo attack stays blocked. Simple rule, easy to enforce in `_load_agents_from_dir`. 2. **Allow only safe fields in agent `bottle_config:`.** E.g., `env` literals (no `?prompt` resolution to host env), `skills`, `prompt`. Forbid `egress`, `git`, `git_user`, `supervise`. Narrows the blast radius but adds field-by-field allowlist maintenance. 3. **Bottle composition / `extends:`.** Bottle B can `extends: A` to inherit + override A's config. Bottles stay home-only. Same convenience without touching the agent surface. Most aligned with the current architecture. ## Recommendation Option 3 (`extends:`) if the underlying pain is "I don't want to duplicate a 50-line bottle for one tweak" — solves the problem without weakening the security model. Option 1 (restrict to $HOME) if the pain is specifically about co-locating one-off agent + bottle config; it's the most minimal-deviation path. Avoid the as-proposed unrestricted form — the convenience win isn't worth a documented trust-boundary regression. Honest rating of the as-proposed design: 4/10 — the use case is real but the security tradeoff is silently steep. The two alternatives are 8/10 (option 3) and 7/10 (option 1).
Author
Owner

Makes sense, lets go with option 3

Makes sense, lets go with option 3
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: didericis/bot-bottle#88