Getting started
Gretl gives every local service a name. Register a port once, then
start, stop, and open it by name —
from the desktop app, the CLI, or any SDK.
Installation
Choose the surface that fits your workflow. All options share the same config file at
~/.gretl/config.json.
Desktop app
The desktop app is the recommended way to get started. Download for your platform:
# macOS Apple Silicon https://gretl.dev → click Download → macOS (M1/M2/M3) # macOS Intel https://gretl.dev → click Download → macOS (Intel) # Windows https://gretl.dev → click Download → Windows # Linux curl -fsSL https://gretl.dev/install.sh | sh
CLI only
If you don't need the GUI, install the CLI and start the daemon manually:
npm install -g @gretl/cli # Start the background daemon (binds to 127.0.0.1:27892) gr daemon start # Check it's running gr daemon status # ✓ daemon running pid 12345 127.0.0.1:27892
127.0.0.1:27892 only. Your ports and config
never leave your machine unless you connect a team account.Daemon
Both the desktop app and the CLI expose a local HTTP API on 127.0.0.1:27892. Only one
should run at a time — if the desktop app is open, there's no need to run gr daemon start.
| Command | Description |
|---|---|
| gr daemon start | Start the background daemon and detach. |
| gr daemon stop | Stop the daemon. |
| gr daemon status | Show whether the daemon is running and its PID. |
Your first service
Open the desktop app and click + Add Port, or use the CLI:
# Register a service gr add 3000 --name "My App" --cmd "npm run dev" # Start it gr start "My App" # Check status gr status
gr.toml
Check a gr.toml into your repo so every teammate gets the same setup automatically on
gr link:
# gr.toml [[service]] port = 3000 name = "Frontend" command = "npm run dev" group = "App" [[service]] port = 8080 name = "API" command = "go run ./cmd/server" group = "App"
Export your current config as gr.toml from Settings → Projects → Export gr.toml.
Groups
Assign ports to a group to start and stop them together:
gr group start App gr group stop App gr group status App
CLI reference
The gr binary talks to the daemon at 127.0.0.1:27892.
Install with npm i -g @gretl/cli.
Configuration
| Command | Description |
|---|---|
| gr add <port> | Register a port with Gretl. Flags: --name --cmd --group |
gr add 9001 --name "Worker" --cmd "node worker.js" --group "Backend"
Team registry sync
Share your service definitions with teammates via gr push and gr pull. Requires GR_TOKEN set to your Gretl API token.
| Command | Description |
|---|---|
| gr push | Push your local config to the team registry. |
| gr push --toml <path> | Push from a gr.toml file instead of local config. |
| gr pull | Pull the team registry into your local config. |
| gr pull --toml | Pull and write a gr.toml to the current directory. |
Committing a gr.toml to your repo means every teammate gets the same port names, groups, and start commands after running gr pull or gr link.
# gr.toml — commit this to your repo
[[service]]
port = 3000
name = "Frontend"
cmd = "npm run dev"
group = "Frontend"
[[service]]
port = 4000
name = "API Server"
cmd = "node server.js"
group = "Backend"
Lifecycle
| Command | Description |
|---|---|
| gr start <name|port> | Start a registered service. |
| gr stop <name|port> | Stop a running service (kills all PIDs on that port). |
| gr status | Tabular view of every known service. |
| gr logs <name|port> | Print the last 50 log lines for a service. |
| gr open <name|port> | Open the service in your browser. |
| gr list | List all registered ports. |
Groups
| Command | Description |
|---|---|
| gr group start <group> | Start every service in the group. |
| gr group stop <group> | Stop every service in the group. |
| gr group status <group> | Show status for the group. |
Daemon
| Command | Description |
|---|---|
| gr daemon start | Start the background HTTP daemon. |
| gr daemon stop | Stop the daemon. |
| gr daemon status | Show daemon status and PID. |
Kubernetes
Manage Kubernetes clusters, workloads, and port-forward tethers from the CLI. Requires GR_TOKEN. Clusters are registered at app.gretl.dev/k8s.
| Command | Description |
|---|---|
| gr k8s clusters | List registered clusters. |
| gr k8s workloads [cluster-id] | List workloads. Omit cluster-id to show all. |
| gr k8s sync <cluster-id> | Discover and sync deployments from the cluster. |
| gr k8s sleep <workload-id> | Scale a workload to 0 replicas immediately. |
| gr k8s wake <workload-id> | Scale a workload back to its last known replica count. |
| gr k8s tethers | List active port-forward tethers. |
| gr k8s tether <workload-id> --port <n> | Open a port-forward tether. Prints localhost:PORT for direct access. |
| gr k8s detach <tether-id> | Close a port-forward tether. |
# List your clusters gr k8s clusters # Sync workloads from a cluster (use first 6+ chars of the ID) gr k8s sync abc123 # See what's running / sleeping gr k8s workloads # Scale an idle agent pod to zero gr k8s sleep def456 # Wake it later (restores previous replica count) gr k8s wake def456 # Port-forward into a running pod — no kubectl required gr k8s tether def456 --port 8000 # ✓ Tether active — localhost:21042 → pod:8000
gretl.dev/agent=true and
gretl.dev/agent-type=langgraph (or crewai, vllm, ollama, ray)
to surface them in the Agent workloads section of the dashboard with auto-sleep enabled by default.eBPF pipeline
Deploy and manage the eBPF observability pipeline from the CLI. Requires kubectl on your PATH and an active kubeconfig context. See eBPF observability for full details.
| Command | Description |
|---|---|
| gr ebpf deploy | Deploy kernel-collector, k8s-collector, and reducer to current K8s context. Reads GR_TOKEN from env or --token=<key>. |
| gr ebpf deploy --namespace <ns> | Deploy to a custom namespace (default: gretl-ebpf). |
| gr ebpf deploy --endpoint <url> | Override the OTLP endpoint (default: https://api.gretl.dev). |
| gr ebpf status | Show DaemonSet, Deployment, and pod status in the eBPF namespace. |
| gr ebpf remove | Delete the eBPF namespace and all associated resources (prompts for confirmation). |
# Deploy with token from env export GR_TOKEN=gr_live_xxxx gr ebpf deploy # Or pass inline gr ebpf deploy --token=gr_live_xxxx # Check pods are running gr ebpf status # Tear down gr ebpf remove
Desktop app
The desktop app is a Tauri (Rust + React) app that runs the same daemon locally and gives you a visual interface for managing all your ports.
Ports tab
The main view shows every registered port with its status, PID, and process name. From here you can:
- ▶ Start — launch the configured command for a service
- ■ Stop — kill all processes bound to that port
- ⊕ Open — open
http://localhost:<port>in your browser - Auto-detect — scan for unmonitored ports running on your machine and add them with smart name suggestions based on the project folder name
Settings tab
General
- Launch at login — open Gretl automatically on startup
- Down alerts — get a notification when a running service drops unexpectedly
- Refresh interval — how often to poll port status (1–30s)
- Theme — Dark, Light, or System
Network
Shows all your local network IPs and your public IP, each with a one-click Copy button. Useful when sharing a local service with teammates on the same network.
Backup & restore
Export all your ports, names, groups, and commands to a gretl-backup.json file.
Import it on any machine to restore your full setup instantly.
# Settings → Projects → Export backup # Downloads gretl-backup.json # Settings → Projects → Import backup # Pick the .json file to restore
~/.gretl/config.json through normal uninstalls, but a backup gives you
an extra safety net.Projects & gr.toml
Link project folders that contain a gr.toml — Gretl will auto-import their
services on startup. Use Scan to find projects automatically, or Link folder
to add one manually.
Config file
All state is stored at ~/.gretl/config.json. This file is
shared by the desktop app, the CLI daemon, and all SDKs — changes made in one surface
are immediately visible in all others.
{
"watched": [3000, 8080],
"names": { "3000": "Frontend", "8080": "API" },
"commands": { "3000": "npm run dev", "8080": "go run ./cmd/server" },
"cwds": { "3000": "/Users/you/my-app" },
"groups": { "3000": "App", "8080": "App" },
"links": { "3000": [8080] },
"refresh_interval": 3,
"theme": "dark",
"alert_on_drop": true
}
SDK reference
Every SDK talks to the local daemon at 127.0.0.1:27892.
Start either the desktop app or gr daemon start before using an SDK.
Node.js
npm install @gretl/sdk
import { PA } from '@gretl/sdk'; const ports = await PA.ports(); // all registered ports const api = await PA.port('API'); // find by name await PA.start('Frontend'); // start by name await PA.stop('Frontend'); // stop by name await PA.waitForActive('API', 30000); // poll until active
Python
pip install gretl
from gretl import PA ports = PA.ports() # list all api = PA.port("API") # find by name or port number PA.start("Frontend") PA.stop("Frontend") PA.wait_for_active("API", timeout=30)
Ruby
gem install gretl
require "gretl" client = Gretl::Client.new client.ports # all ports client.port("API") # find by name client.start("Frontend") client.stop("Frontend") client.wait_for_active("API", timeout: 30)
Go
go get github.com/slowdutch/gretl-sdks/go
import gretl "github.com/slowdutch/gretl-sdks/go" c := gretl.NewClient() ports, _ := c.Ports() api, _ := c.Port("API") c.Start("Frontend") c.Stop("Frontend") _ = api
BYOC — self-hosted
Deploy the full Gretl control plane inside your own Kubernetes cluster using the official Helm chart. All data stays in your infrastructure — no traffic leaves except for license heartbeats to api.gretl.dev.
Helm install
Requires Helm ≥ 3.10 and a Kubernetes cluster. Get your license key from app.gretl.dev/settings.
# Add the Gretl chart repo helm repo add gretl https://charts.gretl.dev helm repo update # Install into its own namespace helm install gretl gretl/gretl \ --namespace gretl --create-namespace \ --set license.key=<your-key> \ --set backend.supabaseUrl=<url> \ --set backend.supabaseServiceKey=<key> \ --set backend.supabaseJwtSecret=<secret> \ --set backend.encryptionKey=<32-char-random> \ --set clickhouse.password=<password>
Key values
| Value | Default | Description |
|---|---|---|
license.key | "" | Enterprise license key (required) |
license.gracePeriodHours | 24 | Hours before features disable if heartbeat fails |
ebpf.enabled | true | Deploy eBPF observability pipeline |
ebpf.minKernelVersion | 5.8 | Nodes below this skip the kernel-collector safely |
clickhouse.enabled | true | Deploy in-cluster ClickHouse for telemetry |
dashboard.enabled | false | Deploy dashboard inside cluster (vs. using app.gretl.dev) |
backend.ingress.enabled | false | Expose backend via Ingress |
backend.ingress.host | gretl.internal | Ingress hostname for the backend API |
eBPF observability
eBPF telemetry gives you kernel-level visibility into every pod — CPU, memory, and network flows — with zero code changes. Requires Linux kernel ≥ 5.8 on cluster nodes.
The pipeline runs three components as a DaemonSet + two Deployments:
| Component | Type | Purpose |
|---|---|---|
kernel-collector | DaemonSet | Attaches eBPF probes, captures TCP, CPU, process spawns per node |
k8s-collector | Deployment | Watches K8s API, maps PIDs to workload identity |
reducer | Deployment | Enriches events, converts to OTLP, forwards to Gretl backend |
Deploy (standalone)
If you're not using the Helm chart, deploy with the CLI:
gr ebpf deploy --token=<GR_TOKEN>
Or with kustomize directly:
# Create secret first kubectl create secret generic gretl-ebpf-secret \ --namespace gretl-ebpf \ --from-literal=GR_TOKEN=<token> \ --from-literal=GRETL_OTLP_ENDPOINT=https://api.gretl.dev/otlp \ --dry-run=client -o yaml | kubectl apply -f - # Deploy everything kubectl apply -k https://github.com/slowdutch/gretl-sdks//gretl-ebpf/k8s # Check status gr ebpf status
What's collected
| Signal | Granularity | Retention |
|---|---|---|
| CPU millicores | Per pod, ~30s | 90 days |
| Memory MiB (RSS) | Per pod, ~30s | 90 days |
| Per-process CPU/memory | Per process within pod, ~30s | 30 days |
| Network flows | Per workload pair | 30 days |
| Inferred service calls | Per service edge, per minute | 14 days |
| CPU stack profiles | Per workload | 30 days |
| Security events | Per process spawn / connection | 90 days |
View this data in the dashboard under Telemetry — six tabs: Metrics, Processes, Profiles, Traffic, Traces, Security.
Dashboard tabs
Metrics
Workload-level CPU, memory, and network time-series. Summary cards show the latest snapshot per workload with colour-coded CPU health. The full table shows per-minute averages with inline sparkbars.
Filter by cluster, workload, and time window (1h / 6h / 24h / 7d).
Processes
Per-process CPU/memory attribution within a workload. Answers "which process is consuming the most resources?" without any instrumentation — data comes from execve events and CPU samples captured by the eBPF kernel-collector.
Example: a pod running LangGraph + a FastAPI server will show two rows — python (langgraph) and uvicorn — each with their own CPU share bar.
Select a workload to activate this tab.
Profiles
CPU stack trace samples from eBPF perf-event profiling. Shows the top 50 unique stack traces by sample count, with frame chains (leaf → root) and estimated CPU time. Useful for finding hot functions without adding a profiler SDK.
Traffic
Service-to-service network dependency graph. The SVG map shows which workloads are talking to each other — edge weight represents traffic volume. The table below shows flow count, total bytes, and average latency per edge.
Built from kernel-observed TCP connections — no Istio or Linkerd required.
Traces
Inferred service call timelines derived from eBPF-observed TCP connections. Shows call counts, avg/p99 latency, and total bytes per (caller → callee, port) edge per minute.
Security
eBPF-observed security-relevant kernel events. The pipeline detects:
| Rule | Severity | What triggers it |
|---|---|---|
SHELL_SPAWN_NONROOT | warn | Shell (bash/sh/zsh) spawned by a non-root process inside a container |
NETWORK_TOOL_EXEC | critical | nmap, masscan, netcat, socat executed |
CREDENTIAL_TOOL_EXEC | critical | Credential access tools (mimikatz, hashcat, john) |
SUDO_EXEC | warn | sudo/su called by non-root |
PKG_MANAGER_EXEC | info | apt, pip, npm, yum executed at runtime (unusual in containers) |
SHELL_OUTBOUND_CONN | warn | Shell or interpreter makes outbound TCP connection to a non-standard port |
Events are grouped by severity (critical / warn / info) with badge counts at the top. Click any row to see the full process name, cmdline, and remote IP if applicable.
Agent auto-detection
Gretl automatically tags workloads as agent workloads based on eBPF-observed process names and command lines — no gr.mark_agent() call required.
The detection sweeper runs every 5 minutes and queries process_metrics for recently-seen processes. Matches are upserted to k8s_workloads.is_agent = true with the detected framework:
| Framework | Detected from |
|---|---|
| vLLM | process name or cmdline contains vllm |
| Ollama | process name is ollama |
| Ray | process name starts with ray::, or raylet, or cmdline contains ray.serve |
| LangGraph | cmdline contains langgraph or langchain |
| CrewAI | cmdline contains crewai |
| Custom | Python/Node running a file with agent or worker in its name |
Manual tags set via the dashboard or SDK always take precedence — auto-detection only fires if is_agent is currently false.