Skip to content

Python SDK

Programmatic access to HomeCloud from Python 3.

Package: homecloud-sdk · Import: from homecloud import HomeCloud

Install

pip install homecloud-sdk

If the package is not on PyPI in your environment yet:

pip install "git+https://github.com/HomeCloudLab/homecloud-sdk.git"
# or editable checkout:
pip install -e ../homecloud-sdk

Create a client

from homecloud import HomeCloud

# Explicit (recommended for servers)
client = HomeCloud(
    access_key="HCAK...",
    secret_key="...",
)

# Environment: HC_ACCESS_KEY_ID / HC_SECRET_ACCESS_KEY / HC_APEX
client = HomeCloud.from_env()

# Profile file ~/.homecloud/credentials
client = HomeCloud()
client = HomeCloud(profile="ci")

Async

from homecloud import AsyncHomeCloud

async with AsyncHomeCloud.from_env() as client:
    await client.so.upload("docs", "./a.txt", key="a.txt")
    await client.mq.send("orders", {"id": 1})

from homecloud_sdk import … still works for compatibility; prefer from homecloud import ….

Object Storage

client.so.upload("docs", "./readme.md", key="readme.md")
client.so.upload(
    "media",
    body=video_bytes,
    key="videos/clip.mp4",
    content_type="video/mp4",
)

meta = client.so.head_object("docs", "readme.md")
client.so.download("docs", "readme.md", "./copy.md")

print(client.so.get_object_uri("docs", "readme.md"))
print(client.so.generate_presigned_url("docs", "readme.md", expires_in=3600))

client.so.list_objects("docs", prefix="readme")
# All pages under a prefix (continuation tokens)
keys = client.so.list_all_objects("docs", prefix="photos/", recursive=True)
client.so.copy("dest-bucket", "a.txt", "folder/a.txt", source_bucket="src-bucket")
client.so.move("dest-bucket", "a.txt", "folder/a.txt", source_bucket="src-bucket")
client.so.delete("docs", "readme.md")

# One API: local↔bucket or bucket↔bucket (same shape as CLI `so sync`)
client.so.sync("./dist", "so://my-website/", delete=True)
client.so.sync("so://docs/", "./site")
client.so.sync("so://photos/", "so://backup/photos/", delete=True)

Management helpers (list_buckets, create_bucket) support Access Key (SigV1) using the user’s effective policies (including system groups). homecloud login is mainly for interactive console sessions or special cases (e.g. IR ↔ Docker), not for listing buckets when credentials are present.

Message Queues

client.mq.send("orders", {"id": 1})
client.mq.send("orders", [{"id": 1}, {"id": 2}])  # batch 1–10

messages = client.mq.receive("orders", max_messages=10, wait_seconds=5)
for msg in messages:
    process(msg)
    client.mq.delete("orders", msg["sequence"])

client.mq.receive("orders", max_messages=10, delete=True)  # receive + ack
client.mq.purge("orders")

dlq = client.mq.receive_dlq("orders", max_messages=5)
client.mq.delete_dlq("orders", 7)
client.mq.purge_dlq("orders")

Functions

for fn in client.functions.list():
    print(fn["name"], fn.get("status"))

result = client.functions.invoke("hello", {"name": "Ada"})
print(result)

print(client.functions.url("hello"))
client.functions.enable_url("hello")
client.functions.disable_url("hello")

for row in client.functions.logs("hello")["items"]:
    print(row)
# Breaking (0.5.14+): logs() returns {"items", "next_cursor"} — not a bare list.
detail = client.functions.get_invocation("hello", invocation_id)

for event in client.functions.stream_logs("hello", invocation_id, timeout_seconds=120):
    if event.get("type") == "log":
        print(event.get("line"))
    elif event.get("type") in {"end", "done"}:
        break

Domains (0.5.15+)

hosts = client.domains.list_hosts(domain_id)
client.domains.update_record(domain_id, record_id, record_type="A", record="1.2.3.5", host="www")
client.domains.detach(attachment_id)

Tasks and Diagrams

Management plane — user JWT (login) or user-bound Access Key SigV1 (not a bare service-account key).

# Smart search (same q grammar as the console)
items = client.tasks.search("cert @alice &ops due:overdue")
detail = client.tasks.get("TASK-12")

# Create / assign by username
client.tasks.create(
    "Rotate cert",
    type="change",
    priority="p2",
    assignees=["alice"],
    label_names=["ops"],
)
client.tasks.assign_me("TASK-12", status="in_progress")
client.tasks.comment("TASK-12", "Deployed to staging")
for event in client.tasks.activity("TASK-12"):
    print(event["event_type"], event.get("payload"))
client.tasks.archive("TASK-12", cancel=True)

diagrams = client.diagrams.list()
doc = client.diagrams.get(diagram_id)
client.diagrams.create("Production plan", description="Draft")
client.diagrams.update(diagram_id, name="Prod", document=doc["document"], revision=doc["revision"])

Mail

boxes = client.mail.list_mailboxes()
msgs = client.mail.list_messages(mailbox_id=boxes[0]["id"], limit=20)
detail = client.mail.get_message(msgs[0]["id"])
raw = client.mail.download_attachment(msgs[0]["id"], "0")

Image Registry

print(client.ir.list())
client.ir.create("myapp", keep_last=10)
print(client.ir.usage())

Secrets

print(client.secrets.list())  # console JWT — metadata
client.secrets.create("my-secret", API_KEY="test")  # create + seed (Access Key)
print(client.secrets.get_value("my-secret"))
print(client.secrets.get_value("my-secret", "API_KEY", "DB_HOST"))
print(client.secrets.get_value("my-secret", format="env"))  # API_KEY=x (no trailing \\n)
client.secrets.put_value("my-secret", {"API_KEY": "rotated"})
client.secrets.put_value("my-secret", API_KEY="test", merge=True)
client.secrets.put_value("my-secret", "API_KEY=x", format="env", merge=True)

Flat map codecs (JSON/ENV/YAML) for files/CLI: homecloud_sdk.secret_formats.

Errors

from homecloud import HomeCloud, NotFoundError, HomeCloudError

try:
    HomeCloud.from_env().so.head_object("docs", "missing.txt")
except NotFoundError as exc:
    print(exc.resource_type, exc.resource)
except HomeCloudError as exc:
    print(exc.status_code, exc)
Type Typical status
NotFoundError 404
UnauthorizedError 401
PermissionDeniedError 403
BadRequestError 400
ConflictError 409
RateLimitError 429
ServiceUnavailableError 502–504
ApiError other HTTP errors

Environment variables

Variable Short Effect
HOMECLOUD_PROFILE HC_PROFILE Active profile
HOMECLOUD_ACCESS_KEY_ID HC_ACCESS_KEY_ID Access key
HOMECLOUD_SECRET_ACCESS_KEY HC_SECRET_ACCESS_KEY Secret
HOMECLOUD_ACCOUNT_ID HC_ACCOUNT_ID Optional account
HOMECLOUD_APEX HC_APEX Platform domain

Credentials file: ~/.homecloud/credentials (JSON multi-profile).

Relation to the CLI

homecloud-cli is a Typer/Rich wrapper around this SDK. Prefer the CLI for shell/CI sync commands and the SDK inside application code.