OpenEnv documentation
CLI
CLI
The openenv CLI provides a set of commands for building, validating, and pushing environments to Hugging Face Spaces or a custom Docker registry. For an end-to-end tutorial on building environments with OpenEnv, see the building an environment guide.
openenv init
openenv.cli.commands.init
< source >( env_name: Annotated[str, typer.Argument(help="Name of the environment to create (snake_case, e.g., 'my_env')")] output_dir: Annotated[str | None, typer.Option('--output-dir', '-o', help='Output directory (defaults to current working directory)')] = None )
Initialize a new OpenEnv environment.
Creates a new directory with the environment name and generates all necessary files based on the OpenEnv template structure.
Example: $ openenv init my_game_env $ openenv init my_env —output-dir /path/to/projects
openenv build
openenv.cli.commands.build
< source >( env_path: Annotated[str | None, typer.Argument(help='Path to the environment directory (default: current directory)')] = None tag: Annotated[str | None, typer.Option('--tag', '-t', help='Docker image tag (default: openenv-<env_name>)')] = None context: Annotated[str | None, typer.Option('--context', '-c', help='Build context path (default: <env_path>/server)')] = None dockerfile: Annotated[str | None, typer.Option('--dockerfile', '-f', help='Path to Dockerfile (default: <context>/Dockerfile)')] = None no_cache: Annotated[bool, typer.Option('--no-cache', help='Build without using cache')] = False build_arg: Annotated[list[str] | None, typer.Option('--build-arg', help='Build arguments (can be used multiple times, format: KEY=VALUE)')] = None )
Build Docker images for OpenEnv environments.
This command builds Docker images using the environment’s pyproject.toml and uv for dependency management. Run from the environment root directory.
Examples:
Build from environment root (recommended)
$ cd my_env $ openenv build
Build with custom tag
$ openenv build -t my-custom-tag
Build without cache
$ openenv build —no-cache
Build with custom build arguments
$ openenv build —build-arg VERSION=1.0 —build-arg ENV=prod
Build from different directory
$ openenv build envs/echo_env
openenv validate
openenv.cli.commands.validate
< source >( target: typing.Annotated[str | None, <typer.models.ArgumentInfo object at 0x7f6a7ba9f310>] = None url: typing.Annotated[str | None, <typer.models.OptionInfo object at 0x7f6a7ba9c460>] = None json_output: typing.Annotated[bool, <typer.models.OptionInfo object at 0x7f6a7ba9ebf0>] = False timeout: typing.Annotated[float, <typer.models.OptionInfo object at 0x7f6a7ba9f820>] = 5.0 verbose: typing.Annotated[bool, <typer.models.OptionInfo object at 0x7f6a7ba9f130>] = False )
Validate local environments and running OpenEnv servers.
Local validation checks if an environment is properly configured with:
- Required files (pyproject.toml, openenv.yaml, server/app.py, etc.)
- Docker deployment support
- uv run server capability
- python -m module execution
Runtime validation checks if a live OpenEnv server conforms to the versioned runtime API contract and returns a criteria-based JSON report.
Examples:
Validate current directory (recommended)
$ cd my_env $ openenv validate
Validate a running environment and return JSON criteria
$ openenv validate —url http://localhost:8000 $ openenv validate https://my-env.hf.space
Validate with detailed output
$ openenv validate —verbose
Validate specific environment
$ openenv validate envs/echo_env
openenv push
openenv.cli.commands.push
< source >( directory: Annotated[str | None, typer.Argument(help='Directory containing the OpenEnv environment (default: current directory)')] = None repo_id: Annotated[str | None, typer.Option('--repo-id', '-r', help="Repository ID as 'repo_name' or 'namespace/repo_name'. Defaults to 'username/env-name' from openenv.yaml.")] = None base_image: Annotated[str | None, typer.Option('--base-image', '-b', help='Base Docker image to use (overrides Dockerfile FROM)')] = None interface: Annotated[bool, typer.Option('--interface', help='Enable web interface (default: True if no registry specified)')] = None no_interface: Annotated[bool, typer.Option('--no-interface', help='Disable web interface')] = False registry: Annotated[str | None, typer.Option('--registry', help='Custom registry URL (e.g., docker.io/username). Disables web interface by default.')] = None private: Annotated[bool, typer.Option('--private', help='Deploy the space as private')] = False create_pr: Annotated[bool, typer.Option('--create-pr', help='Create a Pull Request instead of pushing to the default branch')] = False exclude: Annotated[str | None, typer.Option('--exclude', help='Optional additional ignore file with newline-separated glob patterns to exclude from Hugging Face uploads')] = None hardware: Annotated[str | None, typer.Option('--hardware', '-H', help='Request hardware for Hugging Face Space (e.g. t4-medium, cpu-basic). See HF docs for options.')] = None count: Annotated[int, typer.Option('--count', '-n', help='Number of Space instances to deploy. Each gets a numeric suffix (e.g. env-1, env-2).', min=1)] = 1 env_vars: Annotated[list[str] | None, typer.Option('--env-var', '-e', help='Public Space variable as KEY=VALUE (repeatable). Overrides matching keys from openenv.yaml variables:.')] = None secrets: Annotated[list[str] | None, typer.Option('--secret', help='Private Space secret as KEY=VALUE (repeatable). Value is never logged.')] = None )
Push an OpenEnv environment to Hugging Face Spaces or a custom Docker registry.
This command:
- Validates that the directory is an OpenEnv environment (openenv.yaml present)
- Builds and pushes to Hugging Face Spaces or custom Docker registry
- Optionally enables web interface for deployment
The web interface is enabled by default when pushing to HuggingFace Spaces, but disabled by default when pushing to a custom Docker registry.
Examples:
Push to HuggingFace Spaces from current directory (web interface enabled)
$ cd my_env $ openenv push
Push to HuggingFace repo and open a Pull Request
$ openenv push my-org/my-env —create-pr $ openenv push —repo-id my-org/my-env —create-pr
Push to HuggingFace without web interface
$ openenv push —no-interface
Push to Docker Hub
$ openenv push —registry docker.io/myuser
Push to GitHub Container Registry
$ openenv push —registry ghcr.io/myorg
Push to custom registry with web interface
$ openenv push —registry myregistry.io/path1/path2 —interface
Push to specific HuggingFace repo
$ openenv push —repo-id my-org/my-env
Push privately with custom base image
$ openenv push —private —base-image ghcr.io/meta-pytorch/openenv-base:latest
Push with GPU hardware
$ openenv push —hardware t4-medium
Set a public Space variable (overrides openenv.yaml variables:)
$ openenv push -e OPENSPIEL_GAME=tic_tac_toe -e MAX_STEPS=100
Set a private Space secret (value never logged)
$ openenv push —secret OPENAI_API_KEY=sk-…
openenv serve
openenv.cli.commands.serve
< source >( env_path: Annotated[str | None, typer.Argument(help='Path to the environment directory (default: current directory)')] = None port: Annotated[int, typer.Option('--port', '-p', help='Port to serve on')] = 8000 host: Annotated[str, typer.Option('--host', help='Host to bind to')] = '0.0.0.0' reload: Annotated[bool, typer.Option('--reload', help='Enable auto-reload on code changes')] = False )
Serve an OpenEnv environment locally.
TODO: This command is currently not implemented and has been deferred for later.
Planned functionality:
- Run environment server locally without Docker
- Support multiple deployment modes (local, notebook, cluster)
- Auto-reload for development
- Integration with environment’s [project.scripts] entry point
For now, use Docker-based serving:
- Build the environment: openenv build
- Run the container: docker run -p 8000:8000 <image-name>
Or use uv directly: uv run —project . server —port 8000
openenv fork
openenv.cli.commands.fork
< source >( source_space: Annotated[str, typer.Argument(help="Source Space ID in format 'owner/space-name' (e.g. org/my-openenv-space)")] repo_id: Annotated[str | None, typer.Option('--repo-id', '-r', help='Target repo ID for the fork (default: created under your account with same name)')] = None private: Annotated[bool, typer.Option('--private', help='Create the forked Space as private')] = False set_env: Annotated[list[str], typer.Option('--set-env', '-e', help='Set Space variable (public). Can be repeated. Format: KEY=VALUE')] = [] set_secret: Annotated[list[str], typer.Option('--set-secret', '--secret', '-s', help='Set Space secret. Can be repeated. Format: KEY=VALUE')] = [] hardware: Annotated[str | None, typer.Option('--hardware', '-H', help='Request hardware (e.g. t4-medium, cpu-basic). See Hub docs for options.')] = None )
Fork (duplicate) a Hugging Face Space to your account using the Hub API.
Uses the Hugging Face duplicate_space API. You can set environment variables and secrets, and request hardware/storage/sleep time at creation time.
Examples: $ openenv fork owner/source-space $ openenv fork owner/source-space —private $ openenv fork owner/source-space —repo-id myuser/my-fork $ openenv fork owner/source-space —set-env MODEL_ID=user/model —set-secret HF_TOKEN=hf_xxx $ openenv fork owner/source-space —hardware t4-medium
openenv skills
Installs an openenv-cli skill into your AI assistant’s skills directory so
it knows the openenv CLI is available and what each command does. Supports
Claude Code, Cursor, Codex, and OpenCode.
Install for a single assistant (project-local):
openenv skills add --claude # → .claude/skills/openenv-cli/
openenv skills add --cursor # → .cursor/skills/openenv-cli/
openenv skills add --codex # → .codex/skills/openenv-cli/
openenv skills add --opencode # → .opencode/skills/openenv-cli/Multiple flags can be combined — openenv skills add --claude --cursor installs
for both at once. The skill file is written to a central location
(.agents/skills/openenv-cli/) and each agent directory gets a symlink, so
there is only one copy to update.
Install globally (user-level, across all projects):
openenv skills add --claude --global # → ~/.claude/skills/openenv-cli/Overwrite an existing installation (e.g. after upgrading openenv):
openenv skills add --claude --force
Preview the skill content without installing:
openenv skills preview
Install to a custom path (for non-standard agent setups):
openenv skills add --dest /path/to/my-agent/skills/
openenv.cli.commands.skills.skills_add
< source >( claude: Annotated[bool, typer.Option('--claude', help='Install for Claude.')] = False codex: Annotated[bool, typer.Option('--codex', help='Install for Codex.')] = False cursor: Annotated[bool, typer.Option('--cursor', help='Install for Cursor.')] = False opencode: Annotated[bool, typer.Option('--opencode', help='Install for OpenCode.')] = False global_: Annotated[bool, typer.Option('--global', '-g', help='Install globally (user-level) instead of in the current project directory.')] = False dest: Annotated[Path | None, typer.Option(help='Install into a custom destination (skills directory path).')] = None force: Annotated[bool, typer.Option('--force', help='Overwrite existing skills in the destination.')] = False )
Install OpenEnv CLI skill for AI assistants.
Print generated SKILL.md content.
API Reference
Entry point
Main entry point for the CLI.
CLI helpers
openenv.cli._cli_utils.validate_env_structure
< source >( env_dir: Path strict: bool = False )
Validate that the directory follows OpenEnv environment structure.
Validation utilities
openenv.cli._validation.validate_running_environment
< source >( base_url: str timeout_s: float = 5.0 )
Validate a running OpenEnv server against runtime API standards.
The returned JSON report contains an overall pass/fail result and per-criterion outcomes that can be consumed in CI.
Validate that an environment is ready for multi-mode deployment.
Checks:
- pyproject.toml exists
- uv.lock exists
- pyproject.toml has [project.scripts] with server entry point
- server/app.py has a main() function
- Required dependencies are present
Check which deployment modes are supported by the environment.
openenv.cli._validation.format_validation_report
< source >( env_name: str is_valid: bool issues: list )
Format a validation report for display.
openenv.cli._validation.build_local_validation_json_report
< source >( env_name: str env_path: Path is_valid: bool issues: list deployment_modes: dict[str, bool] | None = None )
Build a JSON report for local environment validation.