# Quickstart

> Run your first sandboxed agent end-to-end in about a minute.

export const quickstartSteps = [
  { ix: "STEP 01", title: "Install clrk", href: "#install-the-cli" },
  { ix: "STEP 02", title: "Provide an API key", href: "#provide-an-api-key" },
  { ix: "STEP 03", title: "Boot the local stack", href: "#boot-the-local-stack" },
  { ix: "STEP 04", title: "Watch it run", href: "#watch-it-run" },
  { ix: "STEP 05", title: "Verify the agent never saw the key", href: "#verify-the-agent-never-saw-the-key" },
];

<Steps steps={quickstartSteps} />

---

This walks through CLRK end-to-end against a local k3s container - install the CLI, boot
a controller-manager + worker, dispatch an Anthropic-calling agent, and confirm the proxy
injected the API key on the way out.

<Callout label="Prereqs">
Docker running on the host, an Anthropic API key in your shell, and macOS or Linux. You
do **not** need an existing Kubernetes cluster - `clrk dev` ships its own k3s.
</Callout>

## Install the CLI

The CLI is a single static Go binary. Build it from a clone of the repo - you'll reuse
that same clone for the example manifests below.

```bash title="terminal"
git clone https://github.com/apoxy-dev/clrk.git
cd clrk
go build -o /usr/local/bin/clrk ./cmd/clrk
```

Confirm it's on your `$PATH`:

```bash title="terminal"
clrk --help
```

## Provide an API key

The example agent calls `api.anthropic.com`. Put your key into the shell - `clrk dev`
will read it from the environment and never write it to the manifests.

```bash title="terminal"
export ANTHROPIC_API_KEY=sk-ant-...
```

## Boot the local stack

From the clone you built in step 1, start the dev stack with the example manifests:

```bash title="terminal"
clrk dev \
  --apply _examples/echo-bot/manifests \
  --secret anthropic-credentials=ANTHROPIC_API_KEY:api-key
```

What this does, in order:

1. Runs a host-readiness check (Docker daemon, `/dev/net/tun`, IPv6, image pullable).
2. Brings up a k3d cluster (docker container `k3d-clrk-dev-server-0`, plus a
   `clrk-registry` container when a local registry is enabled), then applies the
   controller-manager as a Deployment in the `clrk` namespace and a default WorkerPool
   whose reconciler creates the `default-workers` Deployment in the `default` namespace.
   The controller-manager and workers run as in-cluster pods, not sibling docker
   containers.
3. Writes a host-side kubeconfig to `~/.clrk/kubeconfig.host` (apiserver published on
   `https://localhost:<dynamically-allocated-port>` - read the server URL from the
   kubeconfig).
4. Materializes the `anthropic-credentials` Secret from your shell env. The `--secret`
   flag takes `NAME=ENVVAR[:KEY]`; we set `:api-key` explicitly so the
   `CredentialInjectionPolicy` can name it.
5. Server-side-applies the echo-bot manifests once the apiserver is reachable.

Roughly thirty seconds in, the TUI settles when `k3d-clrk-dev-server-0`,
`controller-manager`, and `worker-0` are all running.

## Watch it run

Point `kubectl` (or `clrk`) at the local apiserver and confirm the resources reconciled:

```bash title="terminal"
export KUBECONFIG=~/.clrk/kubeconfig.host

# The DaemonAgent the example installed.
clrk agents get echo-bot

# The egress proxy in front of it.
kubectl get egressgateway echo-bot

# The Secret clrk dev materialized from your shell env.
kubectl get secret anthropic-credentials
```

Once the first cycle fires (about five seconds in), the TUI's `otel-logs` pane carries
one structured line per call:

```
15:04:05.123 POST api.anthropic.com/v1/messages 200 540ms req=312B resp=1024B provider=anthropic model=claude-haiku-4-5 input_tokens=12 output_tokens=24 route=default/anthropic trace=a1b2c3d4
```

The matching span lands in `otel-traces`. These records are emitted by the
EgressGateway's ext_proc - the same surface that emits to your real OTLP collector in
production.

## Verify the agent never saw the key

The example installs a `CredentialInjectionPolicy` that attaches the
`anthropic-credentials` Secret to the AIProviderRoute. The agent makes its `POST` with
*no* `x-api-key` header; the proxy inserts the real value as the request leaves the
sandbox.

Confirm it through the captured telemetry - the agent runs in an isolated gVisor sandbox,
so its process environment is not visible from the worker pod's namespace:

```bash title="terminal"
# Anthropic accepted the call, which only happens if the proxy injected the key.
clrk agents logs echo-bot --tail 20
# Expect a 200 response body with the model's reply.
```

A 200 here proves the proxy supplied the credential: the agent sent its `POST` with no
`x-api-key` header, and the request still succeeded. Open the matching span in
`otel-traces` and the captured `x-api-key` header attribute
(`http.request.header.x-api-key`) shows as `[redacted]` - telemetry never carries the
real secret either.

## Tear down

`Ctrl-C` the dev TUI to stop the local cluster. State persists under `~/.clrk/`; the
next `clrk dev` reattaches to it. For a clean slate:

```bash title="terminal"
rm -rf ~/.clrk
```

## Next steps

- [Local development](/docs/clrk/getting-started/local.md) - every flag on `clrk dev` and how to
  iterate on the controller and worker binaries.
- [Core concepts](/docs/clrk/getting-started/core-concepts.md) - the CRDs you just applied,
  what each one controls, and how they fit together.
- [Run your first agent](/docs/clrk/guides/run-your-first-agent.md) - the same example, explored
  in detail, with the full architecture diagram.
- [Package a custom agent](/docs/clrk/guides/package-a-custom-agent.md) - turn a script into a
  `TaskAgent` and trigger it over HTTP or cron.
