diff --git a/docs/advanced/configuration.mdx b/docs/advanced/configuration.mdx
index 2048eefe6..362ee2683 100644
--- a/docs/advanced/configuration.mdx
+++ b/docs/advanced/configuration.mdx
@@ -175,7 +175,11 @@ When remote vars are set, Strix dual-writes telemetry to both local JSONL and th
- Docker daemon address, for example `unix:///var/run/docker.sock` or `tcp://10.0.0.5:2375`. When not set, Strix connects to the same daemon as the `docker` CLI: the current docker context (`docker context use`), then the default socket. Docker Desktop, OrbStack and Colima register their sockets as contexts, so no extra configuration is needed for them.
+ Docker daemon address, for example `unix:///var/run/docker.sock` or `tcp://10.0.0.5:2375`. When not set, Strix uses `DOCKER_CONTEXT`, then the saved current context (`docker context use`), then the default socket. Docker Desktop, OrbStack and Colima register their sockets as contexts, so no extra configuration is needed for them.
+
+
+
+ Select a Docker context for this shell without changing the saved current context. Used when `DOCKER_HOST` is unset. A missing context is an error; Strix does not silently fall back to another daemon. Set to `default` to use the SDK's default connection settings.
diff --git a/strix/runtime/docker_connection.py b/strix/runtime/docker_connection.py
index 890d54b1e..bc3fcf7cc 100644
--- a/strix/runtime/docker_connection.py
+++ b/strix/runtime/docker_connection.py
@@ -44,13 +44,13 @@ def detail(self) -> str:
def resolve_docker_endpoint(environ: dict[str, str] | None = None) -> DockerEndpoint:
- """``DOCKER_HOST``, else the current docker context, else the SDK default."""
+ """``DOCKER_HOST``, shell/saved context, then the SDK default."""
env = os.environ if environ is None else environ
host = env.get("DOCKER_HOST", "").strip()
if host:
return DockerEndpoint(host, "DOCKER_HOST")
- name = get_current_context_name()
+ name = env.get("DOCKER_CONTEXT", "").strip() or get_current_context_name()
if name == DEFAULT_CONTEXT:
return DockerEndpoint(None, "default socket")
diff --git a/tests/test_docker_connection.py b/tests/test_docker_connection.py
index 5ea73dc8e..11edcf799 100644
--- a/tests/test_docker_connection.py
+++ b/tests/test_docker_connection.py
@@ -2,9 +2,11 @@
from __future__ import annotations
+import hashlib
import importlib
+import json
from types import SimpleNamespace
-from typing import Any, cast
+from typing import TYPE_CHECKING, Any, cast
import pytest
from docker.errors import DockerException
@@ -19,6 +21,10 @@
)
+if TYPE_CHECKING:
+ from pathlib import Path
+
+
cli_utils: Any = importlib.import_module("strix.interface.utils")
@@ -34,7 +40,9 @@ def _sdk_error(inner: BaseException) -> DockerException:
def test_docker_host_wins_over_context(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(docker_connection, "get_current_context_name", lambda: "desktop-linux")
- endpoint = resolve_docker_endpoint({"DOCKER_HOST": "tcp://10.0.0.5:2375"})
+ endpoint = resolve_docker_endpoint(
+ {"DOCKER_HOST": "tcp://10.0.0.5:2375", "DOCKER_CONTEXT": "selected"}
+ )
assert endpoint == DockerEndpoint("tcp://10.0.0.5:2375", "DOCKER_HOST")
@@ -56,6 +64,61 @@ def test_default_context_uses_the_sdk_default(monkeypatch: pytest.MonkeyPatch) -
assert resolve_docker_endpoint({}) == DockerEndpoint(None, "default socket")
+@pytest.mark.parametrize(
+ ("selected", "expected_host"),
+ [
+ ("selected", "unix:///tmp/strix-selected.sock"),
+ ("default", None),
+ ("", "unix:///tmp/strix-saved.sock"),
+ ],
+)
+def test_shell_context_overrides_the_saved_context(
+ monkeypatch: pytest.MonkeyPatch,
+ tmp_path: Path,
+ selected: str,
+ expected_host: str | None,
+) -> None:
+ """Read a real Docker context store, without contacting any daemon."""
+ (tmp_path / "config.json").write_text(json.dumps({"currentContext": "saved"}))
+ for name in ("saved", "selected"):
+ context_id = hashlib.sha256(name.encode()).hexdigest()
+ metadata = tmp_path / "contexts" / "meta" / context_id / "meta.json"
+ metadata.parent.mkdir(parents=True)
+ metadata.write_text(
+ json.dumps(
+ {
+ "Name": name,
+ "Metadata": {},
+ "Endpoints": {
+ "docker": {
+ "Host": f"unix:///tmp/strix-{name}.sock",
+ "SkipTLSVerify": True,
+ }
+ },
+ }
+ )
+ )
+ monkeypatch.setenv("DOCKER_CONFIG", str(tmp_path))
+ monkeypatch.setenv("DOCKER_CONTEXT", selected)
+ monkeypatch.delenv("DOCKER_HOST", raising=False)
+
+ assert resolve_docker_endpoint().host == expected_host
+
+
+def test_missing_shell_context_does_not_fall_back_to_saved_context(
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path
+) -> None:
+ (tmp_path / "config.json").write_text(json.dumps({"currentContext": "default"}))
+ monkeypatch.setenv("DOCKER_CONFIG", str(tmp_path))
+ monkeypatch.setenv("DOCKER_CONTEXT", "missing")
+ monkeypatch.delenv("DOCKER_HOST", raising=False)
+
+ with pytest.raises(
+ DockerConnectionError, match="docker context 'missing' is selected but does not exist"
+ ):
+ resolve_docker_endpoint()
+
+
def test_missing_or_broken_current_context_is_an_error(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(docker_connection, "get_current_context_name", lambda: "gone")
monkeypatch.setattr(