Secure Scripting Runtime for Systems, Cloud, and Agent Automation

Starkite combines the simplicity of the Starlark scripting language with a set of feature-rich modules backed by the Go standard libary. Use it to replace brittle single-threaded operation scripts with high-performance automation for system, infrastructure, and agentic workloads.

# Execute remote commands across fleet hosts via bastion

def main():
    client = ssh.config(
        hosts   = ["node-1", "node-2", "node-3"],
        auth    = {"user": "ops", "key": "~/.ssh/id_ed25519"},
        jump    = {"host": "bastion.corp.net", "user": "admin"},
        timeout = "10s",
    )
    for res in client.exec("uptime"):
        print(res.host, "->", res.stdout.strip())
# Scan host keys and distribute credentials safely

def main():
    # 1. Discover and save host keys to known_hosts
    ssh.keyscan(hosts=["node-1", "node-2", "node-3"], save=True)

    # 2. Check and copy public key (skips already-authorized nodes)
    client = ssh.config(hosts=["node-1", "node-2", "node-3"], auth={"user": "deploy"})
    for r in client.copy_id("~/.ssh/id_ed25519.pub", key_check=True):
        print(r.host, "->", r.stdout.strip())
# Easily create HTTP servers and API endpoints

def handle_webhook(req):
    event = json.decode(req.body)
    return {"status": 200, "body": event.get("type", "ping")}

def main():
    srv = http.server()
    srv.handle("POST /webhook", handle_webhook)
    srv.serve(port=8080)
# Create clients to handle remote HTTP services/APIs

def main():
    resp = http.url("https://httpbin.org/json").get(
        headers = {"Accept": "application/json"},
        timeout = "5s",
    )
    data = json.decode(resp.body)
    print("Status Code:", resp.status_code)
    print("Response Data:", data["slideshow"]["title"])
# Easily query Kubernetes cluster resources

def main():
    pods = k8s.list("pods", namespace="staging")
    for pod in pods:
        print(pod.metadata.name, pod.status.phase)
# Deploy and manage Kubernetes cluster workloads

def main():
    res = k8s.deploy(name="web", image="nginx:1.27", replicas=3)
    k8s.scale("deployment", name="web", replicas=5)
    print("Deployment scaled:", res.deployment)
# Easily create Kubernetes controllers and admission webhooks

def reconcile(event, pod):
    if pod.status.phase in ["Failed", "CrashLoopBackOff"]:
        http.url("https://alerts.internal/k8s").post(
            {"pod": pod.metadata.name, "phase": pod.status.phase},
        )

def main():
    k8s.control("pods", reconcile=reconcile, namespace="default")
# Expose functions as MCP servers for your agentic tools

def get_pod_health():
    """Return status summary of running pods across staging."""
    pods = k8s.list("pods", namespace="staging")
    return [
        {"pod": p.metadata.name, "phase": p.status.phase}
        for p in pods
    ]

def main():
    mcp.serve(tools=[get_pod_health])
// Use Starkite MCP tools with Claude, Cursor, or any agent harness

{
  "mcpServers": {
    "k8s-diagnostics": {
      "command": "kite",
      "args": ["run", "./tools/k8s_mcp.star", "--permissions=k8s-read"]
    }
  }
}
// Use the Starkite runtime in your own harness code 

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const client = new Client({ name: "sre-agent", version: "1.0.0" });
await client.connect(new StdioClientTransport({
  command: "kite",
  args: ["run", "./tools/k8s_mcp.star", "--permissions=k8s-read"],
}));

const health = await client.callTool({ name: "get_pod_health" });
# Define granular permission and execution sanbox profiles

permissions:
  safe-job:
    allow:
      - "fs.read($CWD/data/*)"
      - "fs.write($CWD/data/*)"
    deny:
      - os.exec
      - http.client
# Code makes function calls and access to restricted resources

def main():
    data = path("data/input.json").read_text()
    records = json.decode(data)
    path("data/result.json").write_text(
        json.encode({"count": len(records)}),
    )
    print("Job completed safely within policy bounds.")
# Run scripts with pre-defined or custom permissions
$ kite run ./safe_job.star --permissions=safe-job

# Or, place sandbox boundary around system calls
$ kite run ./safe_job.star --sandbox-opaque --permissions=safe-job

                  
Get Started
$ curl -fsSL https://install.starkite.run/install.sh | sh

Quick Install

Install the kite binary on your local machine using your preferred setup:

brew install project-starkite/tap/kite
curl -fsSL https://install.starkite.run/install.sh | sh
irm https://install.starkite.run/install.ps1 | iex

Running Starkite Scripts

1. Run via the kite CLI

Starkite scripts run through the kite CLI. The kite run command (or its implicit shorthand) accepts any .star file and forwards --var arguments to the script — suitable for ad-hoc invocation, CI pipelines, and pipeline composition with other tools.

$ kite run ./deploy.star

# shorthand — `run` is implicit
$ kite ./deploy.star --var image_tag=v1.0.0

# pipe results to other tools
$ kite ./manifest.star | kubectl apply -f -

2. Execute via shebang

A starkite script with a #!/usr/bin/env kite shebang and the executable bit set runs like any other shell program. The kite prefix is not required at the call site.

$ cat deploy.star
#!/usr/bin/env kite
print("rolling out v1.0.0")

$ chmod +x deploy.star
$ ./deploy.star
rolling out v1.0.0

One Binary. Complete Automation.

Standard Library

Twenty-eight built-in modules cover everyday automation: files, processes, databases (SQL), HTTP client and server, SSH, JSON, YAML, CSV, gzip/zip, hashing, regex, templating, time, UUIDs, structured logging, retries, and concurrency. Every module is built directly into the runtime, eliminating the need for a Python venv, Node install, or external package manager.

Browse modules
#!/usr/bin/env kite

# Spin up an HTTP API server in a few lines
def health(req):
    return {"status": 200, "body": {"ok": True}}

def echo(req):
    return {"status": 200, "body": req.body}

http.serve({
    "GET /health": health,
    "POST /echo":  echo,
}, port=8080)

Infrastructure

Starkite's infrastructure capabilities center on native Kubernetes operations, integrating the API directly into the runtime across three tiers: k8s.list/apply/watch for direct resource control, k8s.deploy/expose/rollout for kubectl-equivalent verbs, and typed constructors for programmatic manifest composition. Scripts run as long-lived controllers via k8s.control or as admission webhooks via k8s.webhook. The kite kube subcommand generates the corresponding deployment manifests for either.

Explore Infrastructure
#!/usr/bin/env kite

# Reject Deployments asking for too many replicas
def validate(obj):
    if obj.spec.replicas > 10:
        return {"allowed": False,
                "message": "max 10 replicas"}
    return {"allowed": True}

k8s.webhook("/validate",
    validate = validate,
    port     = 9443,
    tls_cert = "/certs/tls.crt",
    tls_key  = "/certs/tls.key",
)

Agentic AI

Supercharge your AI-native automation. Starkite integrates with LLMs in three key ways:

  • MCP Tool Serving: Expose Starlark functions as instant tools for external agents (Claude, Cursor, IDEs).
  • Agent Loops: Orchestrate autonomous scripts with native, multi-provider LLM support.
  • Code Generation: Load starkite-skills to let LLMs write clean Starlark code.

Because Starlark is dependency-free and sandboxed, AI agents can generate and run scripts safely without complex setup.

Build agents
#!/usr/bin/env kite

# Expose cluster operations as MCP tools
def list_pods(namespace):
    """List pods in a namespace."""
    pods = k8s.list("pods", namespace=namespace)
    return [{"name":  p["metadata"]["name"],
             "phase": p["status"]["phase"]} for p in pods]

def restart_deployment(name, namespace):
    """Restart a deployment."""
    k8s.rollout("deployment", name,
        action="restart", namespace=namespace)
    return "restarted %s/%s" % (namespace, name)

mcp.serve(
    name  = "k8s-ops",
    tools = [list_pods, restart_deployment],
)

Security

Two layers of defense apply to every script. Permission profiles gate module calls at the API layer under a deny-by-default policy (os.exec denied, fs.read allowed). Pluggable OS-level sandboxing isolates the script across native kernel primitives (Landlock/Seatbelt) and container runtimes. Named profiles are defined in ~/.starkite/config.yaml and apply to any invocation via a single command-line flag.

Read the security model
# Named profile from ~/.starkite/config.yaml
$ kite run ./job.star --permissions=ci --sandbox-profile=ci

# Built-in ladder profile — no config file needed
$ kite run ./job.star --allow-fs

# Deny-all permissions + strict sandbox for untrusted code
$ kite run ./untrusted.star --permissions=deny-all --sandbox-opaque
Error: permission denied: os.exec is not allowed

Built-in Script Testing

Write tests

Test files end in _test.star and define test_* functions. Each test verifies conditions through the built-in assert(cond, msg), with optional setup() and teardown() hooks running before and after every test. The skip() built-in marks the current test as skipped.

def test_addition():
    assert(2 + 2 == 4, "basic math should work")

def test_string_contains():
    assert("kite" in "starkite", "starkite should contain kite")

def test_list_length():
    items = ["a", "b", "c"]
    assert(len(items) == 3, "list should have 3 items")

Run tests

The kite test command discovers every _test.star file under the given path, runs each test_* function, and reports pass/fail counts. The --verbose flag prints each test name as it runs. The exit code is non-zero on any failure.

$ kite test ./tests/
Found 1 test file(s)
============================================================
Tests: 3 passed, 0 failed, 3 total
Time:  9ms
============================================================

Ready to Start? Learn more.