terok_sandbox
terok_sandbox
¶
terok-sandbox: hardened Podman container runner with gate and shield integration.
Public API for standalone use and integration with terok.
The primary configuration type is :class:SandboxConfig:
>>> from terok_sandbox import SandboxConfig
>>> cfg = SandboxConfig(gate_port=9418)
READY_MARKER = '>> init complete'
module-attribute
¶
Default log line emitted by init-ssh-and-repo.sh when the container is ready.
CommandDef(name, help='', handler=None, args=(), group='')
dataclass
¶
Definition of a sandbox subcommand.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Subcommand name (e.g. |
help |
str
|
One-line help string. |
handler |
Callable[..., None] | None
|
Callable implementing the command. |
args |
tuple[ArgDef, ...]
|
Argument definitions. |
group |
str
|
Command group (e.g. |
SandboxConfig(state_dir=_state_root(), runtime_dir=_runtime_root(), config_dir=_config_root(), credentials_dir=_credentials_root(), gate_port=9418, proxy_port=18731, ssh_agent_port=18732, shield_profiles=('dev-standard',), shield_audit=True, shield_bypass=False)
dataclass
¶
Immutable configuration for the sandbox layer.
All paths default to the XDG/FHS-resolved values from :mod:paths.
Override individual fields when constructing from terok's global config
or when using terok-sandbox standalone.
state_dir = field(default_factory=_state_root)
class-attribute
instance-attribute
¶
Writable state root (tokens, gate repos, task data).
runtime_dir = field(default_factory=_runtime_root)
class-attribute
instance-attribute
¶
Transient runtime directory (PID files, sockets).
config_dir = field(default_factory=_config_root)
class-attribute
instance-attribute
¶
Sandbox-scoped configuration root.
Note: shield profiles are resolved by :attr:shield_profiles_dir
via :func:~terok_sandbox.paths.umbrella_config_root, not from
this directory.
credentials_dir = field(default_factory=_credentials_root)
class-attribute
instance-attribute
¶
Shared credentials directory (DB, routes, env mounts).
gate_port = 9418
class-attribute
instance-attribute
¶
HTTP port for the gate server.
proxy_port = 18731
class-attribute
instance-attribute
¶
TCP port for the credential proxy (container access).
ssh_agent_port = 18732
class-attribute
instance-attribute
¶
TCP port for the SSH agent proxy (container access).
shield_profiles = ('dev-standard',)
class-attribute
instance-attribute
¶
Shield egress firewall profile names.
shield_audit = True
class-attribute
instance-attribute
¶
Whether shield audit logging is enabled.
shield_bypass = False
class-attribute
instance-attribute
¶
DANGEROUS: when True, the egress firewall is completely disabled.
gate_base_path
property
¶
Return the gate server's repo base path.
token_file_path
property
¶
Return the path to the gate token file.
pid_file_path
property
¶
Return the PID file path for the managed gate daemon.
shield_profiles_dir
property
¶
Return the directory for terok-managed shield profiles.
proxy_db_path
property
¶
Return the path to the credential proxy sqlite3 database.
proxy_socket_path
property
¶
Return the Unix socket path for the credential proxy.
proxy_pid_file_path
property
¶
Return the PID file path for the managed credential proxy daemon.
proxy_routes_path
property
¶
Return the path to the proxy route configuration JSON.
ssh_keys_dir
property
¶
Return the base directory for per-project SSH keys.
ssh_keys_json_path
property
¶
Return the path to the SSH key mapping JSON.
CredentialDB(db_path)
¶
SQLite-backed credential store and phantom token registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db_path
|
Path
|
Path to the sqlite3 database file. Parent directories are created automatically. |
required |
Source code in src/terok_sandbox/credential_db.py
store_credential(credential_set, provider, data)
¶
Insert or replace a credential entry.
Source code in src/terok_sandbox/credential_db.py
load_credential(credential_set, provider)
¶
Return the credential dict, or None if not found.
Source code in src/terok_sandbox/credential_db.py
list_credentials(credential_set)
¶
Return provider names that have stored credentials.
Source code in src/terok_sandbox/credential_db.py
delete_credential(credential_set, provider)
¶
Remove a credential entry (idempotent).
Source code in src/terok_sandbox/credential_db.py
create_proxy_token(project, task, credential_set, provider)
¶
Create a per-task, per-provider phantom token.
Token format: terok-p-<32 hex chars>.
Source code in src/terok_sandbox/credential_db.py
lookup_proxy_token(token)
¶
Return {project, task, credential_set, provider} or None.
Source code in src/terok_sandbox/credential_db.py
revoke_proxy_tokens(project, task)
¶
Revoke all tokens for a project/task pair. Returns count revoked.
Source code in src/terok_sandbox/credential_db.py
close()
¶
CredentialProxyStatus(mode, running, healthy, socket_path, db_path, routes_path, routes_configured, credentials_stored)
dataclass
¶
Current state of the credential proxy.
mode
instance-attribute
¶
"systemd", "daemon", or "none".
running
instance-attribute
¶
Whether the proxy is active (systemd socket listening or daemon alive).
healthy
instance-attribute
¶
Whether the proxy responded to an HTTP health check.
socket_path
instance-attribute
¶
Configured Unix socket path.
db_path
instance-attribute
¶
Configured credential database path.
routes_path
instance-attribute
¶
Configured proxy routes JSON path.
routes_configured
instance-attribute
¶
Number of routes in routes.json (0 if missing or invalid).
credentials_stored
instance-attribute
¶
Provider names with stored credentials.
CheckVerdict(severity, detail, fixable=False)
dataclass
¶
DoctorCheck(category, label, probe_cmd, evaluate, fix_cmd=None, fix_description='', host_side=False)
dataclass
¶
A single health check to run inside (or against) a container.
The probe_cmd is executed via podman exec <cname> ... by the
orchestrator. The evaluate callable interprets the result.
If fix_cmd is set, the orchestrator may offer it when the check
fails with fixable=True.
Dual execution modes:
- Container mode (
host_side=False): the orchestrator runsprobe_cmdviapodman execand passes the result toevaluate. The standalonedoctorcommand runs the sameprobe_cmddirectly viasubprocesson the host. - Host-side mode (
host_side=True): the orchestrator bypassesprobe_cmdentirely and performs the check via Python APIs (e.g.make_shield), then passes resolved state toevaluate. The standalonedoctorcommand callsevaluate(0, "", "")and the function performs the check itself or reports a neutral result.
category
instance-attribute
¶
Grouping key: "bridge", "env", "mount", "network",
"shield", "git".
label
instance-attribute
¶
Human-readable check name shown in output.
probe_cmd
instance-attribute
¶
Shell command to run inside the container via podman exec.
evaluate
instance-attribute
¶
(returncode, stdout, stderr) → CheckVerdict.
fix_cmd = None
class-attribute
instance-attribute
¶
Optional remediation command for podman exec.
fix_description = ''
class-attribute
instance-attribute
¶
Shown to the operator before applying the fix.
host_side = False
class-attribute
instance-attribute
¶
If True, the check runs on the host (not via podman exec).
The orchestrator calls evaluate(0, "", "") and the evaluate
function performs the host-side check itself.
GateServerStatus(mode, running, port)
dataclass
¶
GateStalenessInfo(branch, gate_head, upstream_head, is_stale, commits_behind, commits_ahead, last_checked, error)
dataclass
¶
Result of comparing gate vs upstream.
GitGate(*, project_id, gate_path, upstream_url=None, default_branch=None, ssh_host_dir=None, ssh_key_name=None, validate_gate_fn=None)
¶
Repository + Gateway for a host-side git gate mirror.
Manages the bare git mirror that containers clone from. Provides operations for initial creation, incremental sync from upstream, selective branch fetching, and staleness detection.
Constructor takes plain parameters — no terok-specific types.
Initialise with plain parameters.
Parameters¶
project_id:
Identifier for this gate's owner.
gate_path:
Path to the bare git mirror on the host.
upstream_url:
Git upstream URL to sync from.
default_branch:
Branch name used for staleness comparisons.
ssh_host_dir:
Explicit SSH directory for git operations. When None,
falls back to SandboxConfig().ssh_keys_dir / project_id.
ssh_key_name:
Explicit SSH key filename.
validate_gate_fn:
Optional callback (project_id) -> None that validates no other
project uses the same gate with a different upstream. Injected by
the orchestration layer; omitted for standalone use.
Source code in src/terok_sandbox/git_gate.py
sync(branches=None, force_reinit=False)
¶
Sync the host-side git mirror gate.
- Uses SSH configuration via GIT_SSH_COMMAND.
- If gate doesn't exist (or force_reinit), performs a fresh
git clone --mirror. - Always runs the sync logic afterward for consistent side effects.
Returns:
| Type | Description |
|---|---|
GateSyncResult
|
Dict with keys: path, upstream_url, created (bool), success, |
GateSyncResult
|
updated_branches, errors. |
Source code in src/terok_sandbox/git_gate.py
sync_branches(branches=None)
¶
Sync specific branches in the gate from upstream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
branches
|
list[str] | None
|
List of branches to sync (default: all via remote update) |
None
|
Returns:
| Type | Description |
|---|---|
BranchSyncResult
|
Dict with keys: success, updated_branches, errors |
Source code in src/terok_sandbox/git_gate.py
compare_vs_upstream(branch=None)
¶
Compare gate HEAD vs upstream HEAD for a branch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
branch
|
str | None
|
Branch to compare (default: configured default_branch) |
None
|
Returns:
| Type | Description |
|---|---|
GateStalenessInfo
|
GateStalenessInfo with comparison results |
Source code in src/terok_sandbox/git_gate.py
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | |
last_commit()
¶
Get information about the last commit on the configured branch.
Returns None if the gate doesn't exist or is not accessible.
Source code in src/terok_sandbox/git_gate.py
GpuConfigError(message, *, hint=_CDI_HINT)
¶
Bases: RuntimeError
CDI/NVIDIA misconfiguration detected during container launch.
Store the CDI hint alongside the standard error message.
Source code in src/terok_sandbox/runtime.py
LifecycleHooks(pre_start=None, post_start=None, post_ready=None, post_stop=None)
dataclass
¶
Optional callbacks fired at container lifecycle transitions.
All slots are None by default. Sandbox.run() fires pre_start
before podman run and post_start after a successful launch.
post_ready and post_stop are available for callers to invoke at
the appropriate time (e.g. after log streaming or container exit).
pre_start = None
class-attribute
instance-attribute
¶
Fired before podman run.
post_start = None
class-attribute
instance-attribute
¶
Fired after a successful podman run.
post_ready = None
class-attribute
instance-attribute
¶
Fired when the container reports ready (caller responsibility).
post_stop = None
class-attribute
instance-attribute
¶
Fired after the container exits (caller responsibility).
RunSpec(container_name, image, env, volumes, command, task_dir, gpu_enabled=False, extra_args=(), unrestricted=True)
dataclass
¶
Everything needed for a single podman run invocation.
container_name
instance-attribute
¶
Unique container name.
image
instance-attribute
¶
Image tag to run (e.g. terok-l1-cli:ubuntu-24.04).
env
instance-attribute
¶
Environment variables injected into the container.
volumes
instance-attribute
¶
Volume mount strings (host:container[:opts]).
command
instance-attribute
¶
Command to execute inside the container.
task_dir
instance-attribute
¶
Host-side task directory (for shield state, logs, etc.).
gpu_enabled = False
class-attribute
instance-attribute
¶
Whether to pass GPU device args to podman.
extra_args = ()
class-attribute
instance-attribute
¶
Additional podman run arguments (e.g. port publishing).
unrestricted = True
class-attribute
instance-attribute
¶
When False, adds --security-opt no-new-privileges.
Sandbox(config=None)
¶
Stateless facade composing sandbox primitives.
All methods delegate to the module-level functions in this package,
passing the stored :class:SandboxConfig. The existing function-level
API remains the canonical interface — this class is a convenience for
callers that manage a config instance.
Source code in src/terok_sandbox/sandbox.py
config
property
¶
Return the sandbox configuration.
ensure_gate()
¶
Verify the gate server is running; raise SystemExit if not.
create_token(project_id, task_id)
¶
Create a task-scoped gate access token.
gate_url(repo_path, token)
¶
Build an HTTP URL for gate access to repo_path.
Source code in src/terok_sandbox/sandbox.py
gate_status()
¶
pre_start_args(container, task_dir)
¶
Return extra podman args for shield integration.
shield_down(container, task_dir)
¶
Remove shield rules for a container (allow all egress).
run(spec, *, hooks=None)
¶
Launch a detached container from spec.
Assembles and executes the podman run command, handling user
namespace mapping, shield or bypass networking, GPU device args,
environment and volume injection, CDI error detection, and lifecycle
hook callbacks.
Fires hooks.pre_start before podman run and hooks.post_start
after a successful launch. Raises :class:~.runtime.GpuConfigError
when the launch fails due to NVIDIA CDI misconfiguration.
Source code in src/terok_sandbox/sandbox.py
stream_logs(container, *, timeout=None, ready_check=None)
¶
Stream container logs until ready_check matches or timeout.
Source code in src/terok_sandbox/sandbox.py
wait_for_exit(container, timeout=None)
¶
Block until container exits; return exit code.
stop(containers)
¶
SSHManager(*, project_id, ssh_host_dir=None, ssh_key_name=None, ssh_config_template=None)
¶
SSH keypair generation and config directory management.
Handles the full SSH setup lifecycle: directory creation, keypair
generation (ed25519 or RSA), config file rendering from templates, and
permission hardening. Keys are stored under ssh_keys_dir/<project>
and used by the credential proxy's SSH agent for container access.
Initialize with plain parameters.
Parameters¶
project_id:
Identifier used for key naming and directory layout.
ssh_host_dir:
Explicit SSH directory (overrides default <ssh_keys_dir>/<id>).
ssh_key_name:
Explicit key filename (overrides derived id_<type>_<id>).
ssh_config_template:
Path to a user-provided SSH config template file.
Source code in src/terok_sandbox/ssh.py
key_name
property
¶
Return the effective SSH key name.
init(key_type='ed25519', key_name=None, force=False)
¶
Initialize the SSH directory and generate a keypair.
Location resolution
- If ssh_host_dir was provided, use that path.
- Otherwise:
<ssh_keys_dir>/<project_id>
Key name defaults to id_<type>_<project_id> (e.g. id_ed25519_proj).
Source code in src/terok_sandbox/ssh.py
ensure_proxy_reachable(cfg=None)
¶
Verify the credential proxy is running and its TCP ports are up.
For systemd socket activation the service may not have started yet
(e.g. after a fresh boot). This function triggers a start via
systemctl --user start and waits for the HTTP and SSH agent TCP
ports to become reachable via /-/health and raw TCP probes.
For daemon mode the /-/health endpoint is probed on the TCP port.
Raises SystemExit with an actionable message if the proxy is
unreachable. Called before task creation when credential proxy is enabled.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
get_proxy_port(cfg=None)
¶
get_proxy_status(cfg=None)
¶
Return the current credential proxy status.
Populates route count from the routes JSON (0 if missing/invalid) and credential provider names from the database (empty if DB doesn't exist).
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
get_ssh_agent_port(cfg=None)
¶
install_proxy_systemd(cfg=None)
¶
Render and install systemd socket+service units, then enable+start the socket.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
is_proxy_running(cfg=None)
¶
Check whether the managed proxy daemon is alive via its PID file.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
is_proxy_service_active()
¶
Check whether the terok-credential-proxy.service unit is active.
Unlike :func:is_socket_active, this tells whether the proxy daemon
itself is running (TCP ports bound), not just whether the socket is
listening. Does not trigger socket activation.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
is_proxy_socket_active()
¶
Check whether the terok-credential-proxy.socket unit is active.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
is_proxy_socket_installed()
¶
Check whether the terok-credential-proxy.socket unit file exists.
is_proxy_systemd_available()
¶
Check whether the systemd user session is reachable.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
start_proxy(cfg=None)
¶
Start the credential proxy as a background daemon.
The proxy listens on a Unix socket and reads credentials from a sqlite3 database. A routes JSON file must exist at the configured path (generated by terok-agent from the YAML registry).
Writes a PID file to runtime_root() / "credential-proxy.pid".
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | |
stop_proxy(cfg=None)
¶
Stop the managed proxy daemon by sending SIGTERM.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
uninstall_proxy_systemd(cfg=None)
¶
Disable+stop the socket and remove unit files.
Source code in src/terok_sandbox/credential_proxy_lifecycle.py
sandbox_doctor_checks(*, proxy_port=None, ssh_agent_port=None, desired_shield_state=None)
¶
Return sandbox-level health checks for in-container diagnostics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proxy_port
|
int | None
|
Credential proxy TCP port (skip check if |
None
|
ssh_agent_port
|
int | None
|
SSH agent TCP port (skip check if |
None
|
desired_shield_state
|
str | None
|
Expected shield state from |
None
|
Returns:
| Type | Description |
|---|---|
list[DoctorCheck]
|
List of :class: |
Source code in src/terok_sandbox/doctor.py
check_units_outdated(cfg=None)
¶
Return a warning string if installed systemd units are stale, else None.
Checks both the unit version stamp and the baked --base-path against
the current configuration. Useful for gate-server status and
sickbay to surface upgrade hints without blocking task creation
(that's ensure_server_reachable's job).
Source code in src/terok_sandbox/gate_server.py
ensure_server_reachable(cfg=None)
¶
Verify the gate server is running and configured correctly.
Raises SystemExit if the server is down, systemd units are outdated,
or the installed base path diverges from the current configuration.
Called before task creation to fail early with an actionable message.
Source code in src/terok_sandbox/gate_server.py
get_gate_base_path(cfg=None)
¶
get_gate_server_port(cfg=None)
¶
get_server_status(cfg=None)
¶
Return the current gate server status.
Source code in src/terok_sandbox/gate_server.py
install_systemd_units(cfg=None)
¶
Render and install systemd socket+service units, then enable+start the socket.
Source code in src/terok_sandbox/gate_server.py
is_daemon_running(cfg=None)
¶
Check whether the managed daemon process is alive via its PID file.
Source code in src/terok_sandbox/gate_server.py
is_systemd_available()
¶
Check whether systemctl --user is usable.
Uses is-system-running which returns well-defined exit codes:
0 = running, 1 = degraded/starting/stopping — both mean systemd is
present. Any other code (or missing binary) means unavailable.
Source code in src/terok_sandbox/gate_server.py
start_daemon(port=None, cfg=None)
¶
Start a terok-gate daemon process (non-systemd fallback).
Writes a PID file to runtime_root() / "gate-server.pid".
If TEROK_GATE_ADMIN_TOKEN is set in the environment, it is
forwarded to the daemon for host-level access to all repos.
Source code in src/terok_sandbox/gate_server.py
stop_daemon(cfg=None)
¶
Stop the managed daemon by reading the PID file and sending SIGTERM.
Source code in src/terok_sandbox/gate_server.py
uninstall_systemd_units(cfg=None)
¶
Disable+stop the socket and remove unit files.
Source code in src/terok_sandbox/gate_server.py
create_token(project_id, task_id, cfg=None)
¶
Generate a 128-bit hex token, persist atomically, and return it.
Uses secrets.token_hex(16) for cryptographic randomness.
Atomic write via tempfile + os.replace().
Source code in src/terok_sandbox/gate_tokens.py
revoke_token_for_task(project_id, task_id, cfg=None)
¶
Remove all tokens for the given project+task pair. Idempotent.
Source code in src/terok_sandbox/gate_tokens.py
credentials_root()
¶
Shared credentials directory used by all terok ecosystem packages.
Priority: TEROK_CREDENTIALS_DIR → /var/lib/terok/credentials (root)
→ XDG data dir.
Source code in src/terok_sandbox/paths.py
umbrella_config_root()
¶
Return the top-level terok config root (umbrella, not sandbox-scoped).
Used for cross-package paths like shield profiles that live under
the shared ~/.config/terok/ umbrella rather than under any single
package's config directory.
Source code in src/terok_sandbox/paths.py
bypass_network_args(gate_port)
¶
Return podman network args for running without shield.
Replicates the networking that terok-shield's OCI hook normally provides
(allowing the container to reach host.containers.internal for the gate
server) but without nftables rules, annotations, or cap-drops.
This is a dangerous fallback for environments where shield can't run. All egress is unfiltered.
Source code in src/terok_sandbox/runtime.py
find_free_port(host='127.0.0.1')
¶
Find and return a free TCP port on host.
Releases the socket immediately — there is a small race window before
the caller binds the port. This is the standard approach when passing
a port number to an external process (e.g. podman run -p).
Source code in src/terok_sandbox/runtime.py
get_container_state(cname)
¶
Return container state ('running', 'exited', ...) or None if not found.
Source code in src/terok_sandbox/runtime.py
get_project_container_states(name_prefix)
¶
Return {container_name: state} for all containers matching name_prefix.
Uses a single podman ps -a call with a name filter instead of
per-container podman inspect calls. Returns an empty dict when
podman is unavailable.
Source code in src/terok_sandbox/runtime.py
gpu_run_args(*, enabled=False)
¶
Return additional podman run args to enable NVIDIA GPU passthrough.
The caller is responsible for determining whether GPUs are enabled (e.g. by reading project configuration). This function only maps the boolean flag to the appropriate podman CLI arguments.
Source code in src/terok_sandbox/runtime.py
is_container_running(cname)
¶
Return True if the named container is currently running.
Source code in src/terok_sandbox/runtime.py
podman_userns_args()
¶
Return user namespace args for rootless podman so UID 1000 maps correctly.
Maps the host user to container UID/GID 1000, the conventional non-root
dev user in terok container images.
Source code in src/terok_sandbox/runtime.py
redact_env_args(cmd)
¶
Return a copy of cmd with sensitive -e KEY=VALUE args redacted.
Handles the two-arg form (-e KEY=VALUE) produced by
:meth:~.sandbox.Sandbox.run. Does not handle --env,
-e=KEY=VALUE, or --env=KEY=VALUE — callers passing sensitive
values via extra_args must pre-redact them.
Source code in src/terok_sandbox/runtime.py
reserve_free_port(host='127.0.0.1')
¶
Reserve a TCP port on host and return (socket, port).
The socket stays open — the caller holds the reservation until they close it (typically right before binding the actual service). Useful for Python-native servers that can accept a pre-bound socket.
Source code in src/terok_sandbox/runtime.py
stop_task_containers(container_names)
¶
Best-effort podman rm -f of the given containers.
Ignores all errors so that task deletion succeeds even when podman is absent or the containers are already gone.
Source code in src/terok_sandbox/runtime.py
stream_initial_logs(container_name, timeout_sec, ready_check)
¶
Stream logs until ready marker is seen or timeout.
Returns True if the ready marker was found, False on timeout.
Source code in src/terok_sandbox/runtime.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | |
wait_for_exit(cname, timeout_sec=None)
¶
Wait for a container to exit and return its exit code.
Returns 124 on timeout, 1 if podman is not found.
Source code in src/terok_sandbox/runtime.py
check_environment(cfg=None)
¶
Check the podman environment for shield compatibility.
Returns a synthetic :class:EnvironmentCheck with bypass info when the
dangerous bypass override is active.
Source code in src/terok_sandbox/shield.py
down(container, task_dir, *, allow_all=False, cfg=None)
¶
Set shield to bypass mode (allow egress) for a running container.
When allow_all is True, also permits private-range (RFC 1918) traffic.
Source code in src/terok_sandbox/shield.py
make_shield(task_dir, cfg=None)
¶
Construct a per-task :class:Shield from sandbox configuration.
Builds a :class:ShieldConfig with state_dir scoped to task_dir.
Source code in src/terok_sandbox/shield.py
pre_start(container, task_dir, cfg=None)
¶
Return extra podman run args for egress firewalling.
Returns an empty list (no firewall args) when the dangerous
bypass_firewall_no_protection override is active.
Raises :class:SystemExit with setup instructions when the
podman environment requires one-time hook installation.
Source code in src/terok_sandbox/shield.py
run_setup(*, root=False, user=False)
¶
Install global OCI hooks for shield egress firewalling.
Global hooks are required on all podman versions to survive container stop/start cycles (terok-shield#122).
Raises :class:SystemExit when neither --root nor --user is given.
Source code in src/terok_sandbox/shield.py
setup_hooks_direct(*, root=False)
¶
Install global hooks via the terok-shield Python API (no subprocess).
Suitable for TUI callers that need direct control. Installs hooks to the system directory (with sudo) when root is True, otherwise to the user directory.
Source code in src/terok_sandbox/shield.py
state(container, task_dir, cfg=None)
¶
Return the live shield state for a running container.
Queries actual nft state even when bypass is set, because containers started before bypass was enabled may still have active rules.
Source code in src/terok_sandbox/shield.py
status(cfg=None)
¶
Return shield status dict from the sandbox configuration.
Source code in src/terok_sandbox/shield.py
up(container, task_dir, cfg=None)
¶
Set shield to deny-all mode for a running container.
generate_keypair(key_type, priv_path, pub_path, comment)
¶
Generate an SSH keypair via ssh-keygen.
Removes any stale half-existing files first, then invokes
ssh-keygen with the given comment embedded in the public key.
Source code in src/terok_sandbox/ssh.py
update_ssh_keys_json(keys_json_path, project_id, result)
¶
Update the SSH key mapping JSON with a project's key paths.
The JSON file maps project IDs to their SSH key file paths, similar
to how routes.json maps provider names to proxy routes. The
credential proxy's SSH agent handler reads this file to locate the
private key for signing requests.
Key management rules (keyed by private_key path):
- No existing entry: write a single-dict entry (simple case).
- Same private_key path: replace in-place (idempotent re-run of
ssh-init). - Different private_key path: expand to / append to a list, so a project can hold multiple independent SSH keys (e.g. GitHub + GitLab).
Uses fcntl.flock to prevent concurrent ssh-init invocations
from corrupting the file.