Skip to main content

tare install

Install the complete TARS dataplane in a single command.


Synopsis

tare install <identity-file> [flags]

Description

tare install performs the full operator workflow:

  1. Load and validate the identity (service-account) file
  2. Optionally sync pinned images and the serve-helm OCI chart to a private registry (--image-sync)
  3. Optionally create a Kubernetes dockerconfigjson pull secret (--image-pull-secret-stdin)
  4. Generate Helm values
  5. helm upgrade --install the TARS dataplane chart
  6. Optionally wait for pods to be ready (--wait)

helm and kubectl are downloaded automatically on first run into ~/.tare/tools/ and reused on subsequent runs.

By default (--ha=true, since ADR 041) the data-plane Envoy proxy + sidecar'd ExtProc deploy with HA-safe values: HPA minReplicas: 2, PDB minAvailable: 1, and EnvoyProxy.spec.shutdown.drainTimeout: 300s for graceful drain of in-flight LLM streams. If the target cluster has no metrics.k8s.io APIService (metrics-server absent), tare install prints a non-fatal warning: the install-time floor of 2 still applies but the HPA cannot autoscale beyond it. Pass --ha=false for single-replica installs (lab / CI).

--serve-url is the customer-facing gateway URL for the data plane being installed. It is optional: if omitted, the helm chart leaves tars-config[proxy-url] unrendered and the CLI does not register a URL with the management plane. Admins can configure the URL later via the management plane (which fires a PROXY_URL configuration event that updates the running data plane's tars-config).

When supplied, tare install calls the management plane's ConfiguratorService.SetDataplaneURL before running helm upgrade --install so the URL is persisted in system_settings.dataplane-url:<workspaceId> for the data plane's workspace (looked up from the identity-file SA's workspaceId). The chart then seeds tars-config[proxy-url] from global.serveUrl at install. No TriggerUpdate is fired: the value will be re-pushed via the standard event flow if an admin later edits it.

Usage

Basic install

tare install identity.json --serve-url https://proxy.acme.com

--serve-url sets the data plane's customer-facing gateway URL. It flows into global.serveUrl in helm values; the controller-helm chart seeds tars-config[proxy-url] from that value at install. (The separate controller.config.proxyUrl value no longer exists: the controller and worker pods read the URL from tars-config[proxy-url] at runtime, not from a PROXY_URL env var.)

Deploys using the embedded Helm chart with defaults:

SettingDefault
Release nametars
System namespacetars-system
Dataplane namespacetars-dataplane
Customerinferred from identity file

Print the generated Helm values to stdout without touching the cluster:

tare install identity.json --serve-url https://proxy.acme.com --print-helm-values

Useful for inspecting values or piping into GitOps tooling. --dry-run is accepted as a hidden alias.

Image sync

Sync all pinned images and the serve-helm OCI chart from the source registry to a private registry before installing:

tare install identity.json --serve-url https://proxy.acme.com \
--image-sync myregistry.example.com
  • The SA JWT token is used to authenticate to the source registry.
  • After the sync, --image-registry (the Helm global.imageRegistry) points at the sync destination so the cluster pulls from where the images now live.
  • Source registry priority: --image-registry (when --image-sync is also set) → REGISTRY_URL env → registry.tetrate.ai (default).
  • The serve-helm chart is mirrored as an OCI artifact alongside the images (e.g. myregistry.example.com/serve-helm:0.1.0-rc.4_<sha>). The tag encodes a SemVer build identifier: the + build metadata is normalized to _ because OCI tags forbid +. The counter lives in the prerelease segment (rc.4, alpha.347) so versions are orderable; tare versions list --channel rc finds the newest release candidate (ADR 048 / tetrateio/fraser#4395). tare install itself still uses the chart embedded in the CLI binary; the mirrored chart exists so downstream helm upgrade oci://myregistry.example.com/serve-helm workflows succeed without reaching back to registry.tetrate.ai.
  • For release-tagged tare builds the chart also gets an extra tag equal to the running CLI version (e.g. myregistry.example.com/serve-helm:v1.2.3) so a downstream helm install --version <tare-version> resolves to the chart that shipped with that CLI. Dev / dirty builds skip the extra tag to keep the mirror free of ambiguous tags.

Sync only (no deploy), --serve-url is not required:

tare install identity.json \
--image-sync myregistry.example.com --sync-only

Mirror-to-mirror sync: pull from an existing private registry and push to a new one in a single invocation. When paired with --image-sync, --image-registry is interpreted as the source registry to pull from; without --image-sync it keeps its original meaning of "set the Helm global.imageRegistry".

tare install identity.json \
--image-registry acme.registry.example.com \
--image-sync acme1.registry.example.com \
--sync-only

Print the image list without copying, --serve-url is not required:

# Just the source references
tare install identity.json --print-images

# Source → destination pairs (requires --image-sync)
tare install identity.json \
--image-sync myregistry.example.com --print-images

Apply CRDs only

Apply the chart's CRDs to the cluster and exit before namespaces, pull secret, and Helm install. --serve-url is not required. Useful for three operational patterns:

  • Cluster-admin handoff: a privileged operator pre-applies CRDs (which are cluster-scoped), then a less-privileged operator runs the rest of tare install against a namespace they own.
  • Race-free upgrades: apply CRDs first so subsequent helm upgrade runs render against the new schema with no chicken-and-egg ordering.
  • Raw helm install workflows: operators who drive Helm directly (GitOps, Argo CD, custom CI) hand the CRD lifecycle to tare and let Helm own only the workload resources.
tare install identity.json --crds-only

After the CRDs land, re-run without --crds-only (and with --serve-url) to complete the install. --crds-only cannot be combined with --sync-only, --image-sync, --image-pull-secret-stdin, or any --print-* flag.

Followed by a raw helm install

If you intend to skip tare install for the workload step and run Helm directly, you must tell Helm not to render or adopt the CRDs that tare already applied via server-side apply. Two flags are needed:

# Step 1: apply CRDs with tare (kubectl server-side apply; no Helm ownership labels)
tare install identity.json --crds-only

# Step 2: install the workload with helm, skipping CRDs in both places they live
helm install tars oci://<registry>/serve-helm \
--version "<version>" \
-f values.yaml -n tars-system \
--skip-crds \
--set ai-gateway-crds.enabled=false
  • --skip-crds tells Helm to skip CRDs in the chart's crds/ directory.
  • --set ai-gateway-crds.enabled=false disables the ai-gateway-crds subchart, which renders CRDs as templates (not as crds/ files) and would otherwise fail Helm's ownership-adoption check against the kubectl-applied CRDs.

Without both flags, Helm errors with:

CustomResourceDefinition "aigatewayroutes.aigateway.envoyproxy.io" ...
exists and cannot be imported into the current release: invalid ownership
metadata; label validation error: missing key "app.kubernetes.io/managed-by"

This split (tare owns CRDs, Helm owns the workload) is the recommended pattern for GitOps and raw-Helm consumers. CRDs survive helm uninstall tars because Helm does not manage them, so customer data (Gateways, AIGatewayRoutes, MCPRoutes, …) is preserved.

Pull secret

Create a Kubernetes dockerconfigjson secret from credentials read on stdin:

echo "user:password" | tare install identity.json \
--serve-url https://proxy.acme.com \
--image-sync myregistry.example.com \
--image-pull-secret-stdin
  • The secret is created (or updated) in both tars-system and tars-dataplane namespaces.
  • Re-runs are idempotent (kubectl apply under the hood).
  • Default secret name: tars-image-pull-secret. Override with --image-pull-secret-name.

To reference a pre-existing secret without creating one:

tare install identity.json --serve-url https://proxy.acme.com \
--image-pull-secret-name my-existing-secret

Full workflow

echo "user:password" | tare install identity.json \
--serve-url https://proxy.acme.com \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--wait

With guardrails

Include llm-guard-api and envoy-dm images and enable guardrails configuration:

echo "user:password" | tare install identity.json \
--serve-url https://proxy.acme.com \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--enable-guardrails \
--wait

The guardrails container (llm-guard-api) requires a node with sufficient memory and CPU. Use --guardrails-config to pin it to the right node pool:

# guardrails.yaml
# nodeSelector:
# node-type: gpu-large
#
# tolerations:
# - key: large-node
# operator: Exists
# effect: NoSchedule

echo "user:password" | tare install identity.json \
--serve-url https://proxy.acme.com \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--enable-guardrails \
--guardrails-config guardrails.yaml \
--wait

The config file uses natural Kubernetes YAML, no escaped JSON strings:

# guardrails.yaml
nodeSelector:
node-type: gpu-large

tolerations:
- key: large-node
operator: Exists
effect: NoSchedule

# Optional: full affinity rules
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: cloud.google.com/gke-nodepool
operator: In
values: [llm-guard-pool]

Pass --guardrails-config - to read the config from stdin instead of a file (follows the kubectl/helm convention):

cat <<'EOF' | tare install identity.json \
--serve-url https://proxy.acme.com \
--enable-guardrails \
--guardrails-config - \
--wait
nodeSelector:
node-type: gpu-large
tolerations:
- key: large-node
operator: Exists
effect: NoSchedule
EOF

Note: --guardrails-config - and --image-pull-secret-stdin both read stdin; use a file when combining both.

Print namespace and pull-secret manifests without touching the cluster. --serve-url is not required:

echo "user:password" | tare install identity.json \
--image-sync myregistry.example.com \
--image-pull-secret-stdin \
--print-resources

Pipe directly into kubectl apply or commit to a GitOps repo.

Flags

Main

FlagDefaultDescription
--serve-url <URL>optionalData plane gateway URL; sets global.serveUrl (which seeds tars-config[proxy-url]) and registers the URL with the management plane. Omit to defer URL configuration to the dashboard.
--hatrueHA-safe defaults for the data-plane Envoy proxy: HPA minReplicas: 2, PDB minAvailable: 1. Pass --ha=false for single-replica lab/CI installs.
--drain-timeout-seconds300EnvoyProxy.spec.shutdown.drainTimeout (seconds). Maximum time Envoy waits for in-flight LLM streams to finish before SIGKILL. The Envoy Gateway controller propagates this into the data-plane Pod's terminationGracePeriodSeconds.
--enable-guardrailsfalseInclude guardrails images and configuration
--guardrails-config <file|->noneYAML file with Guardrails pod scheduling constraints (nodeSelector, affinity, tolerations), or - to read from stdin; requires --enable-guardrails
--waitfalseWait for pods ready after deploy

Registry

FlagDefaultDescription
--image-sync <REG>noneSync images and the serve-helm OCI chart to <REG> before install; sets --image-registry to <REG> after the sync (unless --image-registry was used to override the source)
--image-pull-secret-stdinfalseRead user:password from stdin and create a pull secret
--image-pull-secret-name <NAME>tars-image-pull-secretName of the pull secret to create or reference
--sync-onlyfalseSync images then exit without deploying (requires --image-sync)

Inspect

FlagDefaultDescription
--print-helm-valuesfalsePrint generated Helm values to stdout without deploying
--print-resourcesfalsePrint Kubernetes manifests (namespaces, pull secret) to stdout without applying
--print-imagesfalsePrint image list and exit (add --image-sync for src→dst pairs)
--crds-onlyfalseApply CRDs to the cluster and exit; skip namespaces, pull secret, Helm install, and wait. Incompatible with --sync-only, --image-sync, --image-pull-secret-stdin, and any --print-* flag.

Advanced / hidden

These flags are hidden from --help but accepted for advanced use.

FlagDefaultDescription
--dry-runfalseAlias for --print-helm-values
--release-nametarsHelm release name
--system-namespacetars-systemKubernetes system namespace
--dataplane-namespacetars-dataplaneKubernetes dataplane namespace
--chart-pathembeddedPath or OCI/HTTP reference to Helm chart
--chart-versionnoneChart version (required for remote/OCI charts)
--image-registrynoneContainer image registry. Without --image-sync: sets Helm global.imageRegistry. With --image-sync: source registry to pull from (push destination is --image-sync). Falls back to REGISTRY_URL env, then registry.tetrate.ai
--image-tagnoneImage tag override
--helm-valuesnoneAdditional Helm values file to merge
--deployment-modeenterpriseenterprise or saas
--environmentnoneEnvironment label
--customerfrom identityCustomer label override
--redis-typeclustercluster or hosted
--rate-limitingtrueEnable rate limiting
--timeout5mTimeout for --wait
--guardrails-node-selectornoneNode selector for Guardrails pods as key=value pairs (repeatable); prefer --guardrails-config
--guardrails-affinitynoneAffinity rules for Guardrails pods as a JSON object; prefer --guardrails-config
--guardrails-tolerationsnoneTolerations for Guardrails pods as a JSON array; prefer --guardrails-config

Examples

Production deployment with private registry

# 1. Sync images (run once per release upgrade)
tare install identity.json \
--image-sync gcr.io/acme/tars \
--sync-only

# 2. Install (or upgrade)
echo "user:password" | tare install identity.json \
--serve-url https://proxy.acme.com \
--image-sync gcr.io/acme/tars \
--image-pull-secret-stdin \
--release-name acme-serve \
--wait

GitOps: generate values only

tare install identity.json \
--serve-url https://proxy.acme.com \
--image-registry gcr.io/acme/tars \
--image-pull-secret-name acme-pull-secret \
--guardrails-config guardrails.yaml \
--enable-guardrails \
--print-helm-values > helm/tars-values.yaml

Commit helm/tars-values.yaml and apply separately:

helm upgrade --install tars charts/valet/serve \
--namespace tars-system --create-namespace \
-f helm/tars-values.yaml

Preview images before syncing

tare install identity.json \
--image-sync myregistry.example.com \
--enable-guardrails \
--print-images

Output:

registry.tetrate.ai/gateway:v1.6.5 → myregistry.example.com/gateway:v1.6.5
registry.tetrate.ai/ratelimit:v1.6.5 → myregistry.example.com/ratelimit:v1.6.5
...
registry.tetrate.ai/llm-guard-api:v0.3.1 → myregistry.example.com/llm-guard-api:v0.3.1
registry.tetrate.ai/envoy-dm:v0.3.1 → myregistry.example.com/envoy-dm:v0.3.1
registry.tetrate.ai/serve-helm:0.1.0-rc.4_abcd123 → myregistry.example.com/serve-helm:0.1.0-rc.4_abcd123

Output

All progress is written to stderr; only --print-helm-values, --print-images, and --print-resources write to stdout, making them safe to redirect:

Syncing images registry.tetrate.ai → myregistry.example.com
Copying registry.tetrate.ai/gateway:v1.6.5 → myregistry.example.com/gateway:v1.6.5
2009/11/10 23:00:00 existing blob: sha256:abc123...
...
Image sync complete.

Creating pull secret "tars-image-pull-secret" in namespace tars-system...
pull secret "tars-image-pull-secret" ok
Creating pull secret "tars-image-pull-secret" in namespace tars-dataplane...
pull secret "tars-image-pull-secret" ok

Installing TARS dataplane...
release: tars
system namespace: tars-system
dataplane namespace: tars-dataplane

Creating Kubernetes namespaces...
namespace tars-system: ok
namespace tars-dataplane: ok

Deploying with Helm...
Helm deployment complete

Installation complete.

Next steps:
kubectl get pods -n tars-system
kubectl logs -n tars-system -l app=tars

Verification

kubectl get pods -n tars-system
kubectl get pods -n tars-dataplane
kubectl logs -n tars-system -l app=tars

Management-plane event reporting

After the install completes, the CLI writes a ConfigMap labelled tars.io/component=install-event to tars-system. The tare-doctor CronJob (deployed with this chart) forwards that ConfigMap to the management plane on its next tick (default: every 5 minutes) so the workspace's deployment history reflects the new install. On a successful forward the ConfigMap is deleted; on a 5xx / network failure it is retained for the next tick; on a 4xx it is dropped (the same payload would re-reject).

If you install the chart without going through the tare CLI (raw helm install, ArgoCD / Flux, customer GitOps), the same dashboard row appears via the tare-doctor reconciler which observes the helm release secret and emits an event with source: tare-doctor-poll. Operator attribution is empty on the non-CLI path (rendered as a dash in the timeline).

Rollback and uninstall

# View history
helm history tars -n tars-system

# Rollback
helm rollback tars -n tars-system

# Uninstall
helm uninstall tars -n tars-system
kubectl delete namespace tars-system tars-dataplane