Skip to content

Latest commit

 

History

History
657 lines (530 loc) · 35 KB

AGENTS.md

File metadata and controls

657 lines (530 loc) · 35 KB

AGENTS.md

This is the tool-neutral project instruction entry point for coding agents. Tool-specific compatibility files should point here rather than duplicate project guidance.

Project Overview

AKernel provides standalone and cluster-backed sandbox environments for agents and developer workflows. The current public user-facing surface is the Python akernel-sdk, including the akernel_sdk.Sandbox API and the ak CLI. The default sandbox runtime is gVisor runsc. Runtime identifiers and generic JSON-compatible runtime configuration are forwarded to the selected backend, which owns availability and compatibility checks. The bundled deployment also advertises Kata Containers and Firecracker on KVM-capable nodes. The native Linux runc payload is build-time optional and must be explicitly included and enabled by an operator. Creation-time network policies and atomic runtime replacement support unrestricted networking, blocking new flows except the YuanRong control and published sandbox-port routes, or denying exact and leading-wildcard DNS names. Experimental whole-device NVIDIA GPU requests require runsc. Configurable writable-storage requests are supported by runsc and Firecracker.

Use AKernel when a task needs an isolated remote environment with command execution, file operations, interactive PTYs, port forwarding, or reverse tunnels. The project overview and deployment quick start are in README.md, detailed SDK documentation is in sdk/python/README.md, and runnable examples are in sdk/python/examples/.

Source Layout

  • sdk/python/ - AKernel Python SDK and CLI.
  • sdk/python/akernel_sdk/ - SDK implementation for Sandbox, commands, filesystem, PTY support, instance plumbing, and CLI helpers.
  • sdk/python/akernel_sdk/_dockerfile_launch.py - lightweight public Dockerfile direct-launch configuration, independent of the parser and backend.
  • sdk/python/examples/ - maintained AKernel SDK examples.
  • sdk/python/tests/ - maintained AKernel SDK tests.
  • src/yuanrong/ - pinned openYuanRong mirror checkout, including its recursive component submodules.
  • builder/ - Dockerfiles, service configs, runtime rootfs build, and image entrypoint scripts for the public all-in-one image.
  • deploy/ - Helm charts, standalone scripts, Terraform modules, and deployment helper scripts.
  • deploy/standalone/manage.py - source-build standalone lifecycle shared by Linux and Apple Silicon Mac with OrbStack.
  • assets/ - static images used by the root README.

The open-source AKernel repository contains the SDK, deployment configuration, build tooling, and examples. Node runtime components such as sandboxd and distill-fs are maintained in their own upstream repositories and pinned as Git submodules. The all-in-one build compiles sandboxd and downloads the checksum-pinned static distill-fs release recorded in builder/distill-fs-versions.env. The distill-fs submodule is an optional source reference, not a build input. See the Build section below for runtime payloads.

Common Commands

All commands should be run from the repository root.

make help
make check VENDOR=aliyun
make config VENDOR=aliyun
make build
make standalone
make standalone-status
make standalone-stop
make push
make plan
make deploy
make token TTL=24h
make print-env
make sdk-test
make deploy-script-check
make deploy-standalone-test
make e2e

This is a command reference, not an unconditional sequence. Skip make build and make push when the deployment profile selects an existing image. The image repository and tag used by make build and make push come from the profile created by make config; set both during configuration rather than overriding only the build command.

make plan is read-only with respect to cloud resources. make deploy applies Terraform and Helm changes, while make destroy destroys cloud resources. Agents must show the plan and obtain explicit user approval before running either mutating command. Do not use AUTO_APPROVE=1 without that approval.

Local Deployment State

Interactive deployment helpers write local state under .akernel/default/ by default. Pass ENV=<name> when you need multiple independent deployment profiles. These directories are intentionally ignored by Git. They may contain:

  • generated Terraform variables
  • kubeconfig files and paths
  • IAM signing seeds
  • generated JWT tokens
  • SDK environment exports

The source standalone entry point uses .akernel/standalone/data/ by default, or an absolute AKERNEL_STANDALONE_DATA_DIR. It keeps generated runtime configuration, the signing seed, token, source-state.json, and sdk-env.sh together in that directory. The environment file reads the protected token file when sourced; lifecycle commands print credential paths without their values. This local profile is independent of cloud ENV profiles.

Never commit .akernel/, Terraform state, kubeconfigs, tokens, signing seeds, cloud credentials, private registry URLs, or local debug artifacts.

Build

AKernel uses Docker for building. The public distribution ships one all-in-one image that can run as master, frontend, node, or standalone depending on the deployment entrypoint and environment.

make build
make build RUNTIME_PROFILE=python
make build AKERNEL_ENABLE_RUNC=true
make build AKERNEL_ENABLE_FIRECRACKER=false

For a build that will be pushed and deployed, set IMAGE_REPOSITORY and IMAGE_TAG when creating the deployment profile. A one-off IMAGE_TAG override on make build does not update the profile consumed by make push. The build creates only the selected image reference; it does not add a second akernel-all-in-one alias. make push pushes that selected reference directly.

The build helper performs two Docker builds. builder/runtime.Dockerfile creates yr-runtime-rootfs.img; the default rrt profile contains the pinned openYuanRong RRT binary without Python. Set RUNTIME_PROFILE=python to include the optional Python 3.10 through 3.14 runtimes and openyuanrong_sdk. builder/node.Dockerfile then compiles the node components and produces the AKernel all-in-one image using the selected runtime image and its matching service configuration.

The control-plane and RRT release version is independent of the optional actor-based openyuanrong_sdk installed in the Python runtime profile. This actor backend is deprecated and retained only for compatibility with existing applications. Keep it on its explicitly pinned legacy version; do not advance it with the default openyuanrong-sandbox backend or use it for new features.

Initialize sandboxd with git submodule update --init src/sandboxd before building. The all-in-one image builds the sandboxd binaries, including firecracker-agent; installs checksum-pinned static distill-fs, gVisor, and Kata artifacts; installs the Firecracker VMM and guest kernel; and constructs the matching guest-agent initrd. Runc remains build-time optional, and AKERNEL_ENABLE_FIRECRACKER=false excludes the Firecracker payload.

AKernel builds virtiofsd 1.14.0 from the pinned source commit and release Cargo.lock in builder/node.Dockerfile. Keep its shared-library dependencies and licenses packaged with the Firecracker payload. Both standalone and Helm enable read-only virtio-fs by default; disabling the Firecracker image payload also excludes virtiofsd.

The sandboxd submodule's runtime manifest is the source of truth for the gVisor, runc, and Firecracker releases used by both sandboxd E2E and AKernel packaging. Test an unreleased runtime by checking out the sandboxd commit that pins it rather than overriding manifest fields from the AKernel build. Keep sandboxd's pooled-TAP contract and the matching gVisor compatibility patches validated together when upgrading.

Native Linux/arm64 builds support the rrt runtime profile with runsc and optional runc. Set AKERNEL_TARGETARCH=arm64, AKERNEL_ENABLE_KATA=false, and AKERNEL_ENABLE_FIRECRACKER=false; use AKERNEL_ENABLE_RUNC=true to include runc. ARM64 rejects the Python profile, VM payloads, and NVIDIA GPU requests. Builds require Docker BuildKit. The target precedence is explicit AKERNEL_TARGETARCH, then a supported single-platform DOCKER_DEFAULT_PLATFORM, then the stable amd64 default; never infer the builder's architecture from the Docker client's host. Explicit runtime flags override the selected deployment profile, which overrides defaults. Validate the merged flags, release pins, and URL/checksum override pairs before either Docker build.

The build selects architecture-matched gVisor and runc pins from the sandboxd manifest and distill-fs pins from builder/distill-fs-versions.env, explicitly sets both Docker build platforms, and passes the target architecture to sandboxd compilation. Check downloaded executable ELF headers against the target architecture with builder/scripts/verify-elf-arch.sh; checksum verification, manifest claims, or successful execution under emulation do not establish architecture. Keep the RRT release's AMD64 and ARM64 checksums synchronized with OPEN_YR_VERSION, and the collector archive checksums synchronized with OTELCOL_CONTRIB_VERSION. Collector URL overrides require a matching checksum. make build-helper-test covers build arguments and ELF validation without Docker builds.

Keep official build-download sources as the default and mirror/proxy selection explicit. Preserve third-party repository URLs, release pins, signing keys, checksums, and TLS verification. Forward proxy credentials only through Docker's predefined build arguments by name, without exposing or persisting values. The build download guide owns mirror scope, override precedence, proxy/network options, and release URL/checksum pairs; update it when changing those interfaces.

Install the complete checksum-pinned gVisor release archive with sandboxd's third_party/install-gvisor.sh. Preserve runsc, the containerd shim, and all four adjacent gvisor-bin/ helpers in the final image. The manifest SHA-512 covers the archive, not a bare runsc binary. Keep CI's final-image payload checks synchronized; do not mix helper versions or fetch them at runtime.

The sandboxd gitlink fixes the source revision compiled by make build. AKernel's builder/distill-fs-versions.env fixes the distill-fs release URL and SHA-256. make build compiles the local sandboxd worktree and consumes the static distill-fs release through builder/scripts/install-distill-fs.sh; editing src/distill-fs no longer affects the image. The installer verifies the archive, provenance, version, binary hash, and static ELF contract, and packages its licenses and manifest under /usr/local/share/distill-fs.

Publish and verify a distill-fs release before updating the AKernel manifest pin. This dependency does not require a sandboxd source or gitlink change. Never use a guessed checksum or silently fall back to a source build. Missing or invalid release pins prevent builds. make versions reports the release tag and archive digest without requiring the distill-fs submodule.

The optional node-wide ChunkDB capacity is configured with standalone AKERNEL_CHUNK_DB_SIZE, Helm node.config.sandboxd.chunkDbSize (under core in the umbrella chart), or Terraform chunk_db_size. Guided profiles accept make config CHUNK_DB_SIZE=64GiB. Empty values preserve the Linux 100 GiB default. Sandboxd passes the same capacity to every distill-fs user; existing-cache resizing is unsupported. Preserve the # AKERNEL_CHUNK_DB_SIZE marker in custom deployment templates. See deploy/README.md#shared-chunkdb-capacity for cache replacement semantics.

Each component embeds its own semantic version: sandboxd uses version/VERSION, while distill-fs uses its release package version in Cargo.toml. AKernel does not inject parent-repository version metadata into component compilation.

Inspect the selected local versions without building an image:

make versions

The final image uses standard OCI labels for the AKernel version and revision. Component semantic versions are reported by their binaries, and their exact source revisions are traceable through the sandboxd gitlink and the pinned distill-fs release's packaged manifest.

Deploy

Use deploy/README.md as the deployment entry point. AKernel supports standalone, existing Kubernetes clusters via Helm, and Terraform-based cloud provisioning.

The standalone guide owns source startup, supported platforms, runtime selection, and SDK setup. Preserve the source launcher's native-daemon architecture selection, clean sandboxd gitlink check, occupied-name preflight, profile locking, unique source image, and node/gateway health checks. Status and stop must verify recorded container identities, images, and data mounts; stop also requires an empty sandbox inventory. Source management is independent of cloud profiles and must preserve unrelated containers.

The all-in-one image and node launchers declare lowercase container=oci for PID 1 systemd. Preserve this in the final image, Helm node environment, and standalone launcher: without container detection, privileged systemd shutdown can remount shared host filesystems read-only. See deploy/README.md#systemd-container-identity for deployment implications.

Aliyun's aggregate Pod PID budget is configurable independently of the per-sandbox limit; see deploy/terraform/aliyun/README.md#pod-pid-budget. Aliyun and Huawei default AKernel node pools also configure host PID/thread ceilings and container scope TasksMax. Keep this separate from extra and Dragonfly pools, and verify running Pod ancestors after existing-node migration.

For guided cloud deployment:

make config VENDOR=aliyun
make plan
# After reviewing the plan and obtaining explicit approval:
make deploy
make print-env

Kata and Firecracker are present in the default AKernel runtime configuration but are optional node capabilities. Both require /dev/kvm to be usable from the node container. Firecracker additionally validates its VMM, guest kernel, initrd, and mkfs.ext4. A node without KVM remains ready for runsc workloads and advertises neither VM runtime; a Kata or Firecracker request fails scheduling with a no-resource error when no eligible node exists. Do not treat a configured runtime as an advertised runtime.

Firecracker supports commands, files, PTYs, network policies, published ports, reverse tunnels, EROFS roots and mounts, OCI/Nydus directory roots and read-only host directory mounts through virtio-fs, explicit storage_mb quotas, and recovery across sandboxd restarts. OCI image mounts, writable live host binds, NVIDIA GPUs, and nested KVM remain unsupported.

Do not add Firecracker-specific directory conversion, image caching, or artifact reference counting to sandboxd or its image manager. Consume OCI/Nydus directories directly from the image manager through read-only virtio-fs. Explicit local/S3 imagefile roots and mounts must already be EROFS. The bundled default runtime root also remains EROFS; sandbox writes use a private ext4 disk.

The bundled Firecracker writable disk policy is AsyncDirect with Writeback. Validate io_uring and STATX_DIOALIGN on the target host and filestore; use an explicit Async or Sync policy on incompatible hosts, never silent fallback. Keep standalone and Helm defaults synchronized. Drain before upgrading the runtime stack: checkpoint compatibility includes VMM, kernel, initrd, and virtiofsd digests, and restores retain the saved writable I/O engine.

Runc is excluded from default image builds and from the default advertised runtime set. Guided cloud profiles use make config ENABLE_RUNC=true; this records both the image build flag and Terraform runtime registration. For direct configuration, build an image with AKERNEL_ENABLE_RUNC=true, then use AKERNEL_ENABLE_RUNC=true for standalone, node.config.sandboxd.enableRunc=true for Helm, or enable_runc=true for Terraform. Runc uses the host kernel and therefore has a different isolation boundary from runsc. It does not support experimental GPU or explicit storage_mb requests. Its optional enableKVM extra configuration requires a usable /dev/kvm device.

Standalone configures resolver sources by DNS mode. Enabling runc copies an approved host resolver into the data mount and sets plugin.runtime.direct_resolv_conf_path, while plugin.runtime.resolv_conf_path remains the node resolver used by managed DNS. AKERNEL_RUNC_RESOLV_CONF selects the host source; systemd-resolved hosts require it explicitly because a flat file cannot preserve split DNS. Preserve the direct-resolver marker under [plugin.runtime] in custom templates, and drain sandboxes before refreshing the resolver snapshot or replacing the node. See deploy/standalone/README.md#dns-resolver-sources for mode selection and reachability requirements.

Standalone must check platform and image compatibility before modifying credentials or resolver configuration, and explicitly use the native ARM64 platform with OrbStack. Keep source and existing-image profile defaults documented in the standalone guide. Container proxy credentials belong in the protected profile env-file. Drain sandboxes before stopping the node; stop or removal failures must return nonzero, and stopped profiles retain data and identity files.

The bundled sandboxd configuration enables per-sandbox network ACLs. Pooled TAP networking requires the host tun module and a usable /dev/net/tun. The default iptables backend additionally requires iptables, ip6tables, ipset, IPv4/IPv6 filter tables, br_netfilter, xt_physdev, conntrack, conntrack-netlink, connmark/CONNMARK, timeout-capable hash:ip sets, and IPv4/IPv6 bridge netfilter. The optional bpfnat backend instead requires Linux 5.17 or newer for bpf_loop, eBPF SCHED_CLS, TC clsact, writable bpffs, and permission to load BPF programs and manage TC filters. Both require free TCP/UDP port 53 on the sandbox bridge. Drain existing sandboxes before enabling ACLs or upgrading a node from a pre-ACL configuration; sandboxd refuses to initialize ACLs when old sandbox records remain. A sandbox without a network policy stays unrestricted. Schema v2 supports independent ingress and egress defaults, allow and deny rules over IPv4 CIDRs, domains, protocols, and ports, plus an independent DNS policy. See deploy/README.md for deployment requirements and sdk/python/README.md for API limits.

Dragonfly distribution is optional and disabled by default. Enable it during profile generation with make config INSTALL_DRAGONFLY=true. This installs the pinned public chart and, by default, creates three seed nodes and one server node in dedicated pools. Review the generated Terraform plan and expected cost before applying it.

The deployment helper generates a stable IAM signing seed for the environment and passes it to the Helm chart through Terraform. This allows JWT tokens to be generated locally without exposing the IAM token API publicly.

If .akernel/default/ already exists, make config asks before overwriting config.env and terraform.tfvars. The existing iam-seed is reused unless you delete it or explicitly provide IAM_SEED_HEX, so previously generated tokens normally remain compatible.

For agent/non-interactive deployment setup, do not rely on prompts. Pass config values explicitly and use a named environment to avoid overwriting a user's default profile. Populate the region-specific values after checking the target cloud account; the number of Alibaba Cloud zones and vSwitch CIDRs must match.

ENV_NAME=agent-e2e
make config \
  ENV="${ENV_NAME}" \
  VENDOR=aliyun \
  NON_INTERACTIVE=1 \
  REGION="${REGION}" \
  ZONE_IDS="${ZONE_IDS}" \
  VSWITCH_CIDRS="${VSWITCH_CIDRS}" \
  IMAGE_REPOSITORY="${IMAGE_REPOSITORY}" \
  IMAGE_TAG="${IMAGE_TAG}"

make plan ENV="${ENV_NAME}"

Inspect an existing profile before reusing its name. Add FORCE=1 only when the user has explicitly approved overwriting its generated configuration.

JWT Tokens

Generate SDK tokens locally with:

make token TTL=24h
make token TTL=100y
make token TTL=never

make token and make print-env print JWT credentials. Treat their output as a secret: do not include it in logs, commits, or issue reports, and do not repeat it in chat unless the user explicitly requests credential handoff.

The token generator intentionally follows openYuanrong's current signed JWT format: the LITEBUS_DATA_KEY hex seed is decoded to bytes, the JWT header and payload are signed with HMAC-SHA256, and the hex digest string is base64url encoded.

Long-lived or never-expiring tokens are supported but should not be the default. Current signed JWT tokens are stateless; a leaked token cannot be revoked individually. Rotate the IAM signing seed to invalidate existing tokens.

SDK And CLI

Minimal sandbox usage:

from akernel_sdk import Sandbox

with Sandbox(cpu=2000, memory=4096) as sb:
    result = sb.commands.run("echo hello")
    print(result.stdout)

storage_mb sets the storage scheduling quota, while storage_limit_mb sets the actual writable-root hard limit. A zero limit follows the scheduling quota or cluster default. A positive limit without an explicit scheduling quota also reserves that amount for scheduling. Keep this distinction in SDK documentation and examples; see sdk/python/README.md for runtime support.

Same-node failover and explicit rollback retain the logical sandbox identity:

with Sandbox(failover=True) as sb:
    if not sb.reload():
        print("no local checkpoint is available")

The current functional integration deliberately leaves anonymous local checkpoint creation inside the workload through RRT's internal Unix socket. The node sets YR_RRT_CONTROL_SOCKET_PATH=/run/akernel, making the socket available at /run/akernel/rrt.sock. Do not present that socket protocol as a stable public SDK interface or add public checkpoint catalog methods to the SDK.

Select Kata explicitly only when the cluster has an eligible node:

with Sandbox(runtime="kata", cpu=2000, memory=4096) as sb:
    print(sb.commands.run("uname -s").stdout)

Select an explicitly enabled runc runtime and pass runtime-owned options:

with Sandbox(
    runtime="runc",
    extra_config={"enableKVM": True},
    cpu=2000,
    memory=4096,
) as sb:
    print(sb.commands.run("test -c /dev/kvm").exit_code)

Request an experimental gVisor GPU:

with Sandbox(xpu="gpu:l20:1") as sb:
    print(sb.commands.run("nvidia-smi -L").stdout)

Configure a creation-time network policy:

from akernel_sdk import NetworkPolicy, Sandbox

with Sandbox(network_policy=NetworkPolicy.block()) as sb:
    print(sb.commands.run("echo control-plane-access").stdout)

Configure a generic egress allowlist:

from akernel_sdk import NetworkPolicy, NetworkRule

policy = NetworkPolicy.allowlist(
    [NetworkRule(domain="*.example.com", protocol="tcp", port_range=443)]
)
with Sandbox(network_policy=policy) as sb:
    print(sb.commands.run("curl https://api.example.com").stdout)

Required environment:

export AKERNEL_SERVER_ADDRESS="<server_address>"
export AKERNEL_TOKEN="<your_token>"

When the public Traefik dual-entrypoint mode is enabled, a host/IP-only AKERNEL_SERVER_ADDRESS uses HTTPS/WSS on 443 for the frontend API and exec websocket, and HTTP on 80 for sandbox port URLs. For standalone deployments, use the Traefik container IP printed by deploy/standalone/start.sh:

export AKERNEL_SERVER_ADDRESS=<traefik-container-ip>

No separate AKERNEL_GATEWAY_ADDRESS is required for the default standalone layout. When a custom topology sets it, the override applies only to public sandbox port URLs and reverse tunnels; exec and file transfer continue to use AKERNEL_SERVER_ADDRESS. For source standalone, source the generated sdk-env.sh from the selected data directory. Direct start.sh uses akerneldev/all-in-one:latest by default; pass IMAGE for a locally built or differently tagged image.

Standalone GPU testing additionally requires NVIDIA Container Toolkit on the host and AKERNEL_ENABLE_GPU=true. sandboxd uses the read-only cgroup node-resource provider in standalone mode; Kubernetes deployments retain the Kubernetes provider. Standalone explicitly enables local DNAT because the frontend shares the node network namespace.

Standalone uses iptables NAT by default. Set AKERNEL_NAT_BACKEND=bpfnat to use the experimental embedded TC eBPF backend. AKernel prepares the required network-namespace sysctls, but bpfnat does not change firewall policy; custom host-network deployments with FORWARD=DROP must allow traffic to and from the sandbox bridge. YuanRong receives INSTANCE_IP in Kubernetes or the default-route interface address in standalone mode; AKERNEL_NODE_IP is the explicit override for multi-homed environments.

The standalone sandboxd filestore is a loop-mounted ext4 image under the selected bind-mounted data directory. Explicit storage_mb quotas for runsc and Firecracker use this local-disk filestore. Without an explicit quota, runsc retains its configured memory-backed overlay while Firecracker creates its configured sparse ext4 default.

Terraform-managed Alibaba Cloud node pools instead attach a dedicated 300 GiB ESSD by default, have ACK format it as XFS, and mount it at /home/akernel. The Aliyun sandboxd configuration leaves filestore_dir_size unset and uses /home/akernel/filestore directly, so writable layers and /home/akernel/checkpoints share the native reflink-capable filesystem. Do not set a bounded filestore size for this profile because that reintroduces a loop-backed filesystem and disables the high-performance Firecracker C/R layout.

The bundled node enables YuanRong's local-only sandbox snapshot data plane and stores checkpoint state under the persistent /home/akernel/checkpoints mount. RRT receives YR_RRT_CONTROL_SOCKET_PATH=/run/akernel so sandbox workloads can trigger their local checkpoint handoff through /run/akernel/rrt.sock. Recovery points follow the source sandbox lifecycle. The public SDK exposes only failover and reload, not checkpoint identifiers, restore, list, delete, or snapshot TTLs. Keep local-only snapshot mode and the persistent checkpoint directory configured together when changing node startup arguments.

Keep detailed SDK reference material with the SDK. The root README should contain only the project-level entry points and representative examples:

Install the SDK development tools and run the complete local quality gate with:

python3 -m pip install -e './sdk/python[dev]'
make sdk-check

The Python SDK installs openyuanrong-sandbox as its default execution backend. The actor-based openyuanrong-sdk backend is deprecated and retained only for compatibility with existing applications through the openyuanrong-sdk extra. Do not update its pinned legacy version alongside the default backend or extend it with new capabilities. Installing that extra leaves both distributions present, so openyuanrong-sandbox remains the automatic default unless AKERNEL_BACKEND=openyuanrong-sdk is set before import. Backend selection happens once during import and backend modules are loaded lazily on first use. Keep public Sandbox, Commands, Filesystem, and value types independent of both native packages; all native conversions belong under akernel_sdk._backends.

Keep sandbox cleanup explicit through context managers or kill(), with observable, retryable deletion failures and workload exceptions preserved on context exit. See SDK lifecycle guidance.

Dockerfile direct launch is a supported AKernel SDK capability through DockerContext and Sandbox(dockerfile=DockerfileLaunch(context=..., auto_start_cmd=..., run_timeout=...)). The capability will remain available. Its documented strict subset evolves incrementally with production experience, while unsupported inputs continue to fail closed. The specific API surface may evolve; material changes require documentation and migration guidance. Read sdk/python/docs/launch-from-dockerfile.md before changing this path. FROM supplies only the root filesystem; inherited OCI configuration is not applied. Runtime availability and compatibility remain backend-owned. DockerContext.walk() exposes public structured file and directory entries, including modes and empty directories; context transfer must remain backend-neutral, reject unsafe manifests and unsupported syntax fail-closed, and preserve documented Dockerfile-specific ignore-file precedence. Dockerfiles and active or root ignore files remain ordinary context entries unless the active matcher excludes them. Keep the public types, unit tests, SDK README, Dockerfile launch guide, and examples/dockerfile_launch.py in sync.

When changing a public SDK method, update its type annotations and docstring, add or update unit coverage, and keep the SDK README and maintained examples in sync. Benchmark programs under sdk/python/benchmarks/ are manual tools and are not part of the default test suite.

Release

The CI workflow builds the public Linux/amd64 all-in-one image with only gVisor runsc and the rrt runtime profile. It explicitly sets AKERNEL_ENABLE_KATA=false, AKERNEL_ENABLE_FIRECRACKER=false, and AKERNEL_ENABLE_RUNC=false, excluding VM payloads and virtiofsd. Source-build defaults still include Kata and Firecracker for operators who need them. After SDK checks, distribution validation, deployment syntax checks, and standalone runsc E2E pass, pushes to main in inclusionAI/AKernel publish that tested image only as akerneldev/all-in-one:latest. PRs and forks never publish. Check that the commit is still the current main head before any push; superseded commits and reruns of older commits skip publication entirely. Do not publish per-commit SHA tags or other historical image tags. The job checks the image contains runsc and excludes Kata, Firecracker, virtiofsd, and runc before starting standalone E2E.

CI runs examples with unbuffered Python output. Ordinary examples have a 120-second limit; dockerfile_launch.py gets 600 seconds for its nine sections. Its core startup script uses the Ubuntu base image's shell and does not install packages. Keep its RUN, context-transfer, and startup checks independent of external package mirrors.

Configure the repository Actions variable DOCKERHUB_USERNAME and secret DOCKERHUB_TOKEN with Docker Hub credentials that can push to akerneldev/all-in-one. Keep credentials out of source and logs. Main CI runs are not canceled by subsequent pushes. After E2E and teardown succeed on upstream main, the standalone job exports the tested image as a compressed Actions artifact retained for one day. The separate publish-dockerhub job downloads that exact artifact by ID, loads it, and verifies its image ID against the E2E job output before publishing. PRs and forks skip image transfer and publication. Do not rebuild the image in the publishing job.

The publishing job serializes publication using queue: max (up to 100 pending jobs), so a slow or rerun job cannot overwrite a newer published latest. Keep its existing lock key to coordinate with older workflow runs. Check main before downloading and again immediately before pushing while holding this lock. Do not remove the main-head check or the publication lock. A failed check or push leaves the run failed. Rerun the failed publishing job while the artifact is available; after it expires, rerun all jobs to rebuild and retest the image.

Python SDK releases use stable vX.Y.Z tags or release-candidate vX.Y.ZrcN tags. The tag version must match the version in sdk/python/pyproject.toml, and the tagged commit must be part of main. Publishing a GitHub Release runs .github/workflows/release-python.yml, which checks the SDK, builds and tests the wheel and source distribution, and publishes them through the PyPI trusted publisher configured for the pypi GitHub environment. Do not add a PyPI password or API token to the repository.

PR CI and publishing share .github/actions/python-distributions, which builds the wheel and source distribution and installs each in a separate clean environment with its declared dependencies. Keep dependency validation, isolated imports, version checks, and CLI smoke tests in this shared action.

Test

Run SDK unit tests with:

make sdk-test

Run syntax checks for the tracked Bash deployment scripts, Terraform shell templates, and Python deployment helpers with:

make deploy-script-check

For standalone lifecycle or launch changes, also run the helper tests without starting containers:

make deploy-standalone-test

Run the basic e2e example against a deployed cluster with:

make e2e

The SDK integration and pressure tests are runtime-selectable. Kata and Firecracker tests require a KVM-capable standalone or cluster node:

AKERNEL_RUN_INTEGRATION=1 \
AKERNEL_TEST_RUNTIME=kata \
python sdk/python/tests/integration/test_sandbox.py -v

python sdk/python/benchmarks/sandbox_pressure.py --runtime kata

AKERNEL_RUN_INTEGRATION=1 \
AKERNEL_TEST_RUNTIME=firecracker \
python sdk/python/tests/integration/test_sandbox.py -v

python sdk/python/benchmarks/sandbox_pressure.py --runtime firecracker
python sdk/python/benchmarks/sandbox_pressure.py \
  --runtime firecracker --storage-mb 256
python sdk/python/benchmarks/sandbox_pressure.py \
  --xpu gpu:a10:1 --storage-mb 256 --processes 1 --threads 1

Set AKERNEL_TEST_IMAGE=ubuntu:24.04 with AKERNEL_TEST_RUNTIME=firecracker to run integration and reload coverage against an OCI/Nydus image root. This also verifies that two sandboxes using the same image have private writes. Test both an ordinary OCI image and a Nydus image resolved through the deployed image manager. The pinned distill-fs supports RAFS v5; use nydusify convert --fs-version 5 when preparing Nydus test images.

Maintenance Rules

  • Keep root README and repo-level agent guidance focused on current akernel-sdk sandbox workflows.
  • Prefer linking to sdk/python/README.md for detailed SDK usage instead of copying long examples into other docs.
  • Do not reintroduce obsolete top-level examples or tests. Maintained SDK examples and tests live under sdk/python/.
  • When code changes alter source layout, public APIs, build or deployment commands, supported platforms, or operational assumptions, update this file in the same change so future agents receive current guidance.
  • Use Conventional Commits with a concise title and a prose body explaining what changed and why; do not create title-only commits. Keep a blank line between title and body, wrap the body for terminal readability, and sign commits with git commit -s to include the Developer Certificate of Origin sign-off.
  • Keep unrelated dirty files out of commits, especially local deployment state, generated binaries, Terraform state, kubeconfigs, tokens, and private registry configuration.