daimon-sdk v0.4.1

Python SDK for DAIMON sandbox manager. Create manager-backed sandboxes, connect to MCP endpoints, and call typed file, exec, web, and raw APIs.

Installation

pip install daimon-sdk

Requires Python 3.11+. The SDK uses httpx for HTTP transport and supports both manager-backed and direct MCP connections.

Start Manager Locally

The fastest local path is the published manager Docker image plus the release compose file. This starts processd-sandbox-manager,nsjail, and sandbox MCP workers on demand.

# Replace LAN_IP with your host machine's actual LAN IP (e.g. 192.168.1.100)
# Do NOT use 0.0.0.0 — the MCP workers bind inside nsjail and won't route correctly
# Alternatively, uncomment network_mode: host in the compose file to bypass Docker NAT
PROCESSD_MANAGER_PUBLIC_MCP_HOST=LAN_IP docker compose -f compose.manager.release.yaml up -d

curl -i http://127.0.0.1:18080/health
Docker permissions

The release compose defaults to cgroup_required. It needs cgroup v2 mounted writable plus SYS_ADMIN, SETUID, SETGID, SETFCAP, and DAC_OVERRIDE so nsjail can create per-sandbox namespaces and cgroups. If your local Docker environment cannot provide those permissions, use a best-effort manager compose from processd-standalone or run the manager with adjusted limits.

Quickstart

Use DaimonManagerClient when your application owns sandbox lifecycle. The context manager creates a sandbox, connects to its MCP server, and deletes it on exit by default.

import asyncio

from daimon_sdk import DaimonManagerClient


async def main() -> None:
    async with DaimonManagerClient("http://127.0.0.1:18080") as manager:
        async with manager.sandbox() as sandbox:
            runtime = await sandbox.runtime.get_context()
            print(runtime.base_workdir)

            result = await sandbox.exec.bash("python3 --version")
            print(result.display_text)

            uploaded = await sandbox.files.upload_bytes(
                "artifacts/hello.bin",
                b"hello from daimon-sdk",
            )
            print(uploaded.file_path, uploaded.bytes_written)


asyncio.run(main())

DaimonClient

Low-level client that connects directly to a sandbox MCP endpoint. Use this when you already have a sandbox URL and token. For lifecycle management, prefer DaimonManagerClient.

DaimonClient(base_url, *, access_token=None, timeout_s=30.0)

Construct a client targeting a sandbox MCP endpoint.

Parameters

ParameterTypeDefaultDescription
base_urlstrSandbox MCP endpoint URL (e.g. http://127.0.0.1:18080/mcp).
access_tokenstr | NoneNoneOptional access token sent as X-Access-Token header.
timeout_sfloat30.0HTTP request timeout in seconds.
Connection lifecycle

Use async context manager (async with DaimonClient(...) as client:) or call await client.connect() / await client.close() manually.

RawAPI

Escape hatch for calling arbitrary MCP tools when the typed surface doesn't cover your use case.

async raw.call_tool(name, arguments=None, *, raise_on_error=True) -> dict[str, Any]

Call any MCP tool by name with raw arguments.

Parameters

ParameterTypeDefaultDescription
namestrMCP tool name (e.g. 'GetRuntimeContext').
argumentsdict | NoneNoneTool arguments as a dictionary.
raise_on_errorboolTrueRaise DaimonToolError on tool failure if True.

Returns: dict[str, Any]

payload = await client.raw.call_tool(
    "GetRuntimeContext",
    {},
)
print(payload["baseWorkdir"])

RuntimeAPI

Access sandbox runtime context including base workdir, filesystem policy, network policy, and capabilities.

async runtime.get_context() -> RuntimeContextResult

Fetch the sandbox runtime context.

Returns: RuntimeContextResult

runtime = await client.runtime.get_context()
print(runtime.base_workdir)
print(runtime.capabilities)

DaimonManagerClient

High-level client that talks to the processd-sandbox-manager HTTP API. Manages sandbox lifecycle — create, find-or-create, start, stop, delete.

DaimonManagerClient(base_url, *, access_token=None, timeout_s=30.0)

Construct a manager client targeting the sandbox manager HTTP endpoint.

Parameters

ParameterTypeDefaultDescription
base_urlstrManager HTTP endpoint (e.g. http://127.0.0.1:18080).
access_tokenstr | NoneNoneAccess token for X-Access-Token header.
timeout_sfloat30.0HTTP request timeout.
async manager.health() -> bool

Check if the manager is reachable and healthy.

Returns: bool

async manager.capacity() -> ManagerCapacityResult

Get current capacity info — memory, PID, CPU limits and usage.

Returns: ManagerCapacityResult

capacity = await manager.capacity()
print(capacity.mode, capacity.memory_bytes.available)
async manager.create_sandbox() -> DaimonSandbox

Create a fresh sandbox workspace.

Returns: DaimonSandbox

async manager.find_or_create_sandbox(*, labels, ttl_seconds=None) -> DaimonSandbox

Find a sandbox by labels or create one if none matches. Useful for thread-scoped sandbox reuse.

Parameters

ParameterTypeDefaultDescription
labelsdict[str, str]Labels to match against existing sandboxes.
ttl_secondsint | NoneNoneTime-to-live in seconds for the sandbox.

Returns: DaimonSandbox

sandbox = await manager.find_or_create_sandbox(
    labels={"thread_id": "chat-123"},
    ttl_seconds=3600,
)
async manager.get_sandbox(sandbox_id) -> SandboxInfo

Get sandbox info by ID.

Parameters

ParameterTypeDefaultDescription
sandbox_idstrSandbox unique identifier.

Returns: SandboxInfo

async manager.start_sandbox(sandbox_id) -> SandboxInfo

Start (resume) a stopped sandbox.

Parameters

ParameterTypeDefaultDescription
sandbox_idstrSandbox unique identifier.

Returns: SandboxInfo

async manager.stop_sandbox(sandbox_id) -> SandboxInfo

Stop (pause) a running sandbox.

Parameters

ParameterTypeDefaultDescription
sandbox_idstrSandbox unique identifier.

Returns: SandboxInfo

async manager.delete_sandbox(sandbox_id) -> None

Delete a sandbox and its workspace permanently.

Parameters

ParameterTypeDefaultDescription
sandbox_idstrSandbox unique identifier.

Returns: None

manager.sandbox(*, delete_on_exit=True) -> SandboxContext

Return an async context manager that creates, connects, and optionally deletes a sandbox.

Parameters

ParameterTypeDefaultDescription
delete_on_exitboolTrueDelete the sandbox when exiting the context.

Returns: SandboxContext

async with manager.sandbox() as sandbox:
    result = await sandbox.exec.bash("pwd")
    print(result.display_text)

DaimonSandbox

Wraps a sandbox info and its connected MCP client. Exposes the same namespaces as DaimonClient.

sandbox.info -> SandboxInfo
sandbox.id -> str
sandbox.runtime -> RuntimeAPI
sandbox.files -> FilesAPI
sandbox.exec -> ExecAPI
sandbox.web -> WebAPI
sandbox.raw -> RawAPI
sandbox.refresh() -> SandboxInfo
sandbox.start() -> SandboxInfo
sandbox.stop() -> SandboxInfo
sandbox.delete() -> None

SandboxContext

Async context manager returned by manager.sandbox(). Creates a sandbox on enter, connects the client, and optionally deletes on exit.

FilesAPI

Read, write, edit, glob, and grep files inside the sandbox. All methods return typed result objects.

async files.read(file_path, *, offset=None, limit=None, pages=None) -> ReadResult

Read a file from the sandbox filesystem. Supports text, image, and multi-page content.

Parameters

ParameterTypeDefaultDescription
file_pathstrPath to the file.
offsetint | NoneNoneStarting line for text files.
limitint | NoneNoneMax lines to read.
pagesstr | NoneNonePage range for PDFs (e.g. '1-5').

Returns: ReadResult

read = await client.files.read("/tmp/demo.txt")
print(read.file.content)
print(read.file.num_lines)
async files.write(file_path, content) -> WriteResult

Write content to a file. Creates the file if it doesn't exist.

Parameters

ParameterTypeDefaultDescription
file_pathstrPath to write to.
contentstrText content to write.

Returns: WriteResult

written = await client.files.write("/tmp/demo.txt", "hello\n")
print(written.file_path)
async files.edit(file_path, *, old_string, new_string, replace_all=False) -> EditResult

Edit a file by replacing text. Returns a structured patch.

Parameters

ParameterTypeDefaultDescription
file_pathstrPath to the file.
old_stringstrText to replace.
new_stringstrReplacement text.
replace_allboolFalseReplace all occurrences.

Returns: EditResult

async files.glob(pattern, *, path=None) -> GlobResult

Search for files matching a glob pattern.

Parameters

ParameterTypeDefaultDescription
patternstrGlob pattern (e.g. '**/*.py').
pathstr | NoneNoneRoot directory for the search.

Returns: GlobResult

glob = await client.files.glob("**/*.rs", path=runtime.base_workdir)
print(glob.search_path, glob.num_files)
async files.grep(pattern, *, path=None, glob=None, output_mode=None, ...) -> GrepResult

Search file contents with regex. Supports the full Grep tool parameter set.

Parameters

ParameterTypeDefaultDescription
patternstrRegex pattern to search for.
pathstr | NoneNoneRoot directory to search.
globstr | NoneNoneFile filter glob (e.g. '*.py').
output_modestr | NoneNone'content', 'files_with_matches', or 'count'.

Returns: GrepResult

grep = await client.files.grep("TODO", path=runtime.base_workdir)
print(grep.num_matches)

File Transfer

Upload and download raw bytes through the SDK-only HTTP endpoint (/sdk/file). Meant for SDK and GUI integrations.

async files.upload_bytes(file_path, data) -> FileTransferResult

Upload raw bytes to a file in the sandbox.

Parameters

ParameterTypeDefaultDescription
file_pathstrRemote path in the sandbox.
databytesRaw byte content to upload.

Returns: FileTransferResult

blob = await client.files.upload_bytes(
    "artifacts/report.pdf",
    pdf_bytes,
)
print(blob.file_path, blob.bytes_written, blob.created)
async files.download_bytes(file_path) -> bytes

Download raw bytes from a file in the sandbox.

Parameters

ParameterTypeDefaultDescription
file_pathstrRemote path in the sandbox.

Returns: bytes

data = await client.files.download_bytes("artifacts/report.pdf")
print(len(data))
async files.upload_file(local_path, remote_path) -> FileTransferResult

Upload a local file to the sandbox.

Parameters

ParameterTypeDefaultDescription
local_pathstr | PathLocal filesystem path.
remote_pathstrRemote path in the sandbox.

Returns: FileTransferResult

async files.download_file(remote_path, local_path) -> Path

Download a remote file to the local filesystem.

Parameters

ParameterTypeDefaultDescription
remote_pathstrRemote path in the sandbox.
local_pathstr | PathLocal destination path.

Returns: Path

ExecAPI

Run commands, background tasks, and interactive sessions inside the sandbox.

async exec.bash(command, *, timeout_ms=None, description=None, run_in_background=False, dangerously_disable_sandbox=False) -> BashResult

Execute a bash command in the sandbox.

Parameters

ParameterTypeDefaultDescription
commandstrBash command to execute.
timeout_msint | NoneNoneCommand timeout in milliseconds.
descriptionstr | NoneNoneHuman-readable description of the command.
run_in_backgroundboolFalseRun the command as a background task.
dangerously_disable_sandboxboolFalseDisable sandbox for this command.

Returns: BashResult

result = await client.exec.bash("printf 'hello\n'")
print(result.stdout)

background = await client.exec.bash(
    "sleep 1; echo done",
    run_in_background=True,
)
print(background.background_task_id)

Sessions

Start a persistent TTY session, then write input, poll output, and wait for the process to exit.

async exec.start_session(cmd, *, workdir=None, tty=False, ...) -> SessionHandle

Start an interactive session in the sandbox.

Parameters

ParameterTypeDefaultDescription
cmdstrCommand to start (e.g. '/bin/cat').
workdirstr | NoneNoneWorking directory for the session.
ttyboolFalseAllocate a TTY for the session.

Returns: SessionHandle

session = await client.exec.start_session("/bin/cat", tty=True)

echoed = await session.write("hello session\n")
print(echoed.output)

final = await session.wait_for_exit()
print(final.exit_code)
async session.write(chars='', *, yield_time_ms=None, max_output_tokens=None) -> ExecResult

Write characters to the session's stdin.

Parameters

ParameterTypeDefaultDescription
charsstr''Characters to write to stdin.
yield_time_msint | NoneNoneTime to wait for output.

Returns: ExecResult

async session.poll(*, yield_time_ms=None, max_output_tokens=None) -> ExecResult

Poll the session for new output without writing any input.

Returns: ExecResult

async session.wait_for_exit(*, timeout_s=10.0, yield_time_ms=5000, ...) -> ExecResult

Poll until the session process exits or timeout is reached.

Parameters

ParameterTypeDefaultDescription
timeout_sfloat10.0Max wait time in seconds.
yield_time_msint5000Time to yield for output per poll.

Returns: ExecResult

async session.close(*, exit_payload='__EXIT__\\n', yield_time_ms=500, ...) -> ExecResult

Send exit signal to the session.

Returns: ExecResult

WebAPI

Fetch web pages through the sandbox. Returns a typed result with content, status code, and metadata.

async web.fetch(url, *, timeout_ms=None, max_bytes=None, follow_same_host_redirects=None) -> WebFetchResult

Fetch a URL through the sandbox's controlled HTTP client.

Parameters

ParameterTypeDefaultDescription
urlstrURL to fetch.
timeout_msint | NoneNoneRequest timeout in milliseconds.
max_bytesint | NoneNoneMaximum response body size.
follow_same_host_redirectsbool | NoneNoneFollow same-host redirects.

Returns: WebFetchResult

page = await client.web.fetch("https://example.com")
print(page.status_code)
print(page.content[:200])

Authentication

Manager access tokens protect the control plane. Sandbox MCP tokens are returned by the manager and wired into DaimonSandbox automatically.

from daimon_sdk import DaimonManagerClient

async with DaimonManagerClient(
    "http://127.0.0.1:18080",
    access_token="your-token",
) as manager:
    sandbox = await manager.create_sandbox()
    await sandbox.connect()

    runtime = await sandbox.runtime.get_context()
    print(runtime.capabilities)

DaimonManagerClient sends X-Access-Token to manager APIs. Direct DaimonClient also sends X-Access-Token to MCP and /sdk/file when you pass access_token.

Exceptions

ExceptionDescription
DaimonErrorBase exception for all SDK errors.
DaimonHttpErrorHTTP-level errors from manager or file transfer APIs. Includes status_code and payload.
DaimonToolErrorMCP tool execution failures. Raised when a tool returns an error result.
DaimonConnectionErrorConnection failures (network unreachable, DNS, timeout).
DaimonProtocolErrorProtocol-level errors from the MCP transport.

Models

ReadResult, ReadTextFile, ReadImageFile, ReadPartsFile
WriteResult
EditResult
GlobResult
GrepResult
FileTransferResult
BashResult
ExecResult
WebFetchResult
RuntimeContextResult
SandboxInfo
ManagerCapacityResult, CapacityResource
LimitsStatus
SessionHandle
ContentBlock
Typed results

All tool results expose content_blocks (list of ContentBlock) and display_text (str) for agent/UI output. Use typed fields for logic, raw_payload for debugging.