Skip to main content

hogland (Python)

Python client for hogland's Firecracker-backed sandboxes (hogboxes).

The SDK exposes the user-facing slice of the hogplane API (/v1/hogboxes/*, /v1/limits, /v1/me, plus snapshots and devboxes) as a small, typed surface designed to be a viable backend for PostHog's internal SandboxBase interface — see examples/sandbox_base_adapter.py.

Install

uv pip install posthog-hogland
# or
pip install posthog-hogland

The PyPI distribution is posthog-hogland; the importable package is hogland (same split as pip install pillow / from PIL import ...).

Quickstart

from hogland import Hogland

client = Hogland()  # reads HOG_TOKEN + HOG_HOST from env (same vars as the CLI)

# No ssh_public_key given → the box defaults to access_type="none":
# everything below goes through the exec/files/proxy API. Pass
# ssh_public_key=... (+ access_type="ssh-public") if you want a shell too.
with client.create(cpus=4, memory_mib=8192, disk_gib=50) as box:
    box.write_file("/work/run.py", b"print('hi')\n", mkdir=True)

    result = box.exec(["python", "/work/run.py"], timeout_seconds=30)
    assert result.exit_code == 0
    print(result.stdout)

    # Streaming exec — yields ExecEvent frames as they arrive.
    for event in box.exec_stream(["bash", "-c", "for i in 1 2 3; do echo $i; sleep 1; done"]):
        if event.kind == "stdout":
            print(event.data, end="")
        elif event.kind == "exit":
            print(f"exit: {event.exit_code}")

    # Snapshot for next session. Returns a record whose .id can be
    # passed back as snapshot_id on the next create.
    snap = box.snapshot()

Async is symmetric:

import asyncio
from hogland import AsyncHogland

async def main() -> None:
    async with AsyncHogland() as client:
        async with await client.create(cpus=4, memory_mib=8192) as box:
            result = await box.exec(["uname", "-a"])
            print(result.stdout)

asyncio.run(main())

API at a glance

Method Returns Notes
Hogland.create(...) / .get(id) / .list() / .iter_boxes() Hogbox / HogboxList Create, fetch, paginate user-owned boxes
Hogland.me() / .limits() Me / Limits Caller identity, server-advertised valid ranges
Hogbox.exec(argv, timeout_seconds, env, workdir) ExecResult Batch exec, ≤1 MiB stdout/stderr per stream
Hogbox.exec_stream(argv, ...) Iterator[ExecEvent] SSE streaming exec, no output cap
Hogbox.write_file(path, bytes, mode, mkdir) / .read_file(path) FileWriteResponse / bytes Atomic file IO; ≤64 MiB
Hogbox.snapshot() SnapshotRecord Pause → sync → resume; returns id usable as next snapshot_id
Hogbox.pause() / .resume() / .delete() Hogbox / None Lifecycle
Hogbox.proxy_url(port, path) str Authenticated proxy URL into the box at port

Auth: pass an API credential via HOG_TOKEN env or token=... ctor arg. The SDK transports it as Authorization: Bearer <credential> and is agnostic to credential type — hogplane resolves it through one of several paths (see Auth below). The proxy_url is hit with the same credential; there's no per-tunnel token system (see docs/PYTHON_SDK_CODEGEN_RESEARCH.md §1d).

Errors are typed: AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, ValidationError, RateLimitError, ServerError. All derive from APIError (which derives from HoglandError). The original problem-details body is on .body; the HTTP status is on .status_code.

Configuration

export HOG_TOKEN=...                          # API credential (see Auth)
export HOG_HOST=https://hogland.posthog.dev   # API base URL

Config is resolved from constructor args (token=, base_url=) first, then from these env vars. There is no on-disk config-file fallback today; matching the CLI's ~/.config/hogland/config.json (see pkg/cliconfig/cliconfig.go) is on the roadmap.

Auth

Hogplane authenticates each request to one of these paths — see docs/AUTH_PLAN.md for the full model. The SDK transports any of them identically; the credential string you put in HOG_TOKEN / token= is what selects the path:

Path Credential Who uses it
APIToken Long-lived token minted at signup (tok_...) Humans, CLI users, scripts
K8s SA (EKS OIDC) Projected K8s ServiceAccount JWT with hogland audience In-cluster automation (e.g. PostHog Django → hogland)
GitHub OIDC GitHub Actions workflow JWT CI workflows in PostHog org
Tailscale Identity injected by Tailscale Operator ingress Tailnet-resident humans

For in-cluster automation (the primary SDK consumer), the recommended shape is:

  1. Pod spec mounts a projected SA token volume with hogland's audience (default: hogland) at e.g. /var/run/secrets/hogland.posthog.dev/token. Don't reuse /var/run/secrets/kubernetes.io/serviceaccount/token — its audience is the cluster API server, which won't match hogland.
  2. The integration constructs the client with Hogland.from_token_file("/var/run/secrets/hogland.posthog.dev/token") (or token_provider=): the SDK re-reads the file on every request, so the ~50 min K8s rotation never presents a stale credential.
  3. A k8s_sa TrustMapping on hogland side, keyed on {issuer, namespace, sa_name}, resolves the JWT to a Principal.

See examples/sandbox_base_adapter.py for a reference implementation and INTEGRATION_NOTES.md for the operational checklist.

Multi-deploy / regional setups

The SDK is region-agnostic by construction — one Hogland instance is one base URL plus one credential. If hogland ever runs as separate regional deploys (e.g. hogland-eu.posthog.dev and hogland-us.posthog.dev), the consumer holds one client per region:

clients = {
    "eu": Hogland(base_url="https://hogland-eu.posthog.dev", token=eu_token),
    "us": Hogland(base_url="https://hogland-us.posthog.dev", token=us_token),
}

box = clients[region].create(cpus=4, memory_mib=8192)

This is the same shape Modal users write today (their _get_modal_region(deployment) helper picks the right Modal cloud); the only difference is that with hogland the regional decision is which base_url, not which kwarg. If the deploys share a Principal (federated TrustMapping matched on the same K8s SA), the token is the same across regions; if not, the consumer rotates per-region credentials at construction time.

The vendored examples/sandbox_base_adapter.py factors this through a small _hogland_client(region=...) factory — extend that one function in your repo when regional routing becomes a real requirement.

How the SDK is built

The Pydantic v2 models in src/hogland/_generated/models.py are codegen'd from internal/clitest/testdata/openapi.yaml by scripts/gen_models.py (which filters to the user-facing slice and runs datamodel-code-generator underneath). CI runs the regen and fails the build if the result differs from the committed file. The hand-written facade (_client.py, _async.py, _box.py, _http.py, _sse.py) sits on top and is the public surface.

The choice of "models-only codegen + hand-written facade" is the modern-AI-SDK shape (OpenAI, Anthropic). See docs/PYTHON_SDK_CODEGEN_RESEARCH.md for the analysis and tradeoffs.

Development

# From the repo root.
cd sdk/python
uv venv --python 3.12
uv pip install -e '.[dev]'

# Regenerate models from openapi.yaml.
task py:gen-models

# Run tests, lint, type-check.
uv run pytest -q
uv run ruff check .
uv run ruff format --check .
uv run ty check src tests

Release

See RELEASING.md for the operational flow (bump, tag, push) and the one-time PyPI Trusted Publishing setup.

Short version: bump __version__ in src/hogland/_version.py — the single source of truth — then tag vX.Y.Z-py on main. CI verifies the tag matches _version.py, builds, smoke-tests the wheel, attests, publishes via OIDC, and creates a GitHub Release. The -py suffix keeps this release track separate from the CLI's v*-cli tags.

Changes per version are tracked in CHANGELOG.md.

License

MIT — see LICENSE.

Metadata

Release files for posthog-hogland 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for posthog-hogland 0.4.1
File Size Uploaded
posthog_hogland-0.4.1.tar.gz 51.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for posthog-hogland 0.4.1
File Interpreter ABI Platform
posthog_hogland-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 89.4 kB

Release files / posthog_hogland-0.4.1.tar.gz

Download URL posthog_hogland-0.4.1.tar.gz
Size 51.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9f89e6768ba28eb29ffa8868797f8229d196caa9e3e6e56e85df598981325462
BLAKE2b-256 checksum
How to use checksums
b1cac67965f512f9d4e2afc805bd878650f8cb5a5b91eb530f1ee9004fb5fb4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / posthog_hogland-0.4.1-py3-none-any.whl

Download URL posthog_hogland-0.4.1-py3-none-any.whl
Size 38.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2bc2fd6bf4a710df8eaf4e5bfa1a4ffd8d29751eb13edc650b89bdcba045eaf2
BLAKE2b-256 checksum
How to use checksums
352a527ef6d0c3eee98b676bf5c9878c3010e749601210eb1b32dd99548b51b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page