Skip to main content

Python SDK for the Propie / Previews VM-as-a-Service platform (Firecracker microVMs).

Project description

treinta-previews (Python SDK)

Python SDK for the Propie / Previews VM-as-a-Service platform — launch Firecracker microVMs from a git repo or a local folder, run commands, stream logs, attach drives and databases, take snapshots, promote to permanent (scale-to-zero) previews, and embed a browser widget safely.

It mirrors the Node SDK (cli/client.ts + web/src/lib/api.ts) 1:1 in surface and naming, translated to Pythonic snake_case.

Install

pip install treinta-previews
  • Distribution name: treinta-previews
  • Import name: previews
from previews import PreviewsClient

If the top-level name previews ever collides with another package in your environment, the intended fallback import name is treinta_previews. This release publishes the package as previews; use a virtualenv to avoid collisions.

Optional extras:

pip install "treinta-previews[fastapi]"   # widget proxy FastAPI adapter
pip install "treinta-previews[flask]"     # widget proxy Flask adapter

Authentication

The client resolves credentials from arguments or the environment:

Setting Argument Env vars (in precedence order) Default
API key api_key TREINTA_PREVIEWS_API_KEY, PROPIE_API_KEY, PREVIEWS_API_KEY
Base URL base_url TREINTA_PREVIEWS_API_URL, PROPIE_API_URL https://previews.amapola.treinta.ai/api

Keys look like pvk_<prefix>_<secret> and are sent as Authorization: Bearer pvk_....

Quick start

from previews import PreviewsClient

with PreviewsClient() as client:            # api key from env
    vm = client.vms.create(
        repo_url="https://github.com/owner/repo",
        stack="node20",
        exposed_port=3000,
        environment_variables={"NODE_ENV": "production"},
    )
    vm = client.vms.wait_until_running(vm.id)
    print(vm.url)

    result = client.vms.run(vm.id, "npm test")
    print(result.exit_code, result.stdout)

    for event in client.vms.logs(vm.id, follow=False):
        print(event.message)

    client.vms.destroy(vm.id)

Deploy a local folder (zipped locally, honoring .gitignore, stripping secrets):

det = client.detect.folder("/path/to/app")           # SSE stack detection
vm = client.vms.create_from_folder(
    "/path/to/app",
    stack=det.stack,
    start_command=det.start_command,
    exposed_port=det.exposed_port,
)

Async

from previews import AsyncPreviewsClient

async with AsyncPreviewsClient() as client:
    vms = await client.vms.list()
    async for event in client.vms.logs(vms[0].id, follow=False):
        print(event.message)

API surface

PreviewsClient(api_key=None, *, base_url=None, timeout=30.0, http_client=None) (and the identical AsyncPreviewsClient with await/async generators/aclose()). Both are context managers and expose request(method, path, *, json=None, ...) plus the resources below.

client.vms

Method REST
create(**fields) POST /vms (json)
create_from_folder(path, **meta) / create_from_zip(zip_bytes, **meta) POST /vms (multipart)
list() GET /vms
get(id) GET /vms/:id
destroy(id) DELETE /vms/:id
restart(id) POST /vms/:id/restart
redeploy(id) POST /vms/:id/redeploy
run(id, command, *, timeout=300.0) POST /vms/:id/run (buffered)
logs(id, *, follow=True) GET /vms/:id/logs (SSE) → Iterator[LogEvent]
get_env(id) / set_env(id, env) GET/PUT /vms/:id/env
bandwidth(id) GET /vms/:id/bandwidth
persist(id, slug) / unpersist(id) / rename_slug(id, slug) /vms/:id/persist, /vms/:id/slug
slug_available(slug)(bool, reason?) GET /vms/slug-available
upload_files(id, files, *, base_dir="/app") POST /vms/:id/files
mint_widget_token(id, *, capabilities=None, ttl_seconds=None) POST /vms/:id/widget-token
wait_until_running(id, *, timeout=300.0, interval=2.0, on_status=None) polls GET /vms/:id

create / create_from_* accept snake_case fields: repo_url, stack, branch, subdirectory, exposed_port, install_command, start_command, environment_variables, drive_id, drive_mount_path, drive_read_only, database_integration_id, vcpus, memory_mib, persistent, slug.

client.snapshots / .drives / .integrations / .detect / .accounts

  • snapshots.list() / create(vm_id, name=None) / clone(snapshot_id, *, name=None, environment_variables=None) / delete(snapshot_id)
  • drives.list() / create(name, size_gib, *, mount_path=None) / delete(id)
  • integrations.list() / create(**fields) / delete(id)
  • detect.public(repo_url, *, on_progress=None) / zip(zip_bytes, ...) / folder(path, ...)
  • accounts.current()CurrentPrincipal(account, project, auth_type, api_key)

System status

Not a dedicated resource; use the generic request helper:

from previews import SystemStatus
status = SystemStatus.from_dict(client.request("GET", "/system/status"))
print(status.caches["npm"].size_bytes)

Errors

Non-2xx responses raise PreviewsApiError(status, code, message) from the { error: { code, message } } envelope (a non-JSON body yields code UNKNOWN). wait_until_running raises PreviewsTimeoutError on deadline. Missing credentials raise PreviewsConfigError.

Models

Responses are frozen dataclasses with a tolerant from_dict (camelCase → snake_case, unknown keys ignored): Preview (alias VM), RunCommandResult, VMSnapshot, DetectionResult/EnvVarHint, BandwidthSample/BandwidthResponse, Account/Project/ApiKeyRef/CurrentPrincipal, DatabaseIntegration, Drive, SystemStatus/CacheStat, WidgetToken, UploadResult, LogEvent. The package ships py.typed.

Widget backend proxy

The React widget UI is built separately. This SDK provides the backend piece.

Scoped token (recommended): mint a short-lived pwt_ token server-side and hand it to the browser, which then calls the platform directly.

token = client.vms.mint_widget_token(vm_id, capabilities=["preview:read", "vm:run"])

Backend proxy: the browser calls your server; the proxy forwards only a whitelisted subset of operations to the platform using the pvk_ key it holds — the key never reaches the browser.

from previews import PreviewsClient
from previews.proxy import PreviewsProxy, ProxyConfig, make_router  # or make_blueprint

client = PreviewsClient()
proxy = PreviewsProxy(ProxyConfig(
    client=client,
    allowed_origins=["https://app.example.com"],
    allowed_vm_ids={"<vm-uuid>"},          # None = any VM in the project
    # allow_ops defaults to WIDGET_SAFE_OPS = {"status","run","files","logs","mint_token"}
))

# FastAPI
from fastapi import FastAPI
app = FastAPI()
app.include_router(make_router(proxy, prefix="/previews"))

# Flask
# from flask import Flask
# app = Flask(__name__)
# app.register_blueprint(make_blueprint(proxy, url_prefix="/previews"))

The proxy enforces the origin allowlist, the op whitelist (hard-capped to the widget-safe set), and the per-VM restriction; it never echoes the pvk_ key.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

treinta_previews-0.1.0.tar.gz (33.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

treinta_previews-0.1.0-py3-none-any.whl (35.6 kB view details)

Uploaded Python 3

File details

Details for the file treinta_previews-0.1.0.tar.gz.

File metadata

  • Download URL: treinta_previews-0.1.0.tar.gz
  • Upload date:
  • Size: 33.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for treinta_previews-0.1.0.tar.gz
Algorithm Hash digest
SHA256 09a25b06ddb386452a40fdeef93f6f2dbcf4a352b6a7814b46cdfd4a32ca0059
MD5 7c60985a05dfedb22bb36b64eeb552ac
BLAKE2b-256 c2b4620ec8f998086356a35eb32537f14f4b4c3a4804fd9a000b271ba2ca4dbe

See more details on using hashes here.

File details

Details for the file treinta_previews-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for treinta_previews-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35709c0b4a03fcf66f9d07208acc2de72e00b64d85b82cc83f30f6348ac338ce
MD5 b75fff88530d6901c2105497a0242e17
BLAKE2b-256 742b2192e097198f8130c6156346261872f57b3d1cb1d973f2f4f5f2c0b0a8f8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page