Python SDK for Phala Cloud API, aligned with @phala/cloud action surface.
- Pythonic API (
snake_case, sync-first REPL experience) - Sync + Async parity
- Pydantic request/response validation
- Safe calls (
safe_*) without exceptions
- Python >= 3.10
- uv (for development)
pip install phala-cloudfrom phala_cloud import create_client
client = create_client(api_key="<api-key>")
me = client.get_current_user()
print(me.model_dump())import asyncio
from phala_cloud import create_async_client
async def main() -> None:
client = create_async_client(api_key="<api-key>")
me = await client.get_current_user()
print(me.model_dump())
await client.aclose()
asyncio.run(main())PHALA_CLOUD_API_KEYPHALA_OIDC_TOKENPHALA_CLOUD_WORKSPACEPHALA_CLOUD_API_PREFIX(e.g.https://cloud-api.phala.com/api/v1)
api_keybearer_tokenworkspacebase_urlversion(2025-10-28/2026-01-21/2026-05-22/2026-06-23/2026-11-11, default2026-11-11)timeoutheadersuse_cookie_auth
The SDK sets User-Agent to phala-cloud-sdk-python/<version> python/<x.y.z> httpx/<ver>. A User-Agent in headers replaces that default.
A non-empty client option takes precedence over its corresponding environment variable. After resolving both credentials, the SDK uses the API key if both are set. This includes an API key from PHALA_CLOUD_API_KEY when bearer_token is set explicitly. Cookie auth suppresses both generated authentication headers; it does not remove headers supplied by the caller. The SDK removes surrounding whitespace from the workspace value.
When you supply http_client, its base URL and timeout control requests; configure them on that client. Watch streams use their own read timeout, as described below. The SDK does not close the supplied client.
No. Redirects can send your API key to another server, so the SDK stops at the first response. This applies to API calls and SSE streams. Set base_url to the final API URL instead of relying on a redirect.
Direct calls raise ApiError for 3xx responses. request_full returns the original status, headers, and body with ok=False, so you can inspect the response without following it.
The SDK ignores follow_redirects=True on SDK requests, including requests made with a supplied http_client. It does not change that client's settings or close the supplied client. Custom transports must not follow redirects internally.
- Direct methods: raise on HTTP/validation errors
- Safe methods (
safe_*): returnSafeResult - Function-style wrappers available in
phala_cloud.actions
Example:
result = client.safe_get_kms_list()
if result.ok:
print(result.data.items)
else:
print(result.error)Pass the CVM ID and the one-time token to commit_cvm_update. compose_hash and transaction_hash are optional. If you omit the compose hash, the server reads it from the token. If you omit the transaction hash, the server checks whether the compose hash is already allowed on-chain. The server still validates the token and the update.
Send compose to patch_cvm as a JSON Merge Patch: a set field sets the value, a field set to None removes the key, and a field you leave out is unchanged. requirements merges per key and cannot change gpu_policy. init_scripts replaces the CVM's platform init scripts with the given slugs (docker_cleanup, env_merge); get_cvm_init_scripts shows which ones the CVM runs and which it can select. Turning on docker_cleanup or requirements.ephemeral_data_disk erases data on every boot, so it also needs confirm_data_wipe=True.
client.patch_cvm({
"id": "my-cvm",
"compose": {"secure_time": True, "swap_size": None, "requirements": {"health_check": True}},
})On create, send launch_token (at least 32 bytes) or generate_launch_token=True; the commit response carries a newly issued token once. To rotate, call set_app_launch_token (omit launch_token to have Phala Cloud generate one), then set each CVM's compose.requirements.launch_token_hash to the returned hash before it restarts. clear_app_launch_token removes the token after every CVM has dropped the hash. get_cvm_user_config returns the token to members who can read the CVM. Phala Cloud and the node operator can read the token, so it protects only against parties who know the public compose.
get_compose_hash(compose, os_image_version="0.6.0") hashes the compose the way Phala Cloud does for that OS image: below 0.6.0 manifest_version is 2 and init_script is a single string, from 0.6.0 they are "3" and a list. Without os_image_version both fields are hashed as given.
watch_cvm_state reconnects after a server timeout event, an incomplete stream, or a transient network failure. Set max_retries to limit reconnects; the default is unlimited. Set retry_delay in seconds (default 5). The aliases maxRetries and retryDelay also work. The stream read timeout is the requested timeout plus interval plus 5 seconds.
Only a complete event finishes the watch. The server may send an error event and then continue streaming. If retries are exhausted, the SDK raises RequestError with the last available server and network error details. HTTP errors use the same SDK exception types as other requests and are not retried. In async code, cancelling the task closes the stream and propagates cancellation. Previous versions raised TimeoutError on retry exhaustion; catch RequestError instead.
The Python SDK no longer exposes add_compose_hash, deploy_app_auth, or their safe wrappers. These wrappers did not implement the blockchain operations. Use the JavaScript SDK for these operations instead.
Pass the output of parse_env_vars or dstack_sdk.EnvVar objects to encrypt_env_vars(envs, public_key_hex). It returns the encrypted string synchronously. In async code, use await async_encrypt_env_vars(envs, public_key_hex) instead.
This changes the previous encrypt_env_vars behavior: code that awaited it must switch to async_encrypt_env_vars.
- Replace
await encrypt_env_vars(...)withawait async_encrypt_env_vars(...). Theencrypt_env_varsfunction now returns a string synchronously. - Use the JavaScript SDK for
add_compose_hashanddeploy_app_auth. Their Python wrappers have been removed. - Catch
RequestErrorinstead ofTimeoutErrorwhen watch retries are exhausted. Watch HTTP errors now use SDK exceptions instead ofhttpx.HTTPStatusError. - Do not rely on a raw state event followed by a closed stream as a successful watch result. Only a complete event finishes the watch. Timeouts, incomplete streams, and transient network failures use the reconnect budget.
patch_cvmvalidatesgpusas{"gpus": [{"slot": ..., "product_id": ...}], "attach_mode": ...}, the shape the API accepts. Other shapes now fail validation instead of being sent and ignored.patch_cvmalso acceptsinstance_type, andprepare_only=TruesendsX-Prepare-Only: true; the 465 result then includescommit_token,commit_url,api_commit_urlandonchain_status.update_cvm_visibilitynow callsPATCH /cvms/{id}instead of the deprecatedPATCH /cvms/{id}/visibility. It returnsmessage,correlation_idandstatusinstead of the CVM record; callget_cvm_infoif you need the record.update_cvm_resourcesno longer sendsallow_restart, which the API ignores. The field is deprecated.get_compose_hashnow drops nullstorage_discard,event_log_version,snapshotterandverity_volumesand always emitstproxy_enabled(defaulting togateway_enabled), as the API does. A compose withouttproxy_enabledhashes differently than before.
cd python
# Install dependencies
uv sync
# Run tests
make test
# Lint & format
make checkE2E tests cover sync + async, direct + safe styles, and full interface paths.
Required:
PHALA_CLOUD_E2E_API_KEY— your Phala Cloud API key
Optional:
PHALA_CLOUD_E2E_BASE_URL— defaults tohttps://cloud-api.phala.com/api/v1
cd python
PHALA_CLOUD_E2E_API_KEY="<api-key>" make e2eOr put them in python/.env.test:
PHALA_CLOUD_E2E_API_KEY=<api-key>
# PHALA_CLOUD_E2E_BASE_URL=https://cloud-api.phala.com/api/v1Then simply:
cd python
make e2eApache-2.0