Platform Integration¶
This guide is for platform engineers setting up nskit for an organisation. It covers the three things you need to configure, and how to wrap them in a CLI.
The Three Things to Configure¶
1. Backend (Required)¶
The backend tells nskit where recipes live and how to execute them. It drives the engine:
- No backend (default): Recipes discovered from installed packages, executed locally. Not recommended for production — no versioning, no reproducible updates.
DockerLocalBackend: Discovers from locally pulled Docker images (via labels). No registry needed. Good for development with Docker.GitHubBackend: Discovers recipes from GitHub repos/releases, executes via Docker images from ghcr.io.DockerBackend: Discovers from a Docker registry, executes via Docker.LocalBackend: Discovers from a local directory. Useful for development.
Users can select a backend via the CLI without a wrapper script:
nskit --backend docker-local list # Local Docker images
nskit --backend docker-local init --recipe python_package
Or configure programmatically:
backend = GitHubBackend(
org='myorg', # GitHub org
repo_pattern='{recipe_name}', # Repo name pattern
entrypoint='mycompany.recipes', # Entry point group
)
The backend also controls what nskit list shows — with a GitHubBackend, it lists repos and their release tags. With a DockerBackend, it lists images and tags. With DockerLocalBackend, it lists locally pulled images. Without a backend, it falls back to locally installed entry points.
All Docker-based backends read the nskit.recipe.name label from images to determine the canonical recipe name. The label is read from the registry manifest at list time (no pull needed) and from the pulled image at init time.
2. Entry Point Group (Required for Local Mode)¶
The entry point group name determines which recipes are discoverable when running locally (without Docker). It must match between your recipe packages and the CLI configuration:
# In recipe package's pyproject.toml
[project.entry-points."mycompany.recipes"]
python_package = "mycompany.recipes:PackageRecipe"
# In CLI setup
app = create_cli(
recipe_entrypoint='mycompany.recipes', # Must match
backend=backend,
)
In Docker mode, the entry point group is baked into the container image — the host-side configuration doesn't affect it. But the backend still carries the entrypoint so that local fallback and get-required-fields work correctly.
3. VCS Provider (Optional)¶
If you want nskit to create repos and push code, configure a VCS provider. Providers are discovered via the nskit.vcs.providers entry point and configured via environment variables:
When a VCS provider is configured, nskit init prompts during field collection whether to create a remote repository. After generating the project, nskit always commits the initial files. If the user accepted, it creates the remote and pushes:
name: my-project
repo.owner: Joe Bloggs
repo.email: joe@example.com
Create repository in GitHub? [Y/n]
✓ Created python_package at ./my-project
✓ Committed initial files
✓ Created and pushed to https://github.com/myorg/my-project
This also works programmatically:
result = client.initialize_recipe(recipe='python_package', ...)
if result.success:
ok, msg = client.create_repository('my-project', description='My new project')
You can also use the VCS provider directly with namespace validation:
from nskit.vcs.repo import Repo, NamespaceValidationRepo
from nskit.vcs.namespace_validator import NamespaceValidator
# Validate the name
validator = NamespaceValidator(options=[...])
ok, msg = validator.validate_name('platform-auth-users')
# Create the repo
repo = Repo(name='platform-auth-users', local_dir=Path('./platform/auth/users'))
repo.create()
See Working with Namespaces for namespace configuration.
Wrapping in a CLI¶
The simplest platform setup: create a CLI package that pre-configures everything.
# mycompany_cli.py
from nskit.cli.app import create_cli
from nskit.client.backends import GitHubBackend
app = create_cli(
recipe_entrypoint='mycompany.recipes',
backend=GitHubBackend(org='myorg'),
)
def main():
app()
# pyproject.toml
[project.scripts]
myrecipes = "mycompany_cli:main"
[project.dependencies]
nskit = {extras = ["github"]}
Users install your package and get a fully configured CLI:
pip install mycompany-recipes-cli
myrecipes list # Queries GitHub for available recipes
myrecipes init --recipe python_package # Interactive prompts, Docker execution
myrecipes update # 3-way merge from versioned Docker images
The CLI is a Typer app — mount it as a subcommand if you have an existing tool:
Alternative: Environment Variables¶
If you don't want a custom CLI package, users can configure nskit via environment variables or a config file:
Or pass a config file:
Web API¶
The client layer is pure Python — wrap it with any web framework:
from fastapi import FastAPI, HTTPException
from nskit.client import RecipeClient, UpdateClient
from nskit.client.backends import GitHubBackend
app = FastAPI()
backend = GitHubBackend(org='myorg')
recipe_client = RecipeClient(backend)
@app.get("/recipes")
def list_recipes():
return [r.model_dump() for r in recipe_client.list_recipes()]
Custom Backend¶
Implement RecipeBackend for your infrastructure:
from pydantic import BaseModel
from nskit.client.backends.base import RecipeBackend
class S3BackendConfig(BaseModel):
type: str = "s3"
bucket: str
prefix: str = ""
class S3Backend(RecipeBackend):
def __init__(self, bucket: str, prefix: str = ""):
self._bucket = bucket
self._prefix = prefix
@property
def entrypoint(self) -> str:
return self._entrypoint
def list_recipes(self): ...
def get_recipe_versions(self, recipe_name: str): ...
def fetch_recipe(self, recipe_name: str, version: str, target_path): ...
def get_image_url(self, recipe: str, version: str) -> str: ...
Register it as an entry point so nskit discovers it automatically:
# In your package's pyproject.toml
[project.entry-points."nskit.backends"]
s3 = "my_package.backends:S3BackendConfig, S3Backend"
Once installed, the backend is available in YAML config files: