Add guest-only workspace secrets
Add explicit workspace secrets across the CLI, SDK, and MCP, with create-time secret definitions and per-call secret-to-env mapping for exec, shell open, and service start. Persist only safe secret metadata in workspace records, materialize secret files under /run/pyro-secrets, and redact secret values from exec output, shell reads, service logs, and surfaced errors. Fix the remaining real-guest shell gap by shipping bundled guest init alongside the guest agent and patching both into guest-backed workspace rootfs images before boot. The new init mounts devpts so PTY shells work on Firecracker guests, while reset continues to recreate the sandbox and re-materialize secrets from stored task-local secret material. Validation: uv lock; UV_CACHE_DIR=.uv-cache make check; UV_CACHE_DIR=.uv-cache make dist-check; and a real guest-backed Firecracker smoke covering workspace create with secrets, secret-backed exec, shell, service, reset, and delete.
This commit is contained in:
parent
18b8fd2a7d
commit
fc72fcd3a1
32 changed files with 1980 additions and 181 deletions
|
|
@ -12,15 +12,31 @@ def main() -> None:
|
|||
tempfile.TemporaryDirectory(prefix="pyro-workspace-seed-") as seed_dir,
|
||||
tempfile.TemporaryDirectory(prefix="pyro-workspace-sync-") as sync_dir,
|
||||
tempfile.TemporaryDirectory(prefix="pyro-workspace-export-") as export_dir,
|
||||
tempfile.TemporaryDirectory(prefix="pyro-workspace-secret-") as secret_dir,
|
||||
):
|
||||
Path(seed_dir, "note.txt").write_text("hello from seed\n", encoding="utf-8")
|
||||
Path(sync_dir, "note.txt").write_text("hello from sync\n", encoding="utf-8")
|
||||
created = pyro.create_workspace(environment="debian:12", seed_path=seed_dir)
|
||||
secret_file = Path(secret_dir, "token.txt")
|
||||
secret_file.write_text("from-file\n", encoding="utf-8")
|
||||
created = pyro.create_workspace(
|
||||
environment="debian:12",
|
||||
seed_path=seed_dir,
|
||||
secrets=[
|
||||
{"name": "API_TOKEN", "value": "expected"},
|
||||
{"name": "FILE_TOKEN", "file_path": str(secret_file)},
|
||||
],
|
||||
)
|
||||
workspace_id = str(created["workspace_id"])
|
||||
try:
|
||||
pyro.push_workspace_sync(workspace_id, sync_dir)
|
||||
result = pyro.exec_workspace(workspace_id, command="cat note.txt")
|
||||
print(result["stdout"], end="")
|
||||
secret_result = pyro.exec_workspace(
|
||||
workspace_id,
|
||||
command='sh -lc \'printf "%s\\n" "$API_TOKEN"\'',
|
||||
secret_env={"API_TOKEN": "API_TOKEN"},
|
||||
)
|
||||
print(secret_result["stdout"], end="")
|
||||
diff_result = pyro.diff_workspace(workspace_id)
|
||||
print(f"changed={diff_result['changed']} total={diff_result['summary']['total']}")
|
||||
snapshot = pyro.create_snapshot(workspace_id, "checkpoint")
|
||||
|
|
@ -28,11 +44,22 @@ def main() -> None:
|
|||
exported_path = Path(export_dir, "note.txt")
|
||||
pyro.export_workspace(workspace_id, "note.txt", output_path=exported_path)
|
||||
print(exported_path.read_text(encoding="utf-8"), end="")
|
||||
shell = pyro.open_shell(workspace_id, secret_env={"API_TOKEN": "API_TOKEN"})
|
||||
shell_id = str(shell["shell_id"])
|
||||
pyro.write_shell(
|
||||
workspace_id,
|
||||
shell_id,
|
||||
input='printf "%s\\n" "$API_TOKEN"',
|
||||
)
|
||||
shell_output = pyro.read_shell(workspace_id, shell_id, cursor=0)
|
||||
print(f"shell_output_len={len(shell_output['output'])}")
|
||||
pyro.close_shell(workspace_id, shell_id)
|
||||
pyro.start_service(
|
||||
workspace_id,
|
||||
"web",
|
||||
command="touch .web-ready && while true; do sleep 60; done",
|
||||
readiness={"type": "file", "path": ".web-ready"},
|
||||
secret_env={"API_TOKEN": "API_TOKEN"},
|
||||
)
|
||||
services = pyro.list_services(workspace_id)
|
||||
print(f"services={services['count']} running={services['running_count']}")
|
||||
|
|
@ -43,6 +70,7 @@ def main() -> None:
|
|||
pyro.stop_service(workspace_id, "web")
|
||||
reset = pyro.reset_workspace(workspace_id, snapshot="checkpoint")
|
||||
print(f"reset_count={reset['reset_count']}")
|
||||
print(f"secret_count={len(reset['secrets'])}")
|
||||
logs = pyro.logs_workspace(workspace_id)
|
||||
print(f"workspace_id={workspace_id} command_count={logs['count']}")
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue