Skip to main content

Async Python SDK for Cage Fleet

Project description

Cage Fleet SDK

cage-fleet-sdk is an asyncio Python client and rolling-concurrency scheduler for the Cage Fleet control plane. It creates marker-only cyber-range Arenas, waits when capacity is exhausted, and submits on demand or at the authoritative deadline. Scheduler state is kept only in memory; Fleet Web is the source of truth after a user process exits.

Python 3.11 or newer is required.

Install

The package is ready to build but has not been published to PyPI yet. Until the first release, install it from a repository checkout:

uv sync --extra dev

After the first release is uploaded, users will install it with:

python -m pip install cage-fleet-sdk

uv may create a virtual environment without a pip module. That is expected; run project commands with uv run. If using a conventional pip-created environment instead, install the checkout with python -m pip install -e '.[dev]'.

Backend contract

The SDK talks to the Cage Fleet control plane implemented by the cage-fleet project, not directly to a Cage Node. Its source of truth is api/openapi.yaml. The public client covers all seven SDK-facing operations: challenge discovery, capacity, owned Arena listing, Arena creation, individual Arena lookup, terminal submission, and explicit close. Submission and close send empty request bodies. Arena creation reuses one UUID Idempotency-Key across capacity retries.

The default HTTP behavior honors standard proxy environment variables. Set trust_env=False on CageFleetClient for a direct connection, such as a localhost test backend.

For development verification:

uv sync --extra dev
uv run pytest
uv run ruff check .
uv run mypy src
uv run python -m build

uv build creates the source distribution and wheel under dist/. Before publishing a new release, update the version and repeat the API and live-backend checks.

Authentication

Pass a Task Token supplied by Cage Fleet. The SDK does not persist it locally.

export CAGE_FLEET_TASK_TOKEN='replace-with-secret'

Run one Arena

create_arena() waits and retries by default when the control plane returns capacity_exhausted. The same Idempotency-Key is reused for every retry.

import asyncio
import os

from cage_fleet_sdk import CageFleetClient


async def main() -> None:
    async with CageFleetClient(
        base_url=os.environ.get(
            "CAGE_FLEET_BASE_URL", "https://eval.agentcyberrange.io/"
        ),
        task_token=os.environ["CAGE_FLEET_TASK_TOKEN"],
    ) as client:
        challenges = await client.list_challenges()
        if not challenges:
            raise RuntimeError("Fleet has no available Challenges")

        arena = await client.create_arena(challenges[0].challenge_id)
        print(arena.task_prompt)
        print(arena.entry_urls)

        result = await client.submit(arena.arena_id)
        print(result.verdict)


asyncio.run(main())

client.submit(arena_id) is a standalone public API. It does not require a Scheduler.

Run rolling-concurrency Rounds

Each Round has its own max_concurrency. A Work Slot is refilled immediately after an Arena obtains a terminal SubmissionResult; the Scheduler does not wait for a fixed wave to finish. Capacity Wait still occupies the task's Work Slot.

import asyncio
import os

from cage_fleet_sdk import (
    ArenaHandle,
    CageFleetClient,
    ChallengeTask,
    RoundConfig,
    RoundScheduler,
)


async def run_agent(arena: ArenaHandle) -> None:
    print(arena.task_prompt)
    print(arena.entry_urls)

    # Run your Agent here. It may submit early:
    await arena.submit()


async def main() -> None:
    async with CageFleetClient(
        base_url=os.environ.get(
            "CAGE_FLEET_BASE_URL", "https://eval.agentcyberrange.io/"
        ),
        task_token=os.environ["CAGE_FLEET_TASK_TOKEN"],
    ) as client:
        challenges = await client.list_challenges()
        if not challenges:
            raise RuntimeError("Fleet has no available Challenges")

        scheduler = RoundScheduler(client)
        result = await scheduler.run(
            rounds=[
                RoundConfig(
                    challenges=[
                        ChallengeTask(challenge_id=challenge.challenge_id)
                        for challenge in challenges
                    ],
                    max_concurrency=2,
                ),
            ],
            on_arena_ready=run_agent,
        )
        print(result.status)


asyncio.run(main())

If run_agent() returns without calling arena.submit(), the Scheduler submits automatically. If it raises, the Scheduler records agent_callback_failed and still submits to preserve completed markers. At lease_expires_at, it sets arena.cancel_event, cancels the callback task, and submits immediately.

The Scheduler intentionally does not persist or resume an Execution. If the user process exits, inspect the Arena and its result in Fleet Web.

Cancellation

The default preserves scoring:

await scheduler.cancel()  # submit active Arenas

Explicit close mode abandons scoring:

await scheduler.cancel(mode="close")

Cancellation stops new Challenge Tasks from starting. Task Outcomes already submitted retain their SubmissionResult; tasks not started are returned as cancelled.

Development verification

uv run pytest
uv run ruff check .
uv run mypy src
uv run python -m build

scripts/live_backend_smoke.py exercises every low-level client method against a running Fleet. scripts/live_scheduler_smoke.py asserts rolling refill and can assert capacity fallback with --expect-capacity-fallback; pass --expected-running-concurrency N to also prove that it saturates the capacity actually available.

Repository-only live Agent test

The repository's scripts/run_agent_test.py discovers every current Challenge through client.list_challenges(), runs each one once with a concurrency limit of two, and waits two minutes per mock Agent. When a mock Agent returns, the Scheduler submits its Arena and immediately refills the released Work Slot. The script verifies peak concurrency, rolling refill, outcomes, and that no active Arena remains.

uv sync --extra dev
uv run python scripts/run_agent_test.py

The script also adds this checkout's src/ directory to its import path, so it can load the local SDK source directly. Installing editable mode remains recommended because it also installs the SDK's third-party dependencies into the same Python environment.

Pass one or more --challenge-id arguments only when intentionally testing a subset. Use --max-concurrency or --mock-seconds to change the test. Supplying --agent-command 'python /absolute/path/to/my_agent.py' replaces the mock wait with a real Agent subprocess. That process receives the task prompt on standard input and Arena metadata in the AGENTCYBERRANGE_* environment variables. The default base URL is https://eval.agentcyberrange.io/; provide the Task Token through CAGE_FLEET_TASK_TOKEN or --task-token.

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

cage_fleet_sdk-0.1.0.tar.gz (48.2 kB view details)

Uploaded Source

Built Distribution

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

cage_fleet_sdk-0.1.0-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: cage_fleet_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 48.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for cage_fleet_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3b192cc2ca0707ae1e77f998bd4bfc143365b6b479e7178e6cb24e72ee4da158
MD5 ec7008d2c61760e7855f7b6480237c51
BLAKE2b-256 1636661f99b271e873ec870c5717b9c4d520021ab3f33a73beb4d2dd7012802b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: cage_fleet_sdk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for cage_fleet_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4123fd07d3c6390c27b02ed4135b18de3cf4880e2ef3966d527ef74bdf292e2b
MD5 04297150e6d89a5b594212faf3470992
BLAKE2b-256 66838e81a8047eea34f3cb0b69f003ba50b5abd58175e31c7a28c64378d65eac

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