Getting started

Install the operator from the published Helm chart, instrument a running app with the basquin CLI, fuzz it, replay the interesting inputs under load, and read the result — live images, nothing to build.

Kubernetes + Helm published images — nothing to build

1  What you need

# a) Download a release binary — linux/macOS/Windows × amd64/arm64.
#    Swap the suffix for your platform: basquin-{linux,darwin,windows}-{amd64,arm64}(.exe)
curl -sSL -o basquin https://github.com/ianp94/basquin/releases/latest/download/basquin-linux-amd64
chmod +x basquin && sudo mv basquin /usr/local/bin/basquin
#    (each release also ships checksums.txt to verify against)

# b) go install — pure Go, nothing to package.
go install github.com/ianp94/basquin/operator/cmd/basquin@latest

Everything below pulls published ghcr.io/ianp94/basquin-* images — there's no git clone, no Gradle, no local jar in this path.

2  Install the operator

The chart is published to a GitHub Pages Helm repo, and its default images to GitHub Container Registry — a plain helm install pulls real images at the chart's appVersion, with nothing to build:

helm repo add basquin https://ianp94.github.io/basquin/charts
helm repo update
helm install basquin basquin/basquin-operator \
  --namespace basquin-system --create-namespace --set fullnameOverride=basquin
namespaced by design

The operator watches and mutates only its own namespace — it refuses to start cluster-wide. The standing privilege is a Role/RoleBinding, never a ClusterRole. To instrument another namespace, install another instance. See the Operator guide for the two-CRD model this installs.

3  Instrument an app

Point basquin instrument at a Deployment already running in the cluster. It applies a BasquinTarget, which patches the pod template to load the Basquin agents via an initContainer + shared volume — no rebuild, no image change, and fully reversible (deleting the target restores the Deployment exactly):

# 1. Instrument a running app — no rebuild, no image changes.
basquin instrument -n basquin-system --deployment jpetstore \
  --jvm-opts-var CATALINA_OPTS --coverage-includes 'org.mybatis.jpetstore.*' --coverage-service --wait

--wait blocks until the target's status.phase reaches Injected. basquin instrument -h lists every flag; the full field-by-field reference is in the Operator guide and OPERATOR-USAGE.md.

4  Run a campaign

A BasquinCampaign drives an already-instrumented target. It runs in one of two modes — explore first, to discover the interesting inputs, then load, to hammer them:

# 2. Fuzz it: coverage-guided exploration, emitting a corpus of interesting inputs.
#    (The campaign name defaults to <target>-campaign, i.e. jpetstore-campaign.)
basquin run -n basquin-system --target jpetstore \
  --base-url http://jpetstore-app.basquin-system.svc.cluster.local:8080 \
  --iterations 500 --grammar examples/grammar/jpetstore.grammar \
  --corpus examples/corpus/jpetstore --watch

# 3. Replay what it found, under load: --corpus-from reuses the corpus step 2 emitted.
basquin run -n basquin-system --name jpetstore-load --mode load --target jpetstore \
  --base-url http://jpetstore-app.basquin-system.svc.cluster.local:8080 \
  --duration 30m --concurrency 50 --corpus-from jpetstore-campaign --watch

explore (default) mutates requests, keeps the ones that reach new code in the app's own JaCoCo coverage, and on completion emits its cost-ranked replay corpus as the <campaign>-corpus-out ConfigMap — which --corpus-from <campaign> feeds straight into a load run (use --corpus <dir> instead to replay local files). load replays a saved corpus verbatim at a fixed concurrency for a duration — no mutation, no coverage — watching the same latency/heap/thread invariants under sustained traffic. --watch tails the campaign to completion, printing coverage/findings/dashboard as they land.

5  Read results

Read status straight from the cluster, or open the per-campaign dashboard:

# 4. Read results / open the per-campaign dashboard.
basquin status -n basquin-system
basquin dashboard -n basquin-system --campaign jpetstore-campaign   # then open :7070

basquin status renders targets and campaigns together (add --watch to follow); basquin dashboard finds the dashboard pod, port-forwards it, and prints a tokenized http://localhost:7070 URL — reads are token-gated, so this is the way to open it rather than a bare kubectl port-forward. The dashboard outlives the run: it's garbage-collected with the campaign, not the driver Job, so results stay viewable after the driver completes.

Reading the status panel

Whether you're watching basquin run --watch, the dashboard, or a standalone runner's terminal screen, the same rows show up. What they mean:

RowWhat it tells you
execs / execs/secIterations completed and throughput.
crashesGenuine faults only — unhandled exceptions and 5xx. Expected input rejections are counted separately as rejected, not here.
invariant findsIterations that tripped a latency / heap / thread invariant. On an instrumented target these are harvested server-side.
rejectedInputs a target's CrashClassifier declared as expected validation failures — not bugs.
corpus / last findCorpus size and time since the last interesting input, during exploration.
coverage %Real "% of code explored" in the app under test, read from its JaCoCo agent — lights up only when a coverage source reports.
next

Want the whole picture — the iteration boundary, invariants, the JVMTI agent, and coverage-guided exploration? Read How it works. For the full BasquinTarget/BasquinCampaign field reference, load mode, and the dashboard's security model, see the Operator guide and OPERATOR-USAGE.md.

where next

Running without Kubernetes, or hacking on Basquin itself? → Developing covers building from source and driving the standalone runner against localhost. Full operator reference → Operator / OPERATOR-USAGE.md.