01cee056be
test / integration-docker (push) Successful in 45s
test / unit (push) Successful in 48s
Update Quality Badges / update-badges (push) Failing after 52s
test / integration-firecracker (push) Successful in 5m27s
test / coverage (push) Failing after 29s
test / publish-infra (push) Has been skipped
lint / lint (push) Successful in 2m31s
Implements the interim secret-provider design (PRD prd-new-secret-provider): each agent receives a random ENV_VAR_SECRET injected into its container env at launch. The host uses this key to encrypt each egress auth token value (HMAC-SHA256 CTR mode, stdlib-only) and store it in a new bottled_agent_secrets table (one row per env var, key column plaintext for auditing). The key never touches the DB. On infra container restart the in-memory token map is lost. launch_consolidated now calls _reprovision_running_bottles after ensure_running: for each registered bottle still alive on the gateway network it execs `printenv ENV_VAR_SECRET` into the agent container and posts the result to the new POST /bottles/<id>/reprovision_gateway control-plane endpoint, which decrypts the stored rows and restores _tokens — no manual intervention needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
89 lines
3.3 KiB
Python
89 lines
3.3 KiB
Python
"""DockerBottlePlan — concrete subclass of BottlePlan.
|
|
|
|
Carries the Docker-specific resolved fields produced by
|
|
DockerBottleBackend.prepare. The launch step consumes it without
|
|
further resolution; preflight rendering is inherited from BottlePlan.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from pathlib import Path
|
|
|
|
from ...agent_provider import PromptMode
|
|
from .. import BottlePlan
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class DockerBottlePlan(BottlePlan):
|
|
"""Docker-specific resolved fields produced by
|
|
DockerBottleBackend.prepare. Inherits `spec`, `stage_dir`,
|
|
`git_gate_plan`, `egress_plan`, `supervise_plan`, and
|
|
`agent_provision` from BottlePlan."""
|
|
|
|
slug: str
|
|
# name -> value for vars forwarded into the docker-run child process
|
|
# via subprocess env (so values never land on argv or in a file).
|
|
# repr=False keeps secret/interpolated/OAuth values out of any
|
|
# accidental log of the plan dataclass.
|
|
forwarded_env: dict[str, str] = field(repr=False)
|
|
use_runsc: bool
|
|
# Consolidated mode (PRD 0070): the agent reaches git-gate over HTTP at the
|
|
# shared gateway's address (`http://<gateway_ip>:9420`), set at launch.
|
|
# Empty → single-tenant defaults (the `git-gate` alias over git://).
|
|
agent_git_gate_url: str = ""
|
|
# Likewise the supervise MCP endpoint at the gateway (`http://<gw>:9100/`);
|
|
# empty → the single-tenant `supervise` alias.
|
|
agent_supervise_url: str = ""
|
|
# Per-bottle identity token the agent presents on every attributed request
|
|
# (egress proxy credentials, git-gate/supervise headers); set by launch
|
|
# from the orchestrator registration. Empty pre-registration.
|
|
identity_token: str = ""
|
|
# Encryption key for the agent's stored egress secrets; injected into the
|
|
# agent container as ENV_VAR_SECRET via the compose subprocess env (bare
|
|
# name — value never written to the compose file). Empty pre-registration.
|
|
env_var_secret: str = ""
|
|
|
|
@property
|
|
def container_name(self) -> str:
|
|
return self.agent_provision.instance_name
|
|
|
|
@property
|
|
def git_gate_insteadof_host(self) -> str:
|
|
if self.agent_git_gate_url.startswith("http://"):
|
|
return self.agent_git_gate_url.removeprefix("http://").rstrip("/")
|
|
return super().git_gate_insteadof_host
|
|
|
|
@property
|
|
def git_gate_insteadof_scheme(self) -> str:
|
|
if self.agent_git_gate_url.startswith("http://"):
|
|
return "http"
|
|
return super().git_gate_insteadof_scheme
|
|
|
|
@property
|
|
def image(self) -> str:
|
|
return self.agent_provision.image
|
|
|
|
@property
|
|
def dockerfile_path(self) -> str:
|
|
"""Absolute path to the Dockerfile that builds `image`. Sourced
|
|
from the agent provision plan — the manifest may override per
|
|
bottle; otherwise the provider plugin's bundled Dockerfile."""
|
|
return self.agent_provision.dockerfile
|
|
|
|
@property
|
|
def prompt_file(self) -> Path:
|
|
return self.agent_provision.prompt_file
|
|
|
|
@property
|
|
def agent_command(self) -> str:
|
|
return self.agent_provision.command
|
|
|
|
@property
|
|
def agent_prompt_mode(self) -> PromptMode:
|
|
return self.agent_provision.prompt_mode
|
|
|
|
@property
|
|
def agent_provider_template(self) -> str:
|
|
return self.agent_provision.template
|