Vautra Python SDK
Python SDK for Vautra projects, buckets, and object storage.
The SDK signs every request and sends it to https://app.vautra.com/api by default. Keep the secret access key on a trusted server; it is a credential, not a public identifier.
Requirements
- Python 3.9 or newer.
requests,cryptography, andpqcrypto(installed automatically).
Installation
pip install vautra-sdk
The distribution is vautra-sdk; the import name is vautra. The npm package of
the same name is the separate Node.js SDK.
Quick start
Set your SDK credentials:
$env:VAUTRA_ACCESS_KEY_ID="your-access-key-id"
$env:VAUTRA_SECRET_ACCESS_KEY="your-secret-access-key"
Then create one client and reuse it:
from vautra import Vautra
with Vautra() as vautra:
projects = vautra.projects.list()
print(projects["data"])
Upload a file:
from vautra import Vautra
with Vautra() as vautra:
obj = vautra.objects.upload(
bucket_id="bucket-id",
key="contract.pdf",
body="C:/files/contract.pdf",
)
print(obj["id"])
Download a file:
from vautra import Vautra
with Vautra() as vautra:
vautra.objects.download("object-id", destination="C:/files/contract-copy.pdf")
Authentication
Create an access key in the Vautra dashboard, then build one client and reuse it — each client owns a connection pool:
from vautra import Vautra
vautra = Vautra(
access_key_id="...",
secret_access_key="...",
)
Credentials fall back to the VAUTRA_ACCESS_KEY_ID and VAUTRA_SECRET_ACCESS_KEY environment variables, and api_url to VAUTRA_API_URL, when the corresponding argument is omitted:
from vautra import Vautra
with Vautra() as vautra: # reads the environment
page = vautra.projects.list()
Every request carries a fresh nonce, including retries, so signed headers are never replayed. Access-key permissions and project or bucket restrictions are enforced by the Vautra backend.
Invalid configuration raises VautraConfigError immediately, before any request is sent. api_url must use https:// unless it points at a loopback address, so signed credentials never travel over plaintext.
Encryption and project scope
Object uploads use VS3 encryption v2. The SDK creates a random AES-256-GCM file key, encrypts each upload part locally, wraps the file key and storage reference with the project's ML-KEM-768 public key, and sends only ciphertext plus envelope metadata to Vautra.
Access keys can be scoped to Vautra-managed projects and, when explicitly granted by the backend, user-managed projects. Vautra-managed uploads need no customer key material. User-managed uploads require the project's ML-KEM public key as project_public_key; the SDK never receives the project private key.
Most integrations use one of these two flows:
| Project type | Upload needs | Download needs |
|---|---|---|
| Vautra-managed project | SDK access key only | SDK access key only |
| User-managed project | SDK access key + derived publicKey |
SDK access key + derived secretKey |
For user-managed projects, the Python SDK does not take the 24-word recovery phrase directly. Derive keys with the Node SDK once, then use those derived keys from Python. See User-managed projects.
Client options
| Option | Default | Description |
|---|---|---|
access_key_id |
$VAUTRA_ACCESS_KEY_ID |
Required. |
secret_access_key |
$VAUTRA_SECRET_ACCESS_KEY |
Required. Never logged; the client redacts it in repr(). |
api_url |
$VAUTRA_API_URL or https://app.vautra.com/api |
Must be https:// for remote hosts. |
timeout |
30.0 |
Seconds, for non-upload requests. Use 0 or None to disable. |
session |
new session | Supply your own requests.Session to control proxies or TLS verification. |
Every method also accepts a per-call timeout= override and a cancel_event= (a threading.Event).
Vautra is a context manager, and close() releases the connection pool:
with Vautra(access_key_id="...", secret_access_key="...") as vautra:
...
Object keys
Vautra object keys are flat file names, not S3-style paths:
- No
/or\separators —documents/report.pdfis rejected. - Maximum 255 bytes (UTF-8), no control characters, no trailing period.
- The key must end in a supported extension.
Supported extensions are currently: txt, csv, json, xml, html, css, js, ts, md, pdf, png, jpg, jpeg, gif, webp, svg, zip, gz, doc, docx, xlsx, pptx, mp3, mp4, webm, wav, ogg. Files whose contents do not match their extension are rejected on upload.
Invalid keys raise VautraError immediately, before an upload session is created.
Projects
page = vautra.projects.list(
page=1,
page_size=20,
search="production",
key_management="managed",
)
project = vautra.projects.get("project-id")
List methods return {"data": [...], "meta": {"page", "pageSize", "total", "totalPages"}}.
Buckets
page = vautra.buckets.list(project_id="project-id", page=1, page_size=20)
bucket = vautra.buckets.get("bucket-id")
created = vautra.buckets.create(
project_id="project-id",
name="documents",
versioning_enabled=True,
)
Bucket deletion is intentionally not exposed by the SDK.
List objects
page = vautra.objects.list("bucket-id", page=1, page_size=20, search="invoice")
Upload objects
New uploads use VS3 encryption v2. The SDK creates a random AES-256-GCM file key, encrypts every part locally, wraps the file key and storage reference with the project's ML-KEM-768 public key, and sends only ciphertext plus envelope metadata to Vautra. The SDK never receives or stores the project private key.
For large local files, pass a path. The SDK reads only the active chunks instead of loading the whole file into memory:
obj = vautra.objects.upload(
bucket_id="bucket-id",
key="movie.mp4",
body="C:/videos/movie.mp4", # str, pathlib.Path, or {"path": ...}
content_type="video/mp4",
)
For content already in memory:
vautra.objects.upload(
bucket_id="bucket-id",
key="hello.txt",
body=b"Hello from Vautra",
content_type="text/plain",
)
When content_type is omitted, Vautra derives the stored MIME type from the key's extension.
Supported body types
| Body | Behaviour |
|---|---|
bytes, bytearray, memoryview |
Used directly. |
str |
Encoded as UTF-8. |
pathlib.Path, os.PathLike, {"path": "..."} |
Opened and read by range; memory stays flat. |
| A seekable binary file object | Read by range from its current position. The SDK does not close a handle it did not open. |
A non-seekable stream (pipe, socket, generator of bytes) |
Spooled to a temporary file so failed parts can be retried, then cleaned up. |
Any object with size and read(start, end) |
Used as a random-access source. |
A stream cannot be rewound, so a failed part could not otherwise be retried. Spooling keeps memory flat regardless of stream size. Prefer a path when the data is already on disk.
Text-mode file objects are rejected — open files with "rb".
Upload options
import threading
cancel = threading.Event()
vautra.objects.upload(
bucket_id="bucket-id",
key="archive.zip",
body="./archive.zip",
concurrency=2, # 1-4
max_attempts=3, # 1-5
attempt_timeout=180.0, # seconds per request
cancel_event=cancel,
on_started=lambda upload_id: print("session", upload_id),
on_progress=lambda p: print(p["stage"], p["loadedBytes"], p["totalBytes"]),
)
Progress stages are starting, uploading, finalizing, and success. loaded_bytes never goes backwards and a retried part is counted once.
Storage part uploads and completion requests retry network failures, HTTP 408, 429, and 5xx. Retry delays are one second then three seconds. Other 4xx responses are terminal, as are errors raised locally by the SDK.
If an upload fails after its session is created, the SDK makes a best-effort request to cancel and clean up that session. Secure cross-process resume is not supported.
User-managed projects
User-managed projects need one extra step because Python does not accept the 24-word BIP39 recovery phrase directly.
Simple rule:
- For upload, Python needs the derived
publicKey. - For download, Python needs the derived
secretKey. - Keep the recovery phrase and
secretKeyprivate.
First, install or use the Node SDK and derive the keys from the recovery phrase:
npm install vautra-sdk
$env:VAUTRA_CUSTOMER_MNEMONIC="your 24 word recovery phrase"
node --input-type=module -e "import { deriveCustomerMlKemKeypair } from 'vautra-sdk'; console.log(JSON.stringify(deriveCustomerMlKemKeypair(process.env.VAUTRA_CUSTOMER_MNEMONIC), null, 2));"
The output looks like this:
{
"publicKey": "base64-ml-kem-public-key",
"secretKey": "base64-ml-kem-secret-key"
}
Use publicKey for Python upload:
import os
from vautra import Vautra
with Vautra() as vautra:
obj = vautra.objects.upload(
bucket_id="bucket-id",
key="contract.pdf",
body="C:/files/contract.pdf",
project_public_key=os.environ["VAUTRA_PROJECT_PUBLIC_KEY"],
)
print(obj["id"])
Use secretKey for Python download:
import os
from vautra import Vautra
with Vautra() as vautra:
vautra.objects.download(
"object-id",
destination="C:/files/contract-copy.pdf",
customer_private_key=os.environ["VAUTRA_CUSTOMER_PRIVATE_KEY"],
)
You can also pass the values directly, but environment variables are safer:
$env:VAUTRA_PROJECT_PUBLIC_KEY="base64-ml-kem-public-key"
$env:VAUTRA_CUSTOMER_PRIVATE_KEY="base64-ml-kem-secret-key"
If a user-managed upload starts without project_public_key, the SDK stops before uploading any file parts.
For high-throughput user-managed downloads, opt into direct storage reads:
vautra.objects.download(
"object-id",
destination="C:/files/contract-copy.pdf",
customer_private_key=os.environ["VAUTRA_CUSTOMER_PRIVATE_KEY"],
direct_storage_download=True,
)
Keep the recovery phrase and derived secretKey in a trusted backend or secret
manager. Do not commit either value, and do not send them to Vautra.
Part encryption metadata
Each content part is encrypted with AES-256-GCM using the upload's random file key and a fresh random 96-bit IV. The IV and authentication tag are recorded per part in the completion payload; the uploaded part body contains ciphertext only. A retried part may be encrypted again with a new IV/tag, and only the metadata from the successful storage write is submitted at completion.
Upload status and cancellation
status = vautra.objects.upload_status("upload-id")
vautra.objects.cancel_upload("upload-id")
upload_status() is for inspection only; it does not enable resume.
To cancel a running upload, set the threading.Event you passed as cancel_event:
import threading
cancel = threading.Event()
worker = threading.Thread(
target=vautra.objects.upload,
kwargs={
"bucket_id": "bucket-id",
"key": "large.zip",
"body": "./large.zip",
"cancel_event": cancel,
},
)
worker.start()
cancel.set()
The upload raises VautraCancelledError, and the session is cleaned up. An already-set event raises before any request is sent.
Because requests is synchronous, cancellation takes effect between parts and between retry attempts — it does not interrupt a request already on the wire. attempt_timeout bounds that window.
Download objects
Return the object as bytes:
data = vautra.objects.download("object-id")
Buffered downloads are capped at 256 MiB so a large object cannot exhaust memory. Pass max_bytes to change the cap, or 0 to disable it. Objects above the cap raise VautraError with status 413.
Write directly to a file, which streams and is not capped:
vautra.objects.download("object-id", destination="./report.pdf")
The file is written to a temporary sibling and renamed into place, so an interrupted download never leaves a truncated file at destination.
Or consume the stream yourself:
with vautra.objects.download_stream("object-id") as stream:
print(stream.content_type, stream.content_length)
for chunk in stream:
sink.write(chunk)
Vautra-managed objects use the backend managed download path. User-managed encryption v2 objects can be downloaded by passing the ML-KEM private key for that project; encrypted parts are fetched through the backend encrypted-range endpoint and decrypted locally:
data = vautra.objects.download(
"object-id",
customer_private_key="base64-ml-kem-secret-key",
)
The SDK does not send the private key to Vautra.
For high-throughput user-managed downloads, opt into direct storage reads. The SDK will use backend-authorized presigned part URLs, send the signed range headers returned by the API, and fall back to the backend encrypted-range path if direct storage fails:
vautra.objects.download(
"object-id",
customer_private_key="base64-ml-kem-secret-key",
direct_storage_download=True,
destination="./report.pdf",
)
Delete objects
vautra.objects.delete("object-id")
Deletion is subject to access-key permissions and backend object-state rules.
Errors
Every SDK error derives from VautraError:
from vautra import VautraError
try:
vautra.buckets.get("missing-bucket")
except VautraError as error:
print(error.status, error.code, error.message)
| Exception | Raised when |
|---|---|
VautraError |
The API returned a failure. Carries status, code, details. |
VautraConfigError |
Client configuration is unusable. Raised before any request. |
VautraTimeoutError |
A request exceeded its time budget (status 408). |
VautraConnectionError |
The API could not be reached (status 0). |
VautraCancelledError |
The caller's cancel_event was set. |
Errors also expose retryable, which the upload retry loop uses to avoid burning attempts on failures that would repeat identically.
The SDK never follows redirects: signed credentials are not forwarded to another host, and an unexpected 3xx surfaces as a VautraError with code unexpected_redirect.
Thread safety
A Vautra client is safe to share across threads. Uploads use a bounded worker pool internally and read bodies under a lock.
Development
python -m venv .venv && .venv/Scripts/activate # or source .venv/bin/activate
pip install -e ".[dev]"
pytest
ruff check . && mypy
The test suite runs against a mock Vautra API whose signature verification is a port of the backend's verifier, so a passing test means the SDK interoperates with the real service rather than with a permissive stub.
To try a real upload:
python examples/upload_file.py ./examples/sample.txt --bucket-id BUCKET --key sample.txt
License
MIT. See LICENSE.
Release files for vautra-sdk 1.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vautra_sdk-1.1.2.tar.gz | 56.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vautra_sdk-1.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 96.7 kB
Release files / vautra_sdk-1.1.2.tar.gz
| Download URL | vautra_sdk-1.1.2.tar.gz |
|---|---|
| Size | 56.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1376291f5e564db985c32c1e204e5ed04bb1120681160c198753e2042a15b55d
|
|
BLAKE2b-256 checksum How to use checksums |
ba4b9665a626e791ea05a671fc1edefc2743c16421d6a8570a6923cdc43e93b7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / vautra_sdk-1.1.2-py3-none-any.whl
| Download URL | vautra_sdk-1.1.2-py3-none-any.whl |
|---|---|
| Size | 40.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa6b9756b069a42212ba2e50febe99c37d3abf04410877d2d78b72d79f4774f0
|
|
BLAKE2b-256 checksum How to use checksums |
671c9393776392144cf7c973e9d0165536ebd2d95b1718a3702e86d4812750a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|