# Python SDK Use the synchronous `security_run.Client` in Python 3.10+. Install from the repository and supply `SECURITY_RUN_API_KEY` as described in [Authentication & installation](/docs/authentication/). The SDK provisions sessions and returns connection details. It does not execute remote commands or transfer files itself; use SSH/SFTP, an RDP client, or the browser for work inside the VM. ## Quickstart with automatic cleanup Save as `quickstart.py`: ```python from security_run import Client with Client() as client: with client.launch("ubuntu-24.04", ttl="30m") as session: print("Session:", session.id) print("IP:", session.ip) print("Browser:", session.browser_url) input("Open the browser URL. Press Enter here when finished: ") ``` Run it with `python quickstart.py`. `launch()` waits for readiness and loads native credentials. The inner context requests destruction when it exits, including when the work raises an exception. The outer context closes the client's HTTP connections. Use `windows-server-2025` for a Windows desktop. The browser URL requires sign-in as the owner. Do not exit the session context until your work is finished. ## Credits and a quote ```python from security_run import Client with Client() as client: balance = client.credits() quote = client.quote( "ubuntu-24.04", ttl="30m", transfer_budget_credits=10, ) print("Available:", balance["available_credits"], "credits") print("Usage upper limit:", quote["max_credits"], "credits") ``` A quote does not create a VM or spend credits. This example returns an upper limit of 15 credits: 5 for compute and 10 for outbound data. Actual usage is deducted while the VM runs; the full quoted amount is not required at launch. The session stops earlier if credits run out. ## Launch options ```python session = client.launch( "ubuntu-24.04", ttl="30m", transfer_budget_credits=50, wait=True, timeout=900, idempotency_key="case-482-linux", ) ``` | Parameter | Default | Meaning | | --- | --- | --- | | `image` | Required | An exact ID from `client.images()`. | | `ttl` | `"30m"` | Duration string, or integer seconds. Range 300–14,400 seconds, subject to trial limits. | | `transfer_budget_credits` | Server default: 50 | Integer outbound-credit budget from 10 to 500. | | `wait` | `True` | Wait for `ready` and fetch native credentials. | | `timeout` | `900` | Readiness wait timeout, in seconds. | | `idempotency_key` | Generated UUID | Reuse with identical options when retrying the same logical launch. | Python timeouts are in **seconds**. They control the readiness/cleanup wait; individual API requests also have their own timeout and retries. A wait timeout does not cancel or destroy the server-side session. The older `transfer_budget_cents` argument remains a compatibility alias. New code should use `transfer_budget_credits` and should not pass both. ## Client and session reference | Call or property | Result | | --- | --- | | `Client(api_key=None, base_url=None)` | A client using explicit values or the environment. | | `client.images()` | List of image dictionaries, including resource details and pricing. | | `client.credits()` | Credit account dictionary. | | `client.quote(image, ttl="30m", transfer_budget_credits=50)` | Usage upper limit and rate dictionary. | | `client.launch(image, ...)` | A `Session`. | | `client.list()` | List of `Session` objects; follows pagination automatically. | | `client.get(session_id)` | A session with its current state. | | `client.close()` | Close local HTTP resources; does not destroy VMs. | | `session.id`, `.ip`, `.state`, `.browser_url` | Current cached session fields. IP may be `None` before readiness. | | `session.refresh()` | Fetch current state, update the object, return the same session. | | `session.credentials` | A copy of the native credentials; fetches them if not already loaded. Requires readiness. | | `session.to_dict()` | A deep copy of the cached response. May contain credentials. | | `session.destroy(wait=True, timeout=180)` | Request teardown; by default wait up to 180 seconds for a terminal state. | There is no `session.run()` or `session.exec()` method. Use the native connection details with your chosen SSH/RDP tooling. ## Inspect existing sessions ```python from security_run import Client with Client() as client: for session in client.list(): print(session.id, session.state, session.browser_url) session = client.get("ses_YOUR_SESSION_ID") if session.state == "ready": username = session.credentials["username"] print("Ready:", session.ip, username) session.destroy() ``` Retrieving credentials is an owner-authorized operation. Linux supplies `username`, `private_key`, and `host_public_key`; Windows supplies `username`, `password`, and `certificate_fingerprint`. Use the returned host key/fingerprint to verify a native connection, and never log the full credential dictionary. ## Return immediately, then reconnect Use `wait=False` when another process will track startup. Save the ID durably before doing other work: ```python from pathlib import Path from security_run import Client with Client() as client: session = client.launch("ubuntu-24.04", wait=False, ttl="30m") Path("session-id.txt").write_text(session.id) print(session.browser_url) ``` Later, call `client.get(saved_id)` and `session.refresh()` to track readiness. Access `session.credentials` only once `session.state == "ready"`. A `refresh()` response does not include native secrets; the next credentials access will fetch them again. This pattern intentionally leaves the session running. Arrange a cleanup path in the process that owns the work; otherwise it runs until its absolute expiry. ## Handle timeouts and uncertain requests ```python from security_run import Client, SessionError with Client() as client: try: with client.launch( "ubuntu-24.04", ttl="30m", timeout=120, idempotency_key="case-482-linux", ) as session: print(session.browser_url) except SessionError as error: print(str(error)) if error.session_id: # This workflow chooses to stop if startup exceeds its deadline. client.get(error.session_id).destroy() ``` If launch fails before entering the session context, the context cannot clean it up. Readiness errors carry `session_id`; use it to inspect or destroy the session. If a transport error happened before the client received an ID, retry using the original idempotency key and unchanged options to resolve the existing request. Inside a session context, cleanup is attempted even when your work fails. If both work and cleanup fail, the original work error is preserved; on Python 3.11+ a note records the cleanup failure. Inspect the session by ID if teardown could not be confirmed. For async Python applications, this client is synchronous: run blocking operations in a worker thread, or call the [HTTP API](/docs/api/) with your own async client. ## Machine sizes Basic Linux terminals default to Small: 2 vCPUs and 2 GiB RAM at 10 credits/hour. Select Standard for 4 GiB at 40 credits/hour: `client.launch("ubuntu-24.04", size="standard")`. Security desktops and Windows support Standard only (4 GiB, 15 credits/hour). SDK sessions retain their explicit TTL and are not subject to browser inactivity cleanup by default. ## Preconfigured security desktops Use `parrot`, `sift`, `remnux` or `flare` as the image ID with the same launch and cleanup methods. Standard size is selected automatically. For example: `session = client.launch("remnux", ttl="30m")` The Linux desktops return both `credentials.password` for RDP and `credentials.private_key` for SSH. The connection response includes `port: 3389` and `ssh_port: 22`. Use `credentials.username` instead of assuming a login name. FLARE-VM provides RDP on Windows Server 2022.