diff --git a/AGENTS.md b/AGENTS.md index 41c87c3..ec10b30 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,185 +35,25 @@ Keep it current when commands or conventions change. - `kinitro/crypto.py` cryptography helpers. - `kinitro/rl_interface.py` RL interface utilities (Observation, Action, ProprioKeys, ActionKeys). -## Setup / Install - -- Recommended: `uv sync` -- Editable install: `pip install -e .` -- Dev extras (if using pip): `pip install -e ".[dev]"` -- Basilica CLI (for miner deployments): `curl -sSL https://basilica.ai/install.sh | bash` - -## Common Commands - -### Lint - -- `ruff check .` (checks entire project: kinitro/, tests/, environments/, scripts/, demos/) - -### Type Check - -- `ty check .` - -### Tests - -- `pytest tests/` -- With MuJoCo: `MUJOCO_GL=egl pytest tests/` - -### Single Test (examples) - -- `pytest tests/unit/test_pareto.py::TestEpsilonDominates::test_clear_dominance` -- `pytest tests/unit/test_pareto.py -k test_clear_dominance` -- `pytest -k pareto tests/unit` - -### CLI Examples - -- List environments: `uv run kinitro env list` -- Test an env: `uv run kinitro env test metaworld/pick-place-v3` -- Build env image: `uv run kinitro env build --env-id metaworld/pick-place-v3 --tag my-env:v1` - -## Git Hooks - -- Hook script: `.githooks/pre-commit` invokes the `pre-commit` tool for ruff formatting/linting, then runs `ty` type checking. -- Setup: `git config core.hooksPath .githooks && uv tool install pre-commit` -- The hook uses `.pre-commit-config.yaml` for ruff rules via the pre-commit framework. - -## Services (local dev) - -- API: `uv run kinitro api --database-url postgresql://user:pass@host/db` -- Scheduler: `uv run kinitro scheduler --netuid --network finney --database-url postgresql://user:pass@host/db` - - Filter to specific environment families: `--env-families metaworld` or `--env-families metaworld,genesis` -- Executor: `uv run kinitro executor --api-url http://localhost:8000` - - Docker mode: `--eval-mode docker --eval-images '{"metaworld":"image:tag"}'` - - Basilica mode: `--eval-mode basilica --eval-images '{"metaworld":"image:tag"}'` -- Validator: `uv run kinitro validate --backend-url https://api.kinitro.ai --netuid --network finney` - -## Backend Setup (operator quick start) - -- Init DB: `uv run kinitro db init --database-url postgresql://user:pass@host/db` -- DB status: `uv run kinitro db status --database-url postgresql://user:pass@host/db` - -## Environment Config - -- See `.env.example` for common env vars. -- Runtime settings are read via Pydantic settings classes in `kinitro/config.py`. -- Keep secrets out of the repo; do not commit `.env` files or any files listed in `.gitignore`. - -## Code Style Guidelines - -### Imports - -- Group imports: standard library, third-party, then local. -- Keep imports at the top of the file (after module docstring if present). -- Sort with Ruff `I` rules (no separate isort config). -- Prefer explicit imports over wildcard imports. - -### Formatting - -- 4-space indentation. -- Line length target: 100 (`ruff` ignores `E501` but still keep lines reasonable). -- Use trailing commas in multi-line literals and call arguments. - -### Typing - -- Python 3.12 syntax (`list[int]`, `dict[str, float]`, `str | None`). -- Add type hints to all function signatures (parameters and return types), not just public ones. -- Prefer `BaseSettings` and `BaseModel` type annotations for config/DTOs. -- `ty` runs in CI; avoid `Any` unless required and explain why in code. -- Use `NewType` to distinguish domain identifiers that share an underlying primitive (e.g., `MinerUID`, `BlockNumber`, `EnvironmentId`, `Hotkey`). This prevents accidental misuse such as passing a `MinerUID` where a `BlockNumber` is expected. -- Centralize shared newtypes, type aliases, enums, and `TypedDict` definitions in `kinitro/types.py`. Import from there rather than re-defining types locally. -- When introducing a new domain concept that is fundamentally a `str`, `int`, or other primitive, create a `NewType` for it in `kinitro/types.py` and use it consistently across signatures, models, and data structures. -- Prefer `TypedDict` or `dataclasses.dataclass` over plain `dict` for structured data with known keys. - -### Naming - -- `snake_case` for functions, variables, modules. -- `PascalCase` for classes, Pydantic models, and custom types. -- `UPPER_CASE` for constants. -- Test files: `test_*.py`; test classes: `Test*`. - -### Error Handling - -- CLI commands use `typer.Exit(1)` for user-facing failures. -- Provide actionable error messages; log details with `structlog`. -- Avoid broad `except Exception` unless you re-raise or return a clear error. - -### Logging - -- Use `structlog.get_logger()` and structured log fields. -- Log at info/debug/warn/error levels consistently with existing patterns. - -### Async and IO - -- Use `async def` for I/O and DB operations. -- Use `asyncio.run(...)` at CLI boundaries. -- Use `asynccontextmanager` and session managers for DB work. - -### Pydantic / API Models - -- Pydantic models live in `kinitro/backend/models.py`. -- Prefer `Field(...)` for validation constraints and docs. -- Use `from_attributes = True` for ORM-to-model conversion. - -### SQLAlchemy - -- ORM models are in `kinitro/backend/models.py`. -- Keep DB writes inside the storage layer (`kinitro/backend/storage.py`). -- Use `select(...).where(...).limit(...)` patterns for queries. - -### Tests - -- Keep tests deterministic; use fixed seeds (`np.random.default_rng(42)`). -- Use clear asserts; prefer `np.testing` for array comparisons. -- Keep unit tests fast; integration tests go in `tests/integration`. - -## End-to-End Testing - -For detailed testing docs and troubleshooting, see `docs/e2e-testing.md`. - -### Quick Reference - -```bash -# Start all services with mock miner (uses default ports) -./scripts/services.sh start --mock-miner - -# Run test scripts -./scripts/test-e2e.sh # Full E2E pipeline test -./scripts/test-api.sh # Test API endpoints -./scripts/test-mock-miner.sh # Test mock miner endpoints - -# Service management -./scripts/services.sh status # Check running services -./scripts/services.sh logs # Tail service logs -./scripts/services.sh stop # Stop all services -``` - -For multi-worktree development (avoids port collisions between parallel checkouts): - -```bash -./scripts/worktree-env.sh # Generate isolated ports/database (once per worktree) -./scripts/services.sh start --all # Uses worktree-specific ports -``` - -### API Endpoints - -Note: The API prefix is `/v1/` (not `/api/v1/`). - -- Health: `GET /health` -- Miners: `GET /v1/miners` -- Environments: `GET /v1/environments` -- Scores: `GET /v1/scores/latest` -- Weights: `GET /v1/weights/latest` -- Task stats: `GET /v1/tasks/stats` - -## Docs and References - -- Developer overview: `README.md`. -- Backend operator guide: `docs/backend-guide.md`. -- Miner guide: `docs/miner-guide.md`. -- Validator guide: `docs/validator-guide.md`. -- Scoring and incentives: `docs/scoring-and-incentives.md`. -- **Adding new environments**: `environments/README.md` - Complete guide for integrating new robotics environments. +## Development Standards + +All setup instructions, commands, code style guidelines, and testing +procedures are documented in [CONTRIBUTING.md](CONTRIBUTING.md). +Key topics: + +- **Setup**: Prerequisites, `uv sync`, dev install +- **Commands**: Lint (`ruff check .`), format (`ruff format .`), + type check (`ty check .`), tests (`pytest tests/`) +- **Git hooks**: `.githooks/pre-commit` setup +- **Local services**: API, scheduler, executor, validator +- **Code style**: Imports, formatting, typing, naming, error handling, + logging, async, Pydantic, SQLAlchemy, tests +- **E2E testing**: Service scripts, worktree isolation +- **CI checks**: What runs on PRs (ruff format, ruff lint, ty) ## Change Hygiene for Agents - Do not modify files outside the task scope. - Avoid editing generated files and caches (e.g., `.ruff_cache/`). - Keep commits small and focused; update docs if behavior changes. +- Run `ruff check .`, `ruff format --check .`, and `ty check .` before finishing. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a2f09bd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,236 @@ +# Contributing to Kinitro + +Welcome! Kinitro is a Bittensor subnet for evaluating generalist robotics +policies. See the [README](README.md) for an overview of the project +architecture and quick-start instructions. + +This guide covers everything you need to develop, test, and contribute to +Kinitro. + +## Prerequisites and Setup + +- Python 3.12 or higher is required. +- Install with [uv](https://github.com/astral-sh/uv) (recommended): `uv sync` +- Editable install: `pip install -e .` +- Dev extras (if using pip): `pip install -e ".[dev]"` +- Basilica CLI (for miner deployments): `curl -sSL https://basilica.ai/install.sh | bash` + +## Common Commands + +### Lint + +- `ruff check .` (checks entire project) + +### Format + +- `ruff format .` (format all files) +- `ruff format --check .` (check formatting without modifying — used in CI) + +### Type Check + +- `ty check .` + +### Tests + +- `pytest tests/` +- With MuJoCo: `MUJOCO_GL=egl pytest tests/` + +### Single Test (examples) + +- `pytest tests/unit/test_pareto.py::TestEpsilonDominates::test_clear_dominance` +- `pytest tests/unit/test_pareto.py -k test_clear_dominance` +- `pytest -k pareto tests/unit` + +### CLI Examples + +- List environments: `uv run kinitro env list` +- Test an env: `uv run kinitro env test metaworld/pick-place-v3` +- Build env image: `uv run kinitro env build metaworld --tag my-env:v1` + +## Git Hooks + +- Hook script: `.githooks/pre-commit` invokes the `pre-commit` tool for ruff formatting/linting, then runs `ty` type checking. +- Setup: `git config core.hooksPath .githooks && uv tool install pre-commit` +- The hook uses `.pre-commit-config.yaml` for ruff rules via the pre-commit framework. + +## Services (Local Development) + +- API: `uv run kinitro api --database-url postgresql://user:pass@host/db` +- Scheduler: `uv run kinitro scheduler --netuid --network finney --database-url postgresql://user:pass@host/db` + - Filter to specific environment families: `--env-families metaworld` or `--env-families metaworld,genesis` +- Executor: `uv run kinitro executor --api-url http://localhost:8000` + - Docker mode: `--eval-mode docker --eval-images '{"metaworld":"image:tag"}'` + - Basilica mode: `--eval-mode basilica --eval-images '{"metaworld":"image:tag"}'` +- Validator: `uv run kinitro validate --backend-url https://api.kinitro.ai --netuid --network finney` + +## Backend Setup + +- Init DB: `uv run kinitro db init --database-url postgresql://user:pass@host/db` +- DB status: `uv run kinitro db status --database-url postgresql://user:pass@host/db` + +## Environment Config + +- See `.env.example` for common env vars. +- Runtime settings are read via Pydantic settings classes in `kinitro/config.py`. +- Keep secrets out of the repo; do not commit `.env` files or any files listed in `.gitignore`. + +## Code Style Guidelines + +### Imports + +- Group imports: standard library, third-party, then local. +- Keep imports at the top of the file (after module docstring if present). +- Sort with Ruff `I` rules (no separate isort config). +- Prefer explicit imports over wildcard imports. + +### Formatting + +- 4-space indentation. +- Line length target: 100 (`ruff` ignores `E501` but still keep lines reasonable). +- Use trailing commas in multi-line literals and call arguments. + +### Typing + +- Python 3.12 syntax (`list[int]`, `dict[str, float]`, `str | None`). +- Add type hints to all function signatures (parameters and return types), not just public ones. +- Prefer `BaseSettings` and `BaseModel` type annotations for config/DTOs. +- `ty` runs in CI; avoid `Any` unless required and explain why in code. +- Use `NewType` to distinguish domain identifiers that share an underlying primitive (e.g., `MinerUID`, `BlockNumber`, `EnvironmentId`, `Hotkey`). This prevents accidental misuse such as passing a `MinerUID` where a `BlockNumber` is expected. +- Centralize shared newtypes, type aliases, enums, and `TypedDict` definitions in `kinitro/types.py`. Import from there rather than re-defining types locally. +- When introducing a new domain concept that is fundamentally a `str`, `int`, or other primitive, create a `NewType` for it in `kinitro/types.py` and use it consistently across signatures, models, and data structures. +- Prefer `TypedDict` or `dataclasses.dataclass` over plain `dict` for structured data with known keys. + +### Naming + +- `snake_case` for functions, variables, modules. +- `PascalCase` for classes, Pydantic models, and custom types. +- `UPPER_CASE` for constants. +- Test files: `test_*.py`; test classes: `Test*`. + +### Error Handling + +- CLI commands use `typer.Exit(1)` for user-facing failures. +- Provide actionable error messages; log details with `structlog`. +- Avoid broad `except Exception` unless you re-raise or return a clear error. + +### Logging + +- Use `structlog.get_logger()` and structured log fields. +- Log at info/debug/warn/error levels consistently with existing patterns. + +### Async and IO + +- Use `async def` for I/O and DB operations. +- Use `asyncio.run(...)` at CLI boundaries. +- Use `asynccontextmanager` and session managers for DB work. + +### Pydantic / API Models + +- Pydantic models live in `kinitro/backend/models.py`. +- Prefer `Field(...)` for validation constraints and docs. +- Use `from_attributes = True` for ORM-to-model conversion. + +### SQLAlchemy + +- ORM models are in `kinitro/backend/models.py`. +- Keep DB writes inside the storage layer (`kinitro/backend/storage.py`). +- Use `select(...).where(...).limit(...)` patterns for queries. + +### Tests + +- Keep tests deterministic; use fixed seeds (`np.random.default_rng(42)`). +- Use clear asserts; prefer `np.testing` for array comparisons. +- Keep unit tests fast; integration tests go in `tests/integration`. + +## Testing + +For detailed testing docs and troubleshooting, see [`docs/e2e-testing.md`](docs/e2e-testing.md). + +### Quick Reference + +```bash +# Start all services with mock miner (uses default ports) +./scripts/services.sh start --mock-miner + +# Run test scripts +./scripts/test-e2e.sh # Full E2E pipeline test +./scripts/test-api.sh # Test API endpoints +./scripts/test-mock-miner.sh # Test mock miner endpoints + +# Service management +./scripts/services.sh status # Check running services +./scripts/services.sh logs # Tail service logs +./scripts/services.sh stop # Stop all services +``` + +### Worktree Development + +For multi-worktree development (avoids port collisions between parallel checkouts): + +```bash +./scripts/worktree-env.sh # Generate isolated ports/database (once per worktree) +./scripts/services.sh start --all # Uses worktree-specific ports +``` + +## How to Contribute + +1. Fork the repo and clone your fork. +2. Create a branch from `main` with a descriptive name: + - `feature/...` for new features + - `fix/...` for bug fixes + - `docs/...` for documentation changes +3. Make your changes, following the code style guidelines above. +4. Run checks locally before pushing: + + ```bash + ruff format --check . + ruff check . + ty check . + pytest tests/ + ``` + +5. Open a pull request against `main`. + +## CI Checks + +Every pull request runs the following checks automatically: + +| Check | Command | Purpose | +| ------------- | ----------------------- | ----------------------------- | +| Ruff format | `ruff format --check .` | Enforce consistent formatting | +| Ruff lint | `ruff check .` | Catch lint issues | +| Ty type check | `ty check .` | Static type analysis | + +## Commit Conventions + +This project follows [Conventional Commits](https://www.conventionalcommits.org/): + +```text +(): +``` + +**Types:** `feat`, `fix`, `refactor`, `perf`, `chore`, `test`, `docs` + +**Scopes** (optional): `genesis`, `executor`, `scheduler`, `scoring`, `cli`, `crypto`, `rl_interface` + +Examples: + +- `feat(executor): add GPU passthrough for Docker eval containers` +- `fix(cli): use lazy bittensor imports to prevent argparse hijacking` +- `refactor: deduplicate code` + +## Types of Contributions + +- **Code** — core package in `kinitro/` +- **Environments** — evaluation environments in `environments/` (see [environments/README.md](environments/README.md)) +- **Documentation** — guides and references in `docs/` +- **Tests** — unit and integration tests in `tests/` + +## Docs and References + +- Developer overview: [`README.md`](README.md) +- Backend operator guide: [`docs/backend-guide.md`](docs/backend-guide.md) +- Miner guide: [`docs/miner-guide.md`](docs/miner-guide.md) +- Validator guide: [`docs/validator-guide.md`](docs/validator-guide.md) +- Scoring and incentives: [`docs/scoring-and-incentives.md`](docs/scoring-and-incentives.md) +- Adding new environments: [`environments/README.md`](environments/README.md) diff --git a/README.md b/README.md index 0d72702..4319a1f 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,14 @@ This subnet incentivizes the development of **generalist robotics policies** - A Only policies that **generalize across ALL environments** earn rewards, using ε-Pareto dominance scoring. -https://github.com/user-attachments/assets/37942435-8143-41cf-aa78-39f4e8a04509 + ## Key Features ### Vision-Based Observations Miners receive **limited observations** to prevent overfitting: + - **Genesis**: Full-body proprioception (base pose, joint positions/velocities), ego camera (84x84) - Object positions are **NOT exposed** - miners must learn from visual input @@ -102,12 +103,12 @@ flowchart TB ### Service Components -| Service | Command | Purpose | Scaling | -|---------|---------|---------|---------| -| **API** | `kinitro api` | REST API, task pool management | Horizontal (stateless) | -| **Scheduler** | `kinitro scheduler` | Task generation, scoring, weight computation | Single instance | -| **Executor** | `kinitro executor` | Run evaluations (MuJoCo, Genesis) via [Affinetes](https://github.com/AffineFoundation/affinetes/) | Horizontal (GPU machines) | -| **Validator** | `kinitro validate` | Submit weights to chain | Per validator | +| Service | Command | Purpose | Scaling | +| ------------- | ------------------- | ------------------------------------------------------------------------------------------------- | ------------------------- | +| **API** | `kinitro api` | REST API, task pool management | Horizontal (stateless) | +| **Scheduler** | `kinitro scheduler` | Task generation, scoring, weight computation | Single instance | +| **Executor** | `kinitro executor` | Run evaluations (MuJoCo, Genesis) via [Affinetes](https://github.com/AffineFoundation/affinetes/) | Horizontal (GPU machines) | +| **Validator** | `kinitro validate` | Submit weights to chain | Per validator | ### Evaluation Flow @@ -218,73 +219,27 @@ uv run kinitro executor --api-url http://localhost:8000 The API service exposes these endpoints: -| Endpoint | Description | -|----------|-------------| -| `GET /health` | Health check | -| `GET /v1/status` | Current backend status | -| `GET /v1/weights/latest` | Latest computed weights | -| `GET /v1/weights/{block}` | Weights for specific block | -| `GET /v1/scores/latest` | Latest evaluation scores | -| `GET /v1/scores/{cycle_id}` | Scores for specific cycle | -| `GET /v1/miners` | List evaluated miners | -| `GET /v1/environments` | List environments | -| `POST /v1/tasks/fetch` | Fetch tasks (for executors) | -| `POST /v1/tasks/submit` | Submit results (for executors) | -| `GET /v1/tasks/stats` | Task pool statistics | +| Endpoint | Description | +| --------------------------- | ------------------------------ | +| `GET /health` | Health check | +| `GET /v1/status` | Current backend status | +| `GET /v1/weights/latest` | Latest computed weights | +| `GET /v1/weights/{block}` | Weights for specific block | +| `GET /v1/scores/latest` | Latest evaluation scores | +| `GET /v1/scores/{cycle_id}` | Scores for specific cycle | +| `GET /v1/miners` | List evaluated miners | +| `GET /v1/environments` | List environments | +| `POST /v1/tasks/fetch` | Fetch tasks (for executors) | +| `POST /v1/tasks/submit` | Submit results (for executors) | +| `GET /v1/tasks/stats` | Task pool statistics | ## Environments -### MetaWorld (Manipulation) - -MuJoCo-based robot arm manipulation tasks: - -- `metaworld/reach-v3` - Move end-effector to target position -- `metaworld/push-v3` - Push object to goal location -- `metaworld/pick-place-v3` - Pick up object and place at target -- `metaworld/door-open-v3` - Open a door -- `metaworld/drawer-open-v3` - Open a drawer -- `metaworld/drawer-close-v3` - Close a drawer -- `metaworld/button-press-v3` - Press a button from top-down -- `metaworld/peg-insert-v3` - Insert peg into hole - -### Genesis (Humanoid Locomotion + Manipulation) - -Genesis physics simulation with a Unitree G1 humanoid robot in procedurally generated scenes: - -- `genesis/g1-v0` - Scene-grounded tasks with a bipedal humanoid (43 actuated DOFs): - - Navigate to objects - - Pick up small objects - - Place objects at target locations - - Push objects towards destinations - -Use `kinitro env list` to see all available environments. +Two environment families: **MetaWorld** (robot arm manipulation, dev/testing only) and **Genesis** (Unitree G1 humanoid locomotion + manipulation). Use `kinitro env list` to see all available environments. See [environments/README.md](environments/README.md) for full details. ## Miner Policy Interface -Miners deploy a FastAPI server with these endpoints: - -```python -# POST /reset - Reset for new episode -async def reset(task_config: dict) -> str: - """Called at start of each episode. Returns episode_id.""" - pass - -# POST /act - Get action for observation -async def act(observation: np.ndarray, images: dict | None) -> np.ndarray: - """ - Return action for observation. Must respond within 500ms. - - Args: - observation: Proprioceptive state [ee_x, ee_y, ee_z, gripper_state] - images: Optional camera images {"corner": (84,84,3), "gripper": (84,84,3)} - - Returns: - Action as numpy array in [-1, 1] range - """ - return action -``` - -See the [Miner Guide](docs/miner-guide.md) and `kinitro/miner/template/` for complete examples. +Miners deploy a FastAPI server with `/reset` and `/act` endpoints using `Observation` and `Action` types from `kinitro/rl_interface.py`. See the [Miner Guide](docs/miner-guide.md) and `kinitro/miner/template/` for the interface specification and complete examples. ## Development @@ -299,12 +254,15 @@ pytest tests/ MUJOCO_GL=egl pytest tests/ # Type checking -mypy kinitro/ +ty check . # Linting ruff check kinitro/ ``` +See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development guide, +code style, and contribution workflow. + ## Configuration Use environment variables or a `.env` file. See `.env.example` for configuration options. diff --git a/docker-compose.yml b/docker-compose.yml index 0f1577e..cc04d82 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,7 +81,7 @@ services: environment: - MUJOCO_GL=egl volumes: - # Mount Docker socket for affinetes + # Mount Docker socket for Affinetes - /var/run/docker.sock:/var/run/docker.sock depends_on: - api diff --git a/docs/backend-guide.md b/docs/backend-guide.md index b994d40..b5e5f22 100644 --- a/docs/backend-guide.md +++ b/docs/backend-guide.md @@ -13,7 +13,7 @@ flowchart TB DB[(PostgreSQL)] SIM["Isolated Simulations - (affinetes)"] + (Affinetes)"] MINER["Miner Basilica Endpoints"] @@ -131,13 +131,13 @@ Each service (API, Scheduler, Executor) has its own configuration with service-s ### API Service Configuration -| CLI Flag | Environment Variable | Default | Description | -| ---------------- | -------------------------- | -------------------------- | ----------------------------------------------- | -| `--host` | `KINITRO_API_HOST` | `0.0.0.0` | API server bind address | -| `--port` | `KINITRO_API_PORT` | `8000` | API server port | -| `--database-url` | `KINITRO_API_DATABASE_URL` | `postgresql+asyncpg://...` | PostgreSQL connection URL | -| `--no-auth` | - | `false` | Disable API key authentication | -| `--log-level` | `KINITRO_API_LOG_LEVEL` | `INFO` | Logging level | +| CLI Flag | Environment Variable | Default | Description | +| ---------------- | -------------------------- | -------------------------- | ------------------------------ | +| `--host` | `KINITRO_API_HOST` | `0.0.0.0` | API server bind address | +| `--port` | `KINITRO_API_PORT` | `8000` | API server port | +| `--database-url` | `KINITRO_API_DATABASE_URL` | `postgresql+asyncpg://...` | PostgreSQL connection URL | +| `--no-auth` | - | `false` | Disable API key authentication | +| `--log-level` | `KINITRO_API_LOG_LEVEL` | `INFO` | Logging level | **Authentication:** API key authentication is **enabled by default**. Set the `KINITRO_API_API_KEY` environment variable to configure the key. Use `--no-auth` to disable authentication. @@ -149,39 +149,39 @@ Additional API settings: ### Scheduler Service Configuration -| CLI Flag | Environment Variable | Default | Description | -| -------------------- | ----------------------------------------- | -------------------------- | ------------------------------------------------------------------------- | -| `--database-url` | `KINITRO_SCHEDULER_DATABASE_URL` | `postgresql+asyncpg://...` | PostgreSQL connection URL | -| `--network` | `KINITRO_SCHEDULER_NETWORK` | `finney` | Bittensor network | -| `--netuid` | `KINITRO_SCHEDULER_NETUID` | `1` | Subnet UID | -| `--eval-interval` | `KINITRO_SCHEDULER_EVAL_INTERVAL_SECONDS` | `3600` | Seconds between eval cycles | -| `--episodes-per-env` | `KINITRO_SCHEDULER_EPISODES_PER_ENV` | `50` | Episodes per environment | +| CLI Flag | Environment Variable | Default | Description | +| -------------------- | ----------------------------------------- | -------------------------- | ------------------------------------------------------------------------ | +| `--database-url` | `KINITRO_SCHEDULER_DATABASE_URL` | `postgresql+asyncpg://...` | PostgreSQL connection URL | +| `--network` | `KINITRO_SCHEDULER_NETWORK` | `finney` | Bittensor network | +| `--netuid` | `KINITRO_SCHEDULER_NETUID` | `1` | Subnet UID | +| `--eval-interval` | `KINITRO_SCHEDULER_EVAL_INTERVAL_SECONDS` | `3600` | Seconds between eval cycles | +| `--episodes-per-env` | `KINITRO_SCHEDULER_EPISODES_PER_ENV` | `50` | Episodes per environment | | `--env-families` | `KINITRO_SCHEDULER_ENV_FAMILIES` | `null` (all) | Filter to specific families, comma-separated (e.g., `metaworld,genesis`) | -| `--log-level` | `KINITRO_SCHEDULER_LOG_LEVEL` | `INFO` | Logging level | +| `--log-level` | `KINITRO_SCHEDULER_LOG_LEVEL` | `INFO` | Logging level | Additional Scheduler settings: -| Environment Variable | Default | Description | -| ------------------------------------------------ | ------- | -------------------------------------------- | -| `KINITRO_SCHEDULER_PARETO_TEMPERATURE` | `1.0` | Softmax temperature for weight conversion | -| `KINITRO_SCHEDULER_TASK_STALE_THRESHOLD_SECONDS` | `900` | Time after which assigned tasks become stale | -| `KINITRO_SCHEDULER_CYCLE_TIMEOUT_SECONDS` | `7200` | Maximum time to wait for cycle completion | +| Environment Variable | Default | Description | +| ------------------------------------------------ | ------- | --------------------------------------------- | +| `KINITRO_SCHEDULER_PARETO_TEMPERATURE` | `1.0` | Softmax temperature for weight conversion | +| `KINITRO_SCHEDULER_TASK_STALE_THRESHOLD_SECONDS` | `900` | Time after which assigned tasks become stale | +| `KINITRO_SCHEDULER_CYCLE_TIMEOUT_SECONDS` | `7200` | Maximum time to wait for cycle completion | | `KINITRO_SCHEDULER_BACKEND_PRIVATE_KEY` | `null` | X25519 private key (hex) for endpoint decrypt | | `KINITRO_SCHEDULER_BACKEND_PRIVATE_KEY_FILE` | `null` | Path to backend private key file | ### Executor Service Configuration -| CLI Flag | Environment Variable | Default | Description | -| ------------------ | ---------------------------------------- | ----------------------- | --------------------------------------------------- | -| `--api-url` | `KINITRO_EXECUTOR_API_URL` | `http://localhost:8000` | URL of the Kinitro API service | -| `--batch-size` | `KINITRO_EXECUTOR_BATCH_SIZE` | `10` | Number of tasks to fetch at a time | -| `--poll-interval` | `KINITRO_EXECUTOR_POLL_INTERVAL_SECONDS` | `5` | Seconds between polling for tasks | -| `--eval-images` | `KINITRO_EXECUTOR_EVAL_IMAGES` | See below | Env family to Docker image mapping | -| `--eval-mode` | `KINITRO_EXECUTOR_EVAL_MODE` | `docker` | Evaluation mode: 'docker' or 'basilica' | -| `--log-level` | `KINITRO_EXECUTOR_LOG_LEVEL` | `INFO` | Logging level | -| `--concurrent` | `KINITRO_EXECUTOR_USE_CONCURRENT_EXECUTOR` | `false` | Enable multi-process concurrent executor | -| `--max-concurrent` | `KINITRO_EXECUTOR_DEFAULT_MAX_CONCURRENT` | `20` | Max concurrent tasks per environment family | -| `--env-families` | `KINITRO_EXECUTOR_ENV_FAMILIES` | `null` | Comma-separated families (defaults to eval_images) | +| CLI Flag | Environment Variable | Default | Description | +| ------------------ | ------------------------------------------ | ----------------------- | -------------------------------------------------- | +| `--api-url` | `KINITRO_EXECUTOR_API_URL` | `http://localhost:8000` | URL of the Kinitro API service | +| `--batch-size` | `KINITRO_EXECUTOR_BATCH_SIZE` | `10` | Number of tasks to fetch at a time | +| `--poll-interval` | `KINITRO_EXECUTOR_POLL_INTERVAL_SECONDS` | `5` | Seconds between polling for tasks | +| `--eval-images` | `KINITRO_EXECUTOR_EVAL_IMAGES` | See below | Env family to Docker image mapping | +| `--eval-mode` | `KINITRO_EXECUTOR_EVAL_MODE` | `docker` | Evaluation mode: 'docker' or 'basilica' | +| `--log-level` | `KINITRO_EXECUTOR_LOG_LEVEL` | `INFO` | Logging level | +| `--concurrent` | `KINITRO_EXECUTOR_USE_CONCURRENT_EXECUTOR` | `false` | Enable multi-process concurrent executor | +| `--max-concurrent` | `KINITRO_EXECUTOR_DEFAULT_MAX_CONCURRENT` | `20` | Max concurrent tasks per environment family | +| `--env-families` | `KINITRO_EXECUTOR_ENV_FAMILIES` | `null` | Comma-separated families (defaults to eval_images) | **Authentication:** Set `KINITRO_EXECUTOR_API_KEY` environment variable to authenticate with the API server. @@ -225,8 +225,8 @@ Additional Executor settings: These environment variables are read **inside** the Genesis evaluation container (not by the executor process). They control rendering behaviour during evaluations. -| Environment Variable | Default | Description | -| ---------------------- | ------- | --------------------------------------------------------- | +| Environment Variable | Default | Description | +| ---------------------- | ------- | --------------------------------------------------------------------- | | `GENESIS_RENDER_DEPTH` | `0` | Enable depth rendering (`1` to enable; disabled by default for speed) | Depth rendering is disabled by default for speed. Enable it if the miner policy uses depth images. Configure it by setting the environment variable on the Docker container via your orchestration layer (e.g., Docker Compose, Kubernetes). @@ -300,12 +300,12 @@ uv run kinitro executor \ **Concurrent executor settings:** -| Environment Variable | Default | Description | -| --------------------------------------------- | ------- | ---------------------------------------- | -| `KINITRO_EXECUTOR_USE_CONCURRENT_EXECUTOR` | `false` | Enable multi-process concurrent executor | -| `KINITRO_EXECUTOR_DEFAULT_MAX_CONCURRENT` | `20` | Default max concurrent tasks per family | -| `KINITRO_EXECUTOR_MAX_CONCURRENT_PER_FAMILY` | JSON | Per-family concurrency limits (see below)| -| `KINITRO_EXECUTOR_ENV_FAMILIES` | `null` | Families to run (defaults to eval_images)| +| Environment Variable | Default | Description | +| -------------------------------------------- | ------- | ----------------------------------------- | +| `KINITRO_EXECUTOR_USE_CONCURRENT_EXECUTOR` | `false` | Enable multi-process concurrent executor | +| `KINITRO_EXECUTOR_DEFAULT_MAX_CONCURRENT` | `20` | Default max concurrent tasks per family | +| `KINITRO_EXECUTOR_MAX_CONCURRENT_PER_FAMILY` | JSON | Per-family concurrency limits (see below) | +| `KINITRO_EXECUTOR_ENV_FAMILIES` | `null` | Families to run (defaults to eval_images) | **Per-family concurrency configuration:** @@ -314,115 +314,26 @@ export KINITRO_EXECUTOR_MAX_CONCURRENT_PER_FAMILY='{"metaworld": 50, "genesis": ``` The concurrent mode uses a producer-consumer pattern: + - **Fetch loop (producer)**: Pulls tasks from API with backpressure - **Execution workers (consumers)**: N concurrent async workers per family - **Semaphore**: Limits concurrent task executions ## API Reference -The backend exposes a REST API that validators use to fetch weights. - -### Health & Status - -#### `GET /health` - -Health check endpoint. - -```json -{"status": "healthy", "database": "connected"} -``` - -#### `GET /v1/status` - -Current backend status. - -```json -{ - "status": "running", - "current_block": 12345, - "last_eval_block": 12300, - "next_eval_in_seconds": 1800, - "miners_registered": 15, - "environments_enabled": ["metaworld/pick-place-v3", "metaworld/push-v3"] -} -``` - -### Weights (Used by Validators) +The backend exposes a REST API. See the endpoint table in [README.md](../README.md#api-endpoints) for the full list. The key endpoint for validators: -#### `GET /v1/weights/latest` - -Get the latest computed weights. +### `GET /v1/weights/latest` ```json { "block": 12300, - "weights": { - "0": 0.15, - "1": 0.25, - "5": 0.60 - }, + "weights": { "0": 0.15, "1": 0.25, "5": 0.60 }, "created_at": "2024-01-15T10:30:00Z" } ``` -#### `GET /v1/weights/{block}` - -Get weights for a specific block. - -### Scores - -#### `GET /v1/scores/latest` - -Get latest evaluation scores. - -```json -{ - "cycle_id": 42, - "block": 12300, - "scores": { - "0": { - "metaworld/pick-place-v3": 0.85, - "metaworld/push-v3": 0.72 - }, - "1": { - "metaworld/pick-place-v3": 0.90, - "metaworld/push-v3": 0.88 - } - }, - "pareto_frontier": [1], - "created_at": "2024-01-15T10:30:00Z" -} -``` - -#### `GET /v1/scores/{cycle_id}` - -Get scores for a specific evaluation cycle. - -### Miners - -#### `GET /v1/miners` - -List all discovered miners. - -```json -{ - "miners": [ - { - "uid": 0, - "hotkey": "5F...", - "endpoint": "https://xxx.deployments.basilica.ai", - "last_evaluated": "2024-01-15T10:30:00Z", - "status": "active" - } - ] -} -``` - -### Environments - -#### `GET /v1/environments` - -List enabled evaluation environments. +Executor task endpoints (`POST /v1/tasks/fetch`, `POST /v1/tasks/submit`) require API key authentication — see [API Key Authentication](#api-key-authentication) below. ## Database Management @@ -479,6 +390,7 @@ uv run kinitro crypto generate-keypair --name backend ``` This creates (in `$XDG_CONFIG_HOME/kinitro/keys/`, typically `~/.config/kinitro/keys/`): + - `backend` - Private key (keep secret!) - `backend.pub` - Public key (share with miners) @@ -536,7 +448,8 @@ uv run kinitro crypto test-encryption --private-key-file ~/.config/kinitro/keys/ ## Production Deployment -### Docker Compose +
+Docker Compose Create `docker-compose.backend.yml`: @@ -598,7 +511,7 @@ services: api: condition: service_started volumes: - - /var/run/docker.sock:/var/run/docker.sock # Required for affinetes + - /var/run/docker.sock:/var/run/docker.sock # Required for Affinetes restart: always volumes: @@ -622,7 +535,10 @@ Run: docker-compose -f docker-compose.backend.yml up -d ``` -### Systemd Services +
+ +
+Systemd Services Create separate services for each component: @@ -682,7 +598,10 @@ RestartSec=10 WantedBy=multi-user.target ``` -### Reverse Proxy (nginx) +
+ +
+Reverse Proxy (nginx) ```nginx server { @@ -702,6 +621,8 @@ server { } ``` +
+ ## Monitoring ### Logs @@ -782,7 +703,7 @@ Monitor these conditions: ### Memory issues -- Increase `KINITRO_BACKEND_EVAL_MEM_LIMIT` +- Increase `KINITRO_EXECUTOR_EVAL_MEM_LIMIT` - Monitor container memory: `docker stats` - Consider running fewer concurrent evaluations @@ -827,32 +748,15 @@ uv run kinitro api --database-url postgresql://... --no-auth ``` **Notes:** + - Authentication is configured via environment variables only (no CLI flags for keys) - Requests without a valid `Authorization: Bearer ` header receive 401 Unauthorized - The `/v1/tasks/stats`, `/health`, and weight/score endpoints remain unauthenticated - Use `--no-auth` only for local development; always enable auth in production -## Scaling - -### Horizontal Scaling - -For high miner counts, run multiple evaluation workers: - -- Use a job queue (Redis, RabbitMQ) for evaluation tasks -- Run multiple backend instances with shared database -- Load balance API requests - -### GPU Acceleration - -For faster simulation: - -- Use NVIDIA Docker runtime -- Set `MUJOCO_GL=egl` for headless rendering -- Allocate GPU to eval containers - ## Additional Resources - [Validator Guide](./validator-guide.md) - For validators polling this backend - [Miner Guide](./miner-guide.md) - For miners participating in the subnet -- [affinetes](https://github.com/affine-lab/affinetes) - Container-based evaluation library +- [Affinetes](https://github.com/AffineFoundation/affinetes) - Container-based evaluation library - [Bittensor Docs](https://docs.bittensor.com/) - Bittensor documentation diff --git a/docs/e2e-testing.md b/docs/e2e-testing.md index e3ebd6a..25ca63f 100644 --- a/docs/e2e-testing.md +++ b/docs/e2e-testing.md @@ -118,13 +118,13 @@ basilica deploy delete --yes ## Troubleshooting -| Problem | Solution | -|---------|----------| -| Port conflicts | Run `./scripts/worktree-env.sh` to regenerate ports, then `./scripts/services.sh stop` | -| Database issues | `PGPASSWORD=postgres psql -h localhost -p $POSTGRES_PORT -U postgres -c "DROP DATABASE $POSTGRES_DB; CREATE DATABASE $POSTGRES_DB;"` | -| Stuck eval containers | `docker stop $(docker ps -q --filter "name=kinitro-eval")` | -| Check miner commitment | `uv run kinitro miner show-commitment --netuid ... --wallet-name ...` | -| Verify miner endpoint | `curl /health` | +| Problem | Solution | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| Port conflicts | Run `./scripts/worktree-env.sh` to regenerate ports, then `./scripts/services.sh stop` | +| Database issues | `PGPASSWORD=postgres psql -h localhost -p $POSTGRES_PORT -U postgres -c "DROP DATABASE $POSTGRES_DB; CREATE DATABASE $POSTGRES_DB;"` | +| Stuck eval containers | `docker stop $(docker ps -q --filter "name=kinitro-eval")` | +| Check miner commitment | `uv run kinitro miner show-commitment --netuid ... --wallet-name ...` | +| Verify miner endpoint | `curl /health` | ## Cleanup Commands @@ -147,12 +147,12 @@ basilica deploy delete --yes The backend uses these PostgreSQL tables: -| Table | Description | -|-------|-------------| -| `evaluation_cycles` | Cycle metadata (id, block_number, status, n_miners, n_environments, duration_seconds) | -| `miner_scores` | Per-miner per-environment scores (uid, hotkey, env_id, success_rate, mean_reward, episodes_completed) | -| `computed_weights` | Final weights per cycle (cycle_id, block_number, weights_json, weights_u16_json) | -| `task_pool` | Individual evaluation tasks (task_uuid, cycle_id, miner_uid, env_id, seed, status, result) | +| Table | Description | +| ------------------- | ----------------------------------------------------------------------------------------------------- | +| `evaluation_cycles` | Cycle metadata (id, block_number, status, n_miners, n_environments, duration_seconds) | +| `miner_scores` | Per-miner per-environment scores (uid, hotkey, env_id, success_rate, mean_reward, episodes_completed) | +| `computed_weights` | Final weights per cycle (cycle_id, block_number, weights_json, weights_u16_json) | +| `task_pool` | Individual evaluation tasks (task_uuid, cycle_id, miner_uid, env_id, seed, status, result) | ### Query Examples diff --git a/docs/miner-guide.md b/docs/miner-guide.md index 471d526..5bf2c95 100644 --- a/docs/miner-guide.md +++ b/docs/miner-guide.md @@ -88,6 +88,8 @@ Your policy receives **canonical observations** that vary by environment family. ### MetaWorld (Robot Arm Manipulation) +> **Note:** MetaWorld is for local testing and development only; not used in mainnet evaluations. + **Proprioceptive** (`proprio` key): - `ee_pos`: End-effector XYZ position (meters) @@ -575,74 +577,7 @@ Key implications: ### Testing Endpoints -#### Local Testing (Before Deployment) - -Test your policy locally using the FastAPI server: - -```bash -cd my-policy - -# Start the local server -uvicorn server:app --host 0.0.0.0 --port 8001 - -# Test endpoints locally -curl http://localhost:8001/health -curl -X POST http://localhost:8001/reset \ - -H "Content-Type: application/json" \ - -d '{"task_config": {"task_name": "pick-place-v3", "seed": 42}}' -curl -X POST http://localhost:8001/act \ - -H "Content-Type: application/json" \ - -d '{"obs": {"proprio": {"ee_pos": [0.0, 0.0, 0.0], "ee_quat": [0.0, 0.0, 0.0, 1.0], "ee_vel_lin": [0.0, 0.0, 0.0], "ee_vel_ang": [0.0, 0.0, 0.0], "gripper": [1.0]}, "rgb": {}}}' -``` - -#### Remote Testing (After Deployment) - -Test your deployed Basilica endpoint: - -```bash -ENDPOINT="https://YOUR-DEPLOYMENT-ID.deployments.basilica.ai" - -# Health check -curl "${ENDPOINT}/health" - -# Reset -curl -X POST "${ENDPOINT}/reset" \ - -H "Content-Type: application/json" \ - -d '{"task_config": {"task_name": "pick-place-v3", "seed": 42}}' - -# Act -curl -X POST "${ENDPOINT}/act" \ - -H "Content-Type: application/json" \ - -d '{"obs": {"proprio": {"ee_pos": [0.0, 0.0, 0.0], "ee_quat": [0.0, 0.0, 0.0, 1.0], "ee_vel_lin": [0.0, 0.0, 0.0], "ee_vel_ang": [0.0, 0.0, 0.0], "gripper": [1.0]}, "rgb": {}}}' -``` - -## Example Training Setup - -Here's a suggested training approach: - -```python -import metaworld -import torch - -# Create multi-task environment -mt = metaworld.MT10() # 10 tasks - -# Sample tasks -for name, env_cls in mt.train_classes.items(): - env = env_cls() - task = random.choice([t for t in mt.train_tasks if t.env_name == name]) - env.set_task(task) - - # Collect data with your policy - obs = env.reset() - for _ in range(500): - action = policy(obs) # Your policy - obs, reward, done, info = env.step(action) - # Store transition for training - -# Train policy on collected data -# Use behavior cloning, RL, or imitation learning -``` +Use the same curl commands from [Step 4](#step-4-test-locally) for local testing. For remote testing after deployment, replace `http://localhost:8001` with your Basilica endpoint URL (`https://YOUR-DEPLOYMENT-ID.deployments.basilica.ai`). ## Additional Resources diff --git a/docs/validator-guide.md b/docs/validator-guide.md index aecc1a1..d626bf8 100644 --- a/docs/validator-guide.md +++ b/docs/validator-guide.md @@ -10,9 +10,9 @@ This guide explains how to run a validator for Kinitro. Validators are **lightwe │ - Runs evaluations on miner policies │ │ - Computes epsilon-Pareto scores │ │ - Exposes REST API: GET /v1/weights/latest │ -└──────────────────────────┬──────────────────────────────────────┘ - │ HTTP - ▼ +└───────────────────────────────┬─────────────────────────────────┘ + │ HTTP + ▼ ┌─────────────────────────────────────────────────────────────────┐ │ YOUR VALIDATOR │ │ - Polls backend for weights │ @@ -111,7 +111,7 @@ sudo journalctl -u kinitro-validator -f # View logs ## Docker Deployment ```dockerfile -FROM python:3.11-slim +FROM python:3.12-slim WORKDIR /app RUN pip install uv diff --git a/environments/README.md b/environments/README.md index ed03696..84f1215 100644 --- a/environments/README.md +++ b/environments/README.md @@ -83,8 +83,8 @@ Genesis physics simulation with a Unitree G1 humanoid robot in procedurally gene The Genesis container reads these optional environment variables to tune rendering performance: -| Variable | Default | Description | -| ---------------------- | ------- | ---------------------------------------------------------- | +| Variable | Default | Description | +| ---------------------- | ------- | --------------------------------------------- | | `GENESIS_RENDER_DEPTH` | `0` | Set to `1` to enable depth rendering (slower) | Example: enable depth rendering if your policy uses depth images: @@ -95,11 +95,10 @@ GENESIS_RENDER_DEPTH=1 ## Backend Configuration -Configure your backend to use the appropriate image for each environment family. Set these environment variables: +Configure your executor to use the appropriate image for each environment family. Set the environment variable: ```bash -KINITRO_BACKEND_EVAL_IMAGE_METAWORLD=kinitro/metaworld:v1 -KINITRO_BACKEND_EVAL_IMAGE_GENESIS=kinitro/genesis:v1 +KINITRO_EXECUTOR_EVAL_IMAGES='{"metaworld": "kinitro/metaworld:v1", "genesis": "kinitro/genesis:v1"}' ``` --- @@ -114,12 +113,12 @@ cp -r environments/_template environments/myenv ### Required Files -| File | Purpose | -|------|---------| -| `metadata.json` | Display name and description | -| `requirements.txt` | Python dependencies | -| `Dockerfile` | Container build spec | -| `env.py` | Actor class with `evaluate()` method | +| File | Purpose | +| ------------------ | ------------------------------------ | +| `metadata.json` | Display name and description | +| `requirements.txt` | Python dependencies | +| `Dockerfile` | Container build spec | +| `env.py` | Actor class with `evaluate()` method | ### Key Concepts diff --git a/kinitro/backend/storage.py b/kinitro/backend/storage.py index 09b6b86..4f44d03 100644 --- a/kinitro/backend/storage.py +++ b/kinitro/backend/storage.py @@ -36,7 +36,7 @@ class Storage: Async PostgreSQL storage for evaluation results. Usage: - storage = Storage("postgresql+asyncpg://user:pass@localhost/robo") + storage = Storage("postgresql+asyncpg://user:pass@localhost/kinitro") await storage.initialize() async with storage.session() as session: @@ -49,7 +49,7 @@ def __init__(self, database_url: str): Args: database_url: PostgreSQL connection URL - (e.g., postgresql+asyncpg://user:pass@localhost/robo) + (e.g., postgresql+asyncpg://user:pass@localhost/kinitro) """ self._database_url = database_url self._engine = create_async_engine( diff --git a/kinitro/environments/registry.py b/kinitro/environments/registry.py index ed2014b..569566b 100644 --- a/kinitro/environments/registry.py +++ b/kinitro/environments/registry.py @@ -16,7 +16,7 @@ # Note: Environment classes are imported lazily in factory functions to allow # containers with partial dependencies (e.g., genesis container without metaworld) -# Path to environments directory (robo-subnet/environments/) +# Path to repository-root environments/ directory (contains env configs, assets, etc.) _ENVIRONMENTS_DIR = Path(__file__).parent.parent.parent / "environments" # Type alias for environment factory functions diff --git a/kinitro/miner/template/Dockerfile b/kinitro/miner/template/Dockerfile index 87fb98e..1449bb9 100644 --- a/kinitro/miner/template/Dockerfile +++ b/kinitro/miner/template/Dockerfile @@ -1,10 +1,10 @@ -# Dockerfile for Robotics Subnet Miner Policy Server +# Dockerfile for Kinitro Miner Policy Server # # This creates a FastAPI server that exposes your policy for validator queries. # -# Build: docker build -t your-username/robo-policy:v1 . -# Test: docker run -p 8000:8000 your-username/robo-policy:v1 -# Push: docker push your-username/robo-policy:v1 +# Build: docker build -t your-username/kinitro-policy:v1 . +# Test: docker run -p 8000:8000 your-username/kinitro-policy:v1 +# Push: docker push your-username/kinitro-policy:v1 # # Deploy to Basilica: kinitro miner push --repo user/policy --revision abc123 @@ -30,6 +30,10 @@ COPY policy.py . # COPY model.pt . # COPY models/ ./models/ +# Create non-root user +RUN useradd -m -u 1000 miner && chown -R miner:miner /app +USER miner + # Expose port for FastAPI EXPOSE 8000