diff --git a/README.md b/README.md
index cd2869d7..3774de97 100644
--- a/README.md
+++ b/README.md
@@ -10,15 +10,15 @@
[](https://github.com/TerrifiedBug/vectorflow/actions/workflows/ci.yml)
[](https://github.com/TerrifiedBug/vectorflow/releases)
[](LICENSE)
-[](https://terrifiedbug.gitbook.io/vectorflow)
+[](https://vectorflow.sh/docs)
Design, deploy, and monitor [Vector](https://vector.dev) data pipelines visually.
Stop hand-editing YAML. Build observability pipelines with drag-and-drop and deploy them across your fleet from a single dashboard.
-[Documentation](https://terrifiedbug.gitbook.io/vectorflow) · [Quick start](#quick-start) · [Deployment](#deployment) · [Features](#features) · [Configuration](#configuration) · [Development](#development)
+[Documentation](https://vectorflow.sh/docs) · [Quick start](#quick-start) · [Deployment](#deployment) · [Features](#features) · [Configuration](#configuration) · [Development](#development)
-> 🌐 **[Try the live demo →](https://demo.terrifiedbug.com)**
+> 🌐 **[Try the live demo →](https://demo.vectorflow.sh)**
diff --git a/docs/public/.gitbook.yaml b/docs/public/.gitbook.yaml
deleted file mode 100644
index 6acdd6ab..00000000
--- a/docs/public/.gitbook.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-root: ./
-
-structure:
- readme: README.md
- summary: SUMMARY.md
diff --git a/docs/public/.gitbook/gitbook-skill.md b/docs/public/.gitbook/gitbook-skill.md
deleted file mode 100644
index ddc02e01..00000000
--- a/docs/public/.gitbook/gitbook-skill.md
+++ /dev/null
@@ -1,64 +0,0 @@
-# GitBook Documentation Editing Skill
-
-This guide enables AI tools to write GitBook-compatible markdown.
-
-## Key Formatting Elements
-
-GitBook extends standard markdown with custom blocks:
-
-### Tabs
-{% tabs %}
-{% tab title="Docker" %}
-Content for Docker tab
-{% endtab %}
-{% tab title="Standalone" %}
-Content for Standalone tab
-{% endtab %}
-{% endtabs %}
-
-### Hints / Callouts
-{% hint style="info" %}
-Informational callout
-{% endhint %}
-
-{% hint style="warning" %}
-Warning callout
-{% endhint %}
-
-{% hint style="danger" %}
-Danger callout
-{% endhint %}
-
-{% hint style="success" %}
-Success callout
-{% endhint %}
-
-### Steppers
-{% stepper %}
-{% step %}
-### Step Title
-Step content
-{% endstep %}
-{% endstepper %}
-
-### Expandable Content
-
-Click to expand
-Hidden content here
-
-
-## Configuration
-
-- **.gitbook.yaml** -- Space configuration (root directory, readme/summary paths)
-- **SUMMARY.md** -- Table of contents defining sidebar navigation
-- **/.gitbook/vars.yaml** -- Space-level reusable variables
-
-## Writing Guidelines
-
-- Use hierarchical headings (H1-H3)
-- Keep paragraphs short, use bullet points
-- Include code snippets and practical examples
-- Minimize jargon
-- Use tabs for platform-specific content (Docker vs Standalone, Linux vs macOS)
-- Use hints for important callouts
-- Use steppers for sequential procedures
diff --git a/docs/public/README.md b/docs/public/README.md
index 654c6eb3..b3c08cd3 100644
--- a/docs/public/README.md
+++ b/docs/public/README.md
@@ -1,47 +1,11 @@
-# Introduction
+# Public docs moved
-VectorFlow is a visual pipeline management platform for [Vector](https://vector.dev), the high-performance observability data pipeline. It gives teams a web-based UI for designing, deploying, and monitoring Vector pipelines across an entire fleet of nodes -- no hand-edited TOML required.
+The VectorFlow user-facing documentation is now at:
-
+****
-## Why VectorFlow?
+The Fumadocs source for that site lives in the standalone repo:
-Observability pipelines are critical infrastructure, yet managing them usually means juggling config files across dozens of servers. VectorFlow replaces that workflow with a centralized control plane:
+****
-- **Visual Pipeline Editor** -- Drag-and-drop sources, transforms, and sinks onto a canvas. The editor generates valid Vector configuration automatically.
-- **Fleet Management** -- Enroll agents on every node and push pipeline updates from a single dashboard. See which agents are online, what they are running, and roll back instantly.
-- **Multi-Environment Support** -- Maintain separate development, staging, and production environments. Promote pipelines through your deployment lifecycle with confidence.
-- **VRL Snippet Testing** -- Write and test Vector Remap Language transforms interactively before they reach production.
-- **Real-Time Metrics** -- Monitor throughput, error rates, and pipeline health at a glance.
-- **Alerting** -- Define alert rules so you know the moment a pipeline degrades.
-
-## Technology
-
-| Layer | Technology |
-|-------|------------|
-| Frontend | Next.js 16, React 19, TypeScript, Tailwind CSS |
-| Flow Editor | React Flow (@xyflow/react) |
-| Code Editor | Monaco Editor with VRL syntax |
-| API | tRPC (type-safe RPC) |
-| Authentication | NextAuth (credentials + OIDC) |
-| Database | PostgreSQL, Prisma ORM |
-| Agent | Go (zero external dependencies) |
-| Data Engine | Vector (vector.dev) |
-
-{% hint style="info" %}
-**Get started in 5 minutes.** Follow the [Quick Start](getting-started/quick-start.md) guide to spin up VectorFlow with Docker and build your first pipeline.
-{% endhint %}
-
-## Quick Links
-
-| | |
-|---|---|
-| **Getting Started** | [Quick Start](getting-started/quick-start.md) -- Install and run VectorFlow |
-| **Deploy** | [Server](getting-started/deploy-server.md) -- Set up the VectorFlow server |
-| | [Agents](getting-started/deploy-agents.md) -- Enroll agents on your nodes |
-| **Learn** | [Pipeline Editor](user-guide/pipeline-editor.md) -- Build pipelines visually |
-| | [Fleet Management](user-guide/fleet.md) -- Manage your agent fleet |
-| **Operate** | [Architecture](operations/architecture.md) -- Understand how it all fits together |
-| | [Configuration](operations/configuration.md) -- Environment variables and settings |
-| **Reference** | [API](reference/api.md) -- Full API documentation |
-| | [Pipeline YAML](reference/pipeline-yaml.md) -- Pipeline configuration format |
+To propose a docs change, open a PR against `vectorflow.sh` instead of editing this directory.
diff --git a/docs/public/SUMMARY.md b/docs/public/SUMMARY.md
deleted file mode 100644
index 579591b6..00000000
--- a/docs/public/SUMMARY.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Summary
-
-* [Introduction](README.md)
-
-## Getting Started
-
-* [Quick Start](getting-started/quick-start.md)
-* [Deploy the Server](getting-started/deploy-server.md)
-* [Deploy Agents](getting-started/deploy-agents.md)
-* [Your First Pipeline](getting-started/first-pipeline.md)
-
-## User Guide
-
-* [Dashboard](user-guide/dashboard.md)
-* [Pipelines](user-guide/pipelines.md)
-* [Pipeline Editor](user-guide/pipeline-editor.md)
-* [VRL Snippets](user-guide/vrl-snippets.md)
-* [Environments](user-guide/environments.md)
-* [Fleet Management](user-guide/fleet.md)
-* [Alerts](user-guide/alerts.md)
-* [Templates](user-guide/templates.md)
-* [Shared Components](user-guide/shared-components.md)
-* [AI Suggestions](user-guide/ai-suggestions.md)
-* [Cost Optimization](user-guide/cost-optimization.md)
-* [Anomaly Detection](user-guide/anomaly-detection.md)
-* [Pipeline Dependencies](user-guide/pipeline-dependencies.md)
-* [Migration Toolkit](user-guide/migration-toolkit.md)
-
-## Operations
-
-* [Architecture](operations/architecture.md)
-* [Configuration](operations/configuration.md)
-* [Authentication](operations/authentication.md)
-* [SCIM Provisioning](operations/scim.md)
-* [Service Accounts](operations/service-accounts.md)
-* [Backup & Restore](operations/backup-restore.md)
-* [GitOps](operations/gitops.md)
-* [Outbound Webhooks](operations/outbound-webhooks.md)
-* [Security](operations/security.md)
-* [Telemetry](operations/telemetry.md)
-* [Upgrading](operations/upgrading.md)
-
-## Reference
-
-* [API Reference](reference/api.md)
-* [Agent Reference](reference/agent.md)
-* [Agent Architecture](reference/agent-architecture.md)
-* [Agent Troubleshooting](reference/agent-troubleshooting.md)
-* [Database Schema](reference/database.md)
-* [Pipeline YAML](reference/pipeline-yaml.md)
diff --git a/docs/public/getting-started/deploy-agents.md b/docs/public/getting-started/deploy-agents.md
deleted file mode 100644
index 12ca8706..00000000
--- a/docs/public/getting-started/deploy-agents.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# Deploy Agents
-
-The VectorFlow agent (`vf-agent`) is a lightweight Go binary that runs on each node in your fleet. It manages Vector processes, pulls pipeline configurations from the server, and reports health via heartbeats.
-
-{% hint style="info" %}
-**Pull-based architecture** -- agents poll the server for config updates. No inbound ports are required on your fleet nodes. The agent initiates all connections, making it firewall-friendly and easy to deploy behind NAT.
-{% endhint %}
-
-## How it works
-
-1. The agent starts and **enrolls** with the VectorFlow server using a one-time enrollment token
-2. After enrollment, the server issues a persistent **node token** stored on disk
-3. The agent **polls** the server every 15 seconds (configurable) for pipeline configuration changes
-4. When a new config is received, the agent writes it to disk and starts or restarts the Vector process
-5. The agent sends **heartbeats** with host metrics (CPU, memory, disk, network) so the server can track fleet health
-
-Each pipeline runs as an isolated Vector process -- a crash in one pipeline does not affect others.
-
-## Enrollment flow
-
-{% stepper %}
-{% step %}
-### Generate an enrollment token
-
-In the VectorFlow UI, navigate to the **Fleet** page. Select the environment you want to enroll the agent into, then click **Add Node**.
-
-VectorFlow generates a one-time enrollment token. Copy it -- you will need it in the next step.
-
-{% hint style="warning" %}
-Enrollment tokens are single-use. Each agent needs its own token. Generate a new one for each node you want to enroll.
-{% endhint %}
-{% endstep %}
-
-{% step %}
-### Deploy the agent
-
-Choose one of the deployment methods below and start the agent with the enrollment token.
-{% endstep %}
-
-{% step %}
-### Verify enrollment
-
-Once the agent starts, it enrolls with the server and appears on the **Fleet** page with an **Online** status. You can now deploy pipelines to this node.
-
-If the agent does not appear, check its logs for enrollment errors (`docker compose logs -f` or `journalctl -u vf-agent -f`).
-{% endstep %}
-{% endstepper %}
-
-## Deployment methods
-
-{% tabs %}
-{% tab title="Docker" %}
-The simplest approach -- ideal for containerized environments.
-
-```bash
-mkdir -p vectorflow-agent && cd vectorflow-agent
-
-curl -sSfL -o docker-compose.yml \
- https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/docker/agent/docker-compose.yml
-```
-
-Create a `.env` file with your server URL and enrollment token:
-
-```bash
-cat > .env << 'EOF'
-VF_URL=https://your-vectorflow-server:3000
-VF_TOKEN=paste-enrollment-token-here
-EOF
-```
-
-Start the agent:
-
-```bash
-docker compose up -d
-```
-
-The Docker image bundles Vector, so no additional dependencies are needed. Two named volumes persist agent state and Vector data across restarts:
-
-| Volume | Mount point | Contents |
-|--------|-------------|----------|
-| `vectorflow-agent-data` | `/var/lib/vf-agent` | Node token, pipeline configs |
-| `vectorflow-vector-data` | `/var/lib/vector` | Vector checkpoints and buffer data |
-
-The agent container uses `network_mode: host` so Vector can bind to local ports (e.g., for syslog or socket sources).
-
-{% hint style="warning" %}
-After the first successful enrollment, the agent persists a node token to disk. The `VF_TOKEN` enrollment token is no longer needed. You can remove it from your `.env` file, but leaving it has no effect.
-{% endhint %}
-{% endtab %}
-
-{% tab title="Install script" %}
-The install script downloads the agent binary, installs Vector if needed, and configures a systemd service -- all in one command.
-
-```bash
-curl -sSfL https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/agent/install.sh | \
- sudo bash -s -- --url https://vectorflow.example.com --token
-```
-
-**Managing the service:**
-
-```bash
-systemctl status vf-agent # Check status
-journalctl -u vf-agent -f # Follow logs
-sudo systemctl restart vf-agent # Restart
-```
-
-**Upgrading:**
-
-```bash
-# Upgrade to the latest release
-curl -sSfL https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/agent/install.sh | sudo bash
-
-# Install a specific version
-curl -sSfL https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/agent/install.sh | \
- sudo bash -s -- --version v0.3.0
-```
-
-Existing configuration at `/etc/vectorflow/agent.env` is preserved during upgrades.
-
-**Uninstalling:**
-
-```bash
-sudo systemctl stop vf-agent
-sudo systemctl disable vf-agent
-sudo rm /etc/systemd/system/vf-agent.service
-sudo systemctl daemon-reload
-sudo rm /usr/local/bin/vf-agent
-sudo rm -rf /var/lib/vf-agent /etc/vectorflow
-```
-{% endtab %}
-
-{% tab title="Standalone binary" %}
-Download the binary from the [Releases](https://github.com/TerrifiedBug/vectorflow/releases) page and run it directly. Useful for testing or environments without systemd.
-
-```bash
-# Download the latest release for your platform
-curl -sSfL -o vf-agent \
- https://github.com/TerrifiedBug/vectorflow/releases/latest/download/vf-agent-linux-amd64
-chmod +x vf-agent
-```
-
-Run with environment variables:
-
-```bash
-VF_URL=https://your-vectorflow-server:3000 \
-VF_TOKEN=paste-enrollment-token-here \
-./vf-agent
-```
-
-Or with a manual systemd unit:
-
-```ini
-# /etc/systemd/system/vf-agent.service
-[Unit]
-Description=VectorFlow Agent
-After=network-online.target
-Wants=network-online.target
-
-[Service]
-Type=simple
-EnvironmentFile=/etc/vectorflow/agent.env
-ExecStart=/usr/local/bin/vf-agent
-Restart=on-failure
-RestartSec=5
-
-# Security hardening
-NoNewPrivileges=true
-ProtectSystem=strict
-ReadWritePaths=/var/lib/vf-agent
-
-[Install]
-WantedBy=multi-user.target
-```
-
-Make sure Vector is installed and available in `PATH`, or set the `VF_VECTOR_BIN` variable to point to the binary.
-{% endtab %}
-{% endtabs %}
-
-## Environment variables
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `VF_URL` | Yes | -- | VectorFlow server URL (e.g., `https://vectorflow.example.com:3000`) |
-| `VF_TOKEN` | First run | -- | One-time enrollment token from the UI. Not needed after successful enrollment. |
-| `VF_DATA_DIR` | No | `/var/lib/vf-agent` | Directory for agent state (node token, pipeline configs). Mapped to a Docker volume by default. |
-| `VF_VECTOR_BIN` | No | `vector` | Path to the Vector binary. Set automatically in the Docker image. |
-| `VF_POLL_INTERVAL` | No | `15s` | How often the agent polls for config changes. Uses Go duration format (`15s`, `1m`, `30s`). |
-| `VF_LOG_LEVEL` | No | `info` | Log verbosity: `debug`, `info`, `warn`, or `error`. |
-
-## Troubleshooting
-
-**Agent does not appear in Fleet**
-
-- Verify `VF_URL` is correct and reachable from the agent host
-- Check that the enrollment token has not already been used
-- Look at agent logs for `enrollment failed` errors
-
-**Agent shows as "Unhealthy"**
-
-- The server marks a node unhealthy after 3 missed heartbeats (default: 45 seconds)
-- Check network connectivity between the agent and server
-- Verify the agent process is running (`systemctl status vf-agent` or `docker ps`)
-
-**Pipeline not starting on agent**
-
-- Confirm the pipeline is deployed to the correct environment
-- Check agent logs for Vector process errors
-- Verify Vector is installed and accessible at the path in `VF_VECTOR_BIN`
diff --git a/docs/public/getting-started/deploy-server.md b/docs/public/getting-started/deploy-server.md
deleted file mode 100644
index 9e62cca5..00000000
--- a/docs/public/getting-started/deploy-server.md
+++ /dev/null
@@ -1,363 +0,0 @@
-# Deploy the Server
-
-The VectorFlow server is a Next.js application backed by PostgreSQL. This page covers deployment options, environment variables, persistent storage, and production hardening.
-
-{% tabs %}
-{% tab title="Docker (recommended)" %}
-## Docker Compose
-
-The quickest path to production. The provided `docker-compose.yml` starts both the VectorFlow server and PostgreSQL.
-
-### 1. Download the Compose file
-
-```bash
-mkdir -p vectorflow && cd vectorflow
-
-curl -sSfL -o docker-compose.yml \
- https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/docker/server/docker-compose.yml
-```
-
-### 2. Create your `.env` file
-
-```bash
-cat > .env << 'EOF'
-POSTGRES_PASSWORD=
-NEXTAUTH_SECRET=
-# NEXTAUTH_URL=https://vectorflow.example.com
-EOF
-```
-
-Generate secrets with `openssl rand -base64 32`.
-
-### 3. Start the stack
-
-```bash
-docker compose up -d
-```
-
-The entrypoint automatically runs database migrations on every start, so upgrades are handled by pulling a new image and restarting.
-
-### Compose file breakdown
-
-```yaml
-services:
- postgres:
- image: postgres:17-alpine
- environment:
- POSTGRES_DB: vectorflow
- POSTGRES_USER: vectorflow
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
- volumes:
- - pgdata:/var/lib/postgresql/data
- healthcheck:
- test: ["CMD-SHELL", "pg_isready -U vectorflow"]
- restart: unless-stopped
-
- vectorflow:
- image: ghcr.io/terrifiedbug/vectorflow-server:${VF_VERSION:-latest}
- depends_on:
- postgres:
- condition: service_healthy
- ports:
- - "3000:3000"
- environment:
- DATABASE_URL: postgresql://vectorflow:${POSTGRES_PASSWORD}@postgres:5432/vectorflow
- NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
- NEXTAUTH_URL: ${NEXTAUTH_URL}
- volumes:
- - vfdata:/app/.vectorflow
- - backups:/backups
- restart: unless-stopped
-
-volumes:
- pgdata:
- vfdata:
- backups:
-```
-
-### Persistent volumes
-
-| Volume | Mount point | Contents |
-|--------|-------------|----------|
-| `vectorflow-pgdata` | `/var/lib/postgresql/data` | PostgreSQL database files |
-| `vectorflow-data` | `/app/.vectorflow` | Application state, system Vector config |
-| `vectorflow-backups` | `/backups` | Database backup snapshots |
-
-{% hint style="warning" %}
-Never delete the `vectorflow-pgdata` volume without a backup. All pipeline definitions, environments, users, and audit history live in PostgreSQL.
-{% endhint %}
-
-### Pinning a version
-
-By default the Compose file pulls `latest`. To pin a specific release:
-
-```bash
-VF_VERSION=v0.3.0 docker compose up -d
-```
-
-Or set `VF_VERSION` in your `.env` file.
-
-### Networking
-
-The server listens on port 3000. The PostgreSQL port is not exposed by default -- only the VectorFlow container can reach it over the internal Docker network. If you need direct database access for debugging:
-
-```yaml
-# Uncomment in docker-compose.yml
-ports:
- - "127.0.0.1:5432:5432"
-```
-{% endtab %}
-
-{% tab title="Standalone" %}
-## Standalone deployment
-
-Run VectorFlow directly on a Linux host without Docker. This approach gives you full control over the process manager, database, and networking.
-
-### Prerequisites
-
-- **Node.js 22+** and **pnpm**
-- **PostgreSQL 17** (running and accessible)
-- **Vector 0.44.0+** binary (for pipeline validation and VRL testing)
-
-### 1. Download the release
-
-Download the latest release archive from the [Releases](https://github.com/TerrifiedBug/vectorflow/releases) page and extract it.
-
-```bash
-curl -sSfL -o vectorflow.tar.gz \
- https://github.com/TerrifiedBug/vectorflow/releases/latest/download/vectorflow-server.tar.gz
-mkdir -p /opt/vectorflow && tar xzf vectorflow.tar.gz -C /opt/vectorflow
-```
-
-### 2. Set up PostgreSQL
-
-Create a database and user:
-
-```sql
-CREATE USER vectorflow WITH PASSWORD 'your-strong-password';
-CREATE DATABASE vectorflow OWNER vectorflow;
-```
-
-### 3. Configure environment
-
-Create an environment file at `/etc/vectorflow/server.env`:
-
-```bash
-DATABASE_URL=postgresql://vectorflow:your-strong-password@localhost:5432/vectorflow
-NEXTAUTH_SECRET=generate-a-random-32-char-string
-NEXTAUTH_URL=https://vectorflow.example.com
-PORT=3000
-NODE_ENV=production
-```
-
-### 4. Run database migrations
-
-```bash
-cd /opt/vectorflow
-npx prisma migrate deploy
-```
-
-### 5. Start the server
-
-```bash
-node server.js
-```
-
-### Systemd service
-
-For production, run VectorFlow as a systemd service:
-
-```ini
-# /etc/systemd/system/vectorflow.service
-[Unit]
-Description=VectorFlow Server
-After=network-online.target postgresql.service
-Wants=network-online.target
-
-[Service]
-Type=simple
-User=vectorflow
-Group=vectorflow
-WorkingDirectory=/opt/vectorflow
-EnvironmentFile=/etc/vectorflow/server.env
-ExecStart=/usr/bin/node server.js
-Restart=on-failure
-RestartSec=5
-
-# Security hardening
-NoNewPrivileges=true
-ProtectSystem=strict
-ProtectHome=true
-ReadWritePaths=/opt/vectorflow/.vectorflow /backups
-
-[Install]
-WantedBy=multi-user.target
-```
-
-Enable and start:
-
-```bash
-sudo systemctl daemon-reload
-sudo systemctl enable --now vectorflow
-```
-{% endtab %}
-{% endtabs %}
-
-## Environment variables
-
-{% hint style="warning" %}
-Always use strong, random values for `NEXTAUTH_SECRET` and `POSTGRES_PASSWORD`. These protect session data, encrypted secrets (TOTP, certificates), and your database.
-{% endhint %}
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `DATABASE_URL` | Yes | -- | PostgreSQL connection string (e.g., `postgresql://user:pass@host:5432/vectorflow`) |
-| `NEXTAUTH_SECRET` | Yes | -- | Session encryption key. Must be 32+ characters. Generate with `openssl rand -base64 32` |
-| `NEXTAUTH_URL` | No | -- | Canonical server URL (e.g., `https://vectorflow.example.com`). When unset, inferred from the `Host` header |
-| `REDIS_URL` | No | -- | Redis connection string for HA mode (e.g., `redis://redis:6379`). Enables leader election, cross-instance SSE broadcast, and metric distribution. When unset, VectorFlow runs as a single instance with no behavioral change |
-| `PORT` | No | `3000` | HTTP listen port |
-| `NODE_ENV` | No | `production` | Set automatically in Docker. Use `production` for standalone deployments |
-
-When using the Docker Compose setup, the following variables go in your `.env` file and are interpolated into the Compose file:
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `POSTGRES_PASSWORD` | Yes | -- | Password for the PostgreSQL `vectorflow` user |
-| `VF_VERSION` | No | `latest` | Docker image tag to pull |
-
-## Production considerations
-
-### Reverse proxy
-
-In production, place VectorFlow behind a reverse proxy for TLS termination.
-
-
-Nginx example
-
-```nginx
-server {
- listen 443 ssl http2;
- server_name vectorflow.example.com;
-
- ssl_certificate /etc/ssl/certs/vectorflow.crt;
- ssl_certificate_key /etc/ssl/private/vectorflow.key;
-
- location / {
- proxy_pass http://127.0.0.1:3000;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- # SSE streaming support (agent push channel)
- proxy_http_version 1.1;
- proxy_buffering off;
- proxy_cache off;
- }
-}
-```
-
-
-
-Caddy example
-
-```
-vectorflow.example.com {
- reverse_proxy localhost:3000
-}
-```
-
-Caddy handles TLS certificates automatically via Let's Encrypt.
-
-
-When using a reverse proxy, set `NEXTAUTH_URL` to your public URL (e.g., `https://vectorflow.example.com`).
-
-### TLS
-
-- **Agents communicate with the server over HTTPS.** Always terminate TLS in production.
-- If you cannot use a reverse proxy, consider a TLS-terminating load balancer.
-
-### Database tuning
-
-For deployments managing more than 50 agents, consider tuning PostgreSQL:
-
-- Increase `shared_buffers` to 25% of available RAM
-- Set `work_mem` to 64 MB
-- Enable `pg_stat_statements` for query monitoring
-- Schedule regular `VACUUM ANALYZE` runs
-
-### Resource requirements
-
-| Scale | CPU | RAM | Disk |
-|-------|-----|-----|------|
-| Small (1-10 agents) | 1 core | 1 GB | 10 GB |
-| Medium (10-50 agents) | 2 cores | 2 GB | 25 GB |
-| Large (50+ agents) | 4 cores | 4 GB | 50 GB+ |
-
-These are minimums. The server is lightweight -- most resources go to PostgreSQL.
-
-## High Availability
-
-VectorFlow supports running two or more instances behind a load balancer for high availability. When `REDIS_URL` is set, instances coordinate through Redis to provide:
-
-- **Leader election** — only one instance runs singleton services (backup scheduler, alert evaluator, metric aggregation), with automatic failover if the leader goes down
-- **Cross-instance SSE broadcast** — all SSE connections receive all fleet events regardless of which instance the client is connected to
-- **Metric distribution** — heartbeat metrics processed by any instance are visible on all instances
-- **Agent push relay** — configuration pushes reach agents connected to any instance
-
-When `REDIS_URL` is not set, VectorFlow runs in single-instance mode with no behavioral change.
-
-### Prerequisites
-
-- **Redis 7+** (included in the HA Compose file)
-- All instances must share the same `DATABASE_URL`, `NEXTAUTH_SECRET`, and `REDIS_URL`
-
-### Quick start
-
-Download the HA Compose file and nginx config:
-
-```bash
-mkdir -p vectorflow && cd vectorflow
-
-curl -sSfL -o docker-compose.ha.yml \
- https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/docker/server/docker-compose.ha.yml
-
-curl -sSfL -o nginx-ha.conf \
- https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/docker/server/nginx-ha.conf
-```
-
-Create your `.env` file:
-
-```bash
-cat > .env << 'EOF'
-POSTGRES_PASSWORD=
-NEXTAUTH_SECRET=
-NEXTAUTH_URL=http://localhost:3000
-EOF
-```
-
-{% hint style="info" %}
-`REDIS_URL` is set automatically inside the Compose file — you do not need to add it to `.env`.
-{% endhint %}
-
-Start the stack:
-
-```bash
-docker compose -f docker-compose.ha.yml up -d
-```
-
-This starts five services: PostgreSQL, Redis, two VectorFlow instances (`vf1` and `vf2`), and an nginx reverse proxy on port 3000.
-
-### How it works
-
-On startup, instances compete for a Redis-based leader lock. The winner becomes the **leader** and runs singleton services (backup scheduler, alert evaluator, metric aggregation). The other instance runs as a **follower**, serving HTTP traffic and API requests but not running singletons. If the leader goes down, a follower automatically takes over.
-
-Both instances sit behind nginx, which round-robins HTTP requests across them. SSE connections stay open to whichever instance the client connects to, and Redis pub/sub ensures all instances broadcast the same events.
-
-{% hint style="warning" %}
-`NEXTAUTH_SECRET` must be identical across all instances — sessions signed by one instance must be verifiable by the other. The HA Compose file handles this automatically by sharing the same environment variable.
-{% endhint %}
-
-### Scaling beyond two instances
-
-To add more instances, duplicate a `vf` service block in `docker-compose.ha.yml` and add the new service name to the nginx `upstream` block in `nginx-ha.conf`. Leader election and Redis coordination scale to any number of instances.
diff --git a/docs/public/getting-started/first-pipeline.md b/docs/public/getting-started/first-pipeline.md
deleted file mode 100644
index 4756992c..00000000
--- a/docs/public/getting-started/first-pipeline.md
+++ /dev/null
@@ -1,150 +0,0 @@
-# Your First Pipeline
-
-This walkthrough guides you through creating a simple pipeline that generates demo log events, transforms them with VRL (Vector Remap Language), and outputs the result to the console. By the end, you will have a working pipeline deployed to your fleet.
-
-## Prerequisites
-
-- A running VectorFlow server ([Quick Start](quick-start.md))
-- At least one enrolled agent ([Deploy Agents](deploy-agents.md))
-
-## Build the pipeline
-
-{% stepper %}
-{% step %}
-### Create a new pipeline
-
-Navigate to **Pipelines** in the sidebar and click **New Pipeline**.
-{% endstep %}
-
-{% step %}
-### Name and configure
-
-Give the pipeline a name (e.g., `demo-pipeline`) and select the **environment** where your agent is enrolled. Click **Create**.
-
-You are now in the pipeline editor -- a drag-and-drop canvas with three panels:
-
-- **Left** -- Component palette with all available sources, transforms, and sinks
-- **Center** -- Canvas where you build the pipeline graph
-- **Right** -- Detail panel for configuring the selected node
-
-
-{% endstep %}
-
-{% step %}
-### Add a source
-
-In the component palette on the left, find **Demo Logs** under the **Testing** category (or type "demo" in the search box).
-
-Drag it onto the canvas. A green source node appears. Click on it to open the detail panel on the right, and set the **format** to `json`. This generates fake JSON log events every second.
-{% endstep %}
-
-{% step %}
-### Add a transform
-
-Search for **Remap (VRL)** in the component palette and drag it onto the canvas to the right of your source node.
-
-Click the Remap node to open the detail panel. In the **VRL Source** editor, write a simple transformation:
-
-```coffeescript
-.message = "processed: " + string!(.message)
-.processed_at = now()
-```
-
-This prepends "processed: " to each log message and adds a timestamp field.
-{% endstep %}
-
-{% step %}
-### Add a sink
-
-Search for **Console** in the component palette and drag it onto the canvas to the right of your transform node.
-
-Click the Console node and set the **encoding** codec to `json`. The console sink prints events to Vector's stdout, which the agent captures and forwards to VectorFlow for viewing.
-{% endstep %}
-
-{% step %}
-### Connect the nodes
-
-Draw connections between your components:
-
-1. Hover over the **output port** (small circle on the right edge) of the Demo Logs source node
-2. Click and drag a line to the **input port** (small circle on the left edge) of the Remap transform node
-3. Release to create the connection
-4. Repeat: connect the Remap output to the Console sink input
-
-Your pipeline graph should now show: **Demo Logs** -> **Remap (VRL)** -> **Console**
-
-{% hint style="info" %}
-VectorFlow validates connection compatibility in real time. You cannot connect a metrics-only source to a logs-only sink, for example. Invalid connections are rejected automatically.
-{% endhint %}
-{% endstep %}
-
-{% step %}
-### Name your components
-
-Each node has a **Name** field in the detail panel. By default, new components are named after their type (e.g., "Demo Logs"). You can rename them to something more descriptive like "Demo Source", "Add Timestamp", and "Debug Output".
-
-Renaming a component only requires saving the pipeline -- it does not require a redeploy. The backend component ID is auto-generated and shown as a read-only field below the name.
-{% endstep %}
-
-{% step %}
-### Validate the pipeline
-
-Click the **Validate** button (checkmark icon) in the toolbar at the top of the editor.
-
-VectorFlow generates the Vector YAML configuration from your graph and sends it to Vector for validation. If everything is correct, you see a green "Pipeline is valid!" toast notification.
-
-If validation fails, the error message tells you exactly which component has an issue. Fix the configuration and validate again.
-{% endstep %}
-
-{% step %}
-### Save the pipeline
-
-Click the **Save** button in the toolbar (or press `Cmd+S` / `Ctrl+S`). This persists your pipeline graph to the database but does not deploy it yet.
-{% endstep %}
-
-{% step %}
-### Deploy
-
-Click the **Deploy** button in the toolbar. The deploy dialog opens and shows:
-
-- The **target environment** and how many agents are enrolled
-- A **validation check** (the pipeline must be valid to deploy)
-- A **YAML diff** comparing the new config against the previously deployed version (if any)
-- A **Deployment Reason** field -- describe what changed and why
-
-Enter a deployment reason (e.g., "Initial demo pipeline"), then click **Publish to Agents**.
-
-VectorFlow publishes the pipeline configuration. Agents pick up the new config on their next poll cycle (default: 15 seconds).
-{% endstep %}
-
-{% step %}
-### Verify
-
-Navigate to the **Fleet** page. You should see your agent with an **Online** status. The pipeline status shows as **Running** once Vector picks up the configuration.
-
-Back in the pipeline editor, the toolbar shows a green **Deployed** badge. If you enabled metrics, you can see live event rates on the canvas edges.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="success" %}
-**Congratulations!** You have built, validated, and deployed your first VectorFlow pipeline. Demo log events are flowing through your Remap transform and printing to the console.
-{% endhint %}
-
-## What just happened
-
-Under the hood, VectorFlow:
-
-1. Converted your visual graph into a Vector YAML configuration
-2. Validated the config using Vector's built-in `--config-yaml validate` command
-3. Created an immutable **version snapshot** with your changelog entry
-4. Published the config to all agents in the target environment
-5. Each agent pulled the new config, wrote it to disk, and started a new Vector process
-
-## Next steps
-
-Now that you have the basics, explore more of VectorFlow:
-
-- [Pipeline Editor](../user-guide/pipeline-editor.md) -- keyboard shortcuts, import/export, templates, and metrics overlay
-- [VRL Snippets](../user-guide/vrl-snippets.md) -- save and reuse VRL patterns across pipelines
-- [Environments](../user-guide/environments.md) -- organize your fleet into staging, production, and other environments
-- [Fleet Management](../user-guide/fleet.md) -- monitor agent health, view logs, and manage nodes
diff --git a/docs/public/getting-started/quick-start.md b/docs/public/getting-started/quick-start.md
deleted file mode 100644
index 3af40050..00000000
--- a/docs/public/getting-started/quick-start.md
+++ /dev/null
@@ -1,97 +0,0 @@
-# Quick Start
-
-Get VectorFlow running in under 5 minutes. This guide walks you through starting the server with Docker Compose and completing the initial setup.
-
-## Prerequisites
-
-- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) (v2+)
-- A machine with at least 1 CPU core and 1 GB RAM
-
-{% stepper %}
-{% step %}
-### Download and configure
-
-Create a directory for VectorFlow, download the Docker Compose file, and create an environment file with your secrets.
-
-```bash
-mkdir vectorflow && cd vectorflow
-
-# Download the server compose file
-curl -sSfL -o docker-compose.yml \
- https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/docker/server/docker-compose.yml
-```
-
-Create a `.env` file next to `docker-compose.yml`:
-
-```bash
-cat > .env << 'EOF'
-# Database password — use a random 32+ character string
-POSTGRES_PASSWORD=changeme
-
-# Session & encryption key — generate with: openssl rand -base64 32
-NEXTAUTH_SECRET=changeme
-EOF
-```
-
-{% hint style="warning" %}
-Replace both `changeme` values with strong, random strings before starting. You can generate them with:
-
-```bash
-openssl rand -base64 32
-```
-{% endhint %}
-
-`NEXTAUTH_URL` is optional. When omitted, VectorFlow infers the URL from the incoming `Host` header. Set it explicitly if you place VectorFlow behind a reverse proxy (e.g., `https://vectorflow.example.com`).
-{% endstep %}
-
-{% step %}
-### Start VectorFlow
-
-```bash
-docker compose up -d
-```
-
-Docker pulls the VectorFlow server and PostgreSQL images, runs database migrations automatically, and starts both services. You can follow the logs with:
-
-```bash
-docker compose logs -f vectorflow
-```
-
-Wait until you see `Starting VectorFlow...` in the output.
-{% endstep %}
-
-{% step %}
-### Complete the setup wizard
-
-Open your browser and navigate to [http://localhost:3000](http://localhost:3000).
-
-The setup wizard walks you through creating your first admin account. Enter a username, email, and password, then click **Create Account**.
-
-Once logged in, you land on the VectorFlow dashboard.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="success" %}
-**You're ready!** VectorFlow is running and your admin account is set up. Time to deploy some agents and build your first pipeline.
-{% endhint %}
-
-## What's in the stack
-
-The Docker Compose file starts two containers:
-
-| Container | Image | Purpose |
-|-----------|-------|---------|
-| `vectorflow-server` | `ghcr.io/terrifiedbug/vectorflow-server` | Next.js application, tRPC API, pipeline validation |
-| `vectorflow-postgres` | `postgres:17-alpine` | Database for pipelines, environments, users, and audit logs |
-
-Three named volumes persist data across restarts:
-
-- **`vectorflow-pgdata`** -- PostgreSQL data
-- **`vectorflow-data`** -- VectorFlow application state
-- **`vectorflow-backups`** -- Database backups
-
-## Next steps
-
-- [Deploy Agents](deploy-agents.md) -- enroll your first fleet node
-- [Your First Pipeline](first-pipeline.md) -- build and deploy a pipeline in the visual editor
-- [Deploy the Server](deploy-server.md) -- production deployment options, TLS, and environment variables
diff --git a/docs/public/operations/architecture.md b/docs/public/operations/architecture.md
deleted file mode 100644
index 45caafac..00000000
--- a/docs/public/operations/architecture.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# Architecture
-
-VectorFlow uses a **hub-and-spoke architecture** where a central server manages configuration and state while lightweight agents run on each node to execute Vector pipelines.
-
-## System overview
-
-```
- ┌─────────────────────────┐
- │ Browser (React) │
- │ Pipeline Editor, Fleet │
- │ Dashboard, Settings │
- └───────────┬─────────────┘
- │ HTTPS
- ┌───────────▼─────────────┐
- │ VectorFlow Server │
- │ (Next.js + tRPC) │
- │ │
- │ ┌──────────────────┐ │
- │ │ PostgreSQL │ │
- │ │ (all state) │ │
- │ └──────────────────┘ │
- └───┬──────────┬──────┬───┘
- │ │ │
- ┌─────────▼──┐ ┌────▼───┐ ┌▼──────────┐
- │ Agent A │ │Agent B │ │ Agent N │
- │ (Go) │ │ (Go) │ │ (Go) │
- │ ┌───────┐ │ │┌─────┐ │ │ ┌───────┐ │
- │ │Vector │ │ ││Vec. │ │ │ │Vector │ │
- │ └───────┘ │ │└─────┘ │ │ └───────┘ │
- └────────────┘ └────────┘ └───────────┘
-```
-
-## Components
-
-### Server
-
-The VectorFlow server is a **Next.js** application that provides the web UI, REST API, and all management logic. It is the single source of truth for pipeline definitions, environment configuration, user accounts, and audit history.
-
-Key responsibilities:
-- Serve the browser-based pipeline editor and dashboard
-- Store pipeline graphs, configurations, and deployment versions
-- Generate Vector configuration files (YAML/TOML) from visual pipeline graphs
-- Manage user authentication, teams, and role-based access
-- Accept agent heartbeats and store fleet metrics
-- Evaluate alert rules and fire webhook notifications
-
-### Agent
-
-The VectorFlow **agent** is a lightweight Go binary that runs on each node where you want to execute Vector pipelines. Agents are stateless -- all configuration comes from the server.
-
-Key responsibilities:
-- Enroll with the server using a one-time enrollment token
-- Poll the server for configuration changes and pending actions
-- Start, stop, and reload Vector processes on the local node
-- Report metrics, pipeline status, and logs back to the server
-- Self-update when a new agent version is available
-
-### Database
-
-VectorFlow uses **PostgreSQL** as its sole data store. All state lives in the database:
-
-- Pipeline definitions and version history
-- Environment, team, and user configuration
-- Encrypted secrets and certificates
-- Agent node registrations and metrics
-- Audit log entries
-- System settings (OIDC, backup schedule, fleet tuning)
-
-The schema is managed by Prisma ORM, and migrations run automatically on server startup.
-
-### Vector
-
-[Vector](https://vector.dev) is the high-performance data router that does the actual work of collecting, transforming, and shipping observability data. VectorFlow does not replace Vector -- it provides a management layer on top of it.
-
-Each agent manages one or more Vector processes on its node. When a pipeline is deployed, the agent receives a generated Vector configuration file, writes it to disk, and starts or reloads the Vector process.
-
-## Data flow
-
-### Pipeline lifecycle
-
-A pipeline moves through these stages from creation to execution:
-
-```
-Editor (browser)
- │ User builds pipeline graph visually
- ▼
-Server (tRPC mutation)
- │ Pipeline graph saved to PostgreSQL
- ▼
-Deploy preview
- │ Server generates Vector YAML from graph
- │ Resolves secrets and certificates
- │ Validates configuration
- ▼
-Deploy to agents
- │ Creates a PipelineVersion snapshot
- │ Sends config to each agent via heartbeat actions
- ▼
-Agent receives config
- │ Writes YAML to disk
- │ Starts or reloads Vector process
- ▼
-Vector runs pipeline
- │ Data flows from sources → transforms → sinks
- │ Agent reports metrics back via heartbeat
- ▼
-Dashboard
- Events processed, errors, throughput visible in UI
-```
-
-### Metrics collection
-
-Agents report metrics to the server on every heartbeat cycle (default: every 15 seconds):
-
-- **Node metrics** -- CPU, memory, disk, and network usage
-- **Pipeline status** -- Events in/out, errors, bytes processed per component
-- **Logs** -- Pipeline log output
-- **Event samples** -- Sample events for schema discovery
-
-The server stores these in PostgreSQL and evaluates alert rules against configured thresholds on each heartbeat.
-
-## Agent communication
-
-### Pull-based polling
-
-Agents use a **pull-based** communication model. The agent initiates all connections -- the server never connects to agents. This design was chosen for three reasons:
-
-1. **Security** -- Agents can run behind firewalls and NATs without exposing any ports. Only outbound HTTPS is required.
-2. **Simplicity** -- No need for service discovery, message brokers, or persistent connections.
-3. **Scalability** -- The server handles agents as stateless HTTP clients. No per-agent connection state to manage.
-
-### Protocol
-
-Agents communicate via three REST endpoints:
-
-| Endpoint | Method | Purpose |
-|----------|--------|---------|
-| `/api/agent/enroll` | POST | One-time enrollment. Agent sends enrollment token, receives a persistent node token. |
-| `/api/agent/heartbeat` | POST | Periodic check-in. Agent sends metrics and status, receives pending actions (deploy, undeploy, update). |
-| `/api/agent/config` | POST | Fetch the generated Vector configuration for a specific pipeline. |
-
-### Heartbeat cycle
-
-On each heartbeat, the agent sends:
-- Current agent version
-- Node resource metrics (CPU, memory, disk)
-- Status of each running pipeline (events processed, errors)
-- Pipeline logs since last heartbeat
-
-The server responds with any **pending actions**:
-- Deploy a new pipeline version
-- Undeploy a pipeline
-- Self-update to a new agent version
-
-### Enrollment
-
-When an agent starts for the first time, it sends the enrollment token (provided via `VF_TOKEN`) to the server. The server validates the token, registers the node in the target environment, and returns a persistent **node token**. The agent stores this token locally and uses it for all future heartbeat requests.
-
-```
-Agent Server
- │ │
- │── POST /api/agent/enroll ────────▶│
- │ { enrollmentToken } │
- │ │ Validate token
- │ │ Create node record
- │◀── { nodeToken, nodeId } ────────│
- │ │
- │ (stores node token to disk) │
- │ │
- │── POST /api/agent/heartbeat ────▶│
- │ { nodeToken, metrics, ... } │
- │◀── { pendingActions: [...] } ────│
- │ │
-```
-
-## Security model
-
-VectorFlow's architecture is designed with defense in depth:
-
-- **Agent-initiated connections only** -- The server never opens connections to agent nodes. Agents poll the server over HTTPS, so they work behind firewalls without exposing any inbound ports.
-- **Encrypted secrets** -- Sensitive values (API keys, passwords, certificates) are encrypted with AES-256-GCM before storage. They are only decrypted at deploy time when generating Vector configuration.
-- **Token-based agent auth** -- Each agent has a unique node token issued during enrollment. Tokens are stored with restricted file permissions (`0600`) on the agent host.
-- **Role-based access control** -- Users are assigned roles (Viewer, Editor, Admin) per team. Super Admins have platform-wide access.
-- **Audit logging** -- Every mutation is logged with the user, IP address, timestamp, and a diff of changed fields.
-
-For a detailed security guide, see [Security](security.md).
diff --git a/docs/public/operations/authentication.md b/docs/public/operations/authentication.md
deleted file mode 100644
index 716edf7c..00000000
--- a/docs/public/operations/authentication.md
+++ /dev/null
@@ -1,291 +0,0 @@
-# Authentication
-
-VectorFlow supports multiple authentication methods: local credentials, OIDC/SSO, and two-factor authentication (2FA). This page covers initial setup, login methods, user management, and role-based access control.
-
-
-
-## Initial setup
-
-When VectorFlow starts for the first time with an empty database, it redirects to a setup wizard. The wizard creates the first admin account and the first team.
-
-{% stepper %}
-{% step %}
-### Create admin account
-Enter your name, email address, and a password (minimum 8 characters).
-{% endstep %}
-{% step %}
-### Name your team
-Choose a name for your first team (e.g., "Platform Engineering"). Teams organize environments and pipelines.
-{% endstep %}
-{% step %}
-### Start using VectorFlow
-After setup completes, you are redirected to the login page. Sign in with the credentials you just created.
-{% endstep %}
-{% endstepper %}
-
-The first user is automatically a **Super Admin** with full platform access.
-
-## Credentials authentication
-
-By default, users log in with an email address and password. Passwords are hashed with bcrypt before storage.
-
-When a Super Admin creates a new user, VectorFlow generates a random temporary password. The new user must change their password on first login.
-
-## OIDC / SSO
-
-VectorFlow supports any OpenID Connect provider (Okta, Entra ID, Keycloak, Google Workspace, Auth0, etc.). SSO is configured from the Settings page by a Super Admin.
-
-{% stepper %}
-{% step %}
-### Register VectorFlow with your identity provider
-Create an OAuth2/OIDC application in your identity provider. Use the following redirect URI:
-
-```
-https://your-vectorflow-url/api/auth/callback/oidc
-```
-{% endstep %}
-{% step %}
-### Open Settings in VectorFlow
-Navigate to **Settings > Authentication** (Super Admin required).
-{% endstep %}
-{% step %}
-### Enter provider details
-Fill in the OIDC configuration:
-
-| Field | Description | Example |
-|-------|-------------|---------|
-| Issuer URL | OIDC discovery endpoint | `https://accounts.google.com` |
-| Client ID | OAuth2 client ID from your provider | `abc123.apps.googleusercontent.com` |
-| Client Secret | OAuth2 client secret | `GOCSPX-xxxxxxxxxxxx` |
-| Display Name | Button label on the login page | `Sign in with Okta` |
-| Token Auth Method | How the client authenticates to the token endpoint | `client_secret_post` (default) or `client_secret_basic` |
-
-{% endstep %}
-{% step %}
-### Test the connection
-Click **Test Connection** to verify VectorFlow can reach your provider's discovery endpoint. The test fetches `/.well-known/openid-configuration` and validates that required fields are present.
-{% endstep %}
-{% step %}
-### Save and verify
-Save the settings. An SSO button will appear on the login page. Test the flow by logging in with an SSO account.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="info" %}
-OIDC settings are stored encrypted in the database. The client secret is encrypted with AES-256-GCM before storage.
-{% endhint %}
-
-### Group mapping
-
-VectorFlow can automatically assign users to teams based on their identity provider group memberships. Group mappings are configured from **Settings > Team & Role Mapping** and are **shared between OIDC and SCIM** — the same mapping table drives both protocols.
-
-Group sync is **off by default** and must be explicitly enabled. When enabled, VectorFlow operates in one of two modes depending on whether SCIM is active:
-
-{% tabs %}
-{% tab title="OIDC-only mode (SCIM disabled)" %}
-Groups are read from the OIDC token on each login. Team memberships are **reconciled** — users are added to mapped teams **and removed** from teams they no longer belong to (based on the groups present in the token). Changes to group mappings in **Settings > Team & Role Mapping** take effect on the user's next login.
-{% endtab %}
-{% tab title="SCIM + OIDC mode (SCIM enabled)" %}
-SCIM is the **primary lifecycle manager** for group memberships. Your IdP pushes group membership changes (create, update, remove) via SCIM, and VectorFlow tracks them internally. OIDC login acts as a **real-time refresh**, using the union of SCIM group data and token groups to reconcile team memberships. Changes to group mappings in **Settings > Team & Role Mapping** take effect immediately for all SCIM-managed users.
-{% endtab %}
-{% endtabs %}
-
-{% hint style="info" %}
-**Manual assignments are preserved.** Team memberships assigned manually in the VectorFlow UI are never modified by automated group sync. If you want group sync to fully manage a user's membership on a team, remove the manual assignment first.
-{% endhint %}
-
-{% hint style="info" %}
-**Highest role wins.** When a user belongs to multiple IdP groups that map to the same VectorFlow team, the highest role is used (Admin > Editor > Viewer).
-{% endhint %}
-
-{% stepper %}
-{% step %}
-### Enable group sync
-Toggle **Enable Group Sync** on. This tells VectorFlow to process group-to-team mappings. When enabled, OIDC logins read group claims from the token, and SCIM group pushes use the same mapping table to assign team memberships.
-{% endstep %}
-{% step %}
-### Configure scope and claim
-
-| Field | Default | Description |
-|-------|---------|-------------|
-| Groups Scope | `groups` | Extra OIDC scope to request from the provider. Leave empty if your provider includes groups automatically (e.g., Azure AD, Cognito). |
-| Groups Claim | `groups` | Token claim containing group names (e.g., `groups`, `cognito:groups`, `roles`). |
-
-{% hint style="info" %}
-**Scope** is what you ask the provider for in the authorization URL. **Claim** is where you read the groups from in the returned token. These are usually the same value (e.g., `groups` for Keycloak, Pocket ID, Authentik), but some providers differ — for example, AWS Cognito uses no extra scope but returns groups under the `cognito:groups` claim.
-{% endhint %}
-{% endstep %}
-{% step %}
-### Add group mappings
-Map identity provider groups to VectorFlow teams with specific roles. These mappings apply to both OIDC sign-ins and SCIM group pushes — you only need to configure them once.
-
-| Column | Description |
-|--------|-------------|
-| Group Name | The group name as it appears in the OIDC token or SCIM Group displayName |
-| Team(s) | One or more VectorFlow teams to assign the user to. Click the team selector to open a dropdown with checkboxes — select multiple teams to map a single group to several teams with the same role. When multiple teams are selected, the field shows a count (e.g., "3 teams") that you can click to view or modify the selection. |
-| Role | The role to assign: Viewer, Editor, or Admin |
-{% endstep %}
-{% step %}
-### Set defaults
-Configure a **Default Team** and **Default Role** as a fallback. If a user logs in and has no group matches (no IdP groups map to any VectorFlow team), they are assigned to the default team with the default role.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="warning" %}
-Changing group sync settings takes effect immediately — the OIDC provider configuration is rebuilt without requiring a server restart. In SCIM+OIDC mode, mapping changes are applied to all SCIM-managed users at save time. In OIDC-only mode, changes take effect on each user's next login.
-{% endhint %}
-
-## SCIM provisioning
-
-VectorFlow supports SCIM 2.0 for automated user provisioning and deprovisioning from your identity provider. When SCIM is enabled, your IdP can automatically create, update, and deactivate VectorFlow user accounts.
-
-SCIM is configured from **Settings > SCIM** by a Super Admin. You will need to generate a bearer token and enter it along with the SCIM base URL (`{your-vectorflow-url}/api/scim/v2`) into your IdP's SCIM configuration.
-
-{% hint style="info" %}
-SCIM group provisioning does **not** create teams automatically. Instead, SCIM groups are resolved through the shared [group mapping table](#group-mapping) to assign users to existing teams. Make sure your group mappings are configured before enabling SCIM group pushes.
-{% endhint %}
-
-{% hint style="info" %}
-SCIM works best alongside OIDC/SSO. Users created via SCIM should authenticate through your identity provider rather than with local credentials. See the [SCIM Provisioning](scim.md) page for detailed setup instructions and IdP-specific guides.
-{% endhint %}
-
-## Two-factor authentication (2FA)
-
-VectorFlow supports TOTP-based two-factor authentication compatible with any authenticator app (Google Authenticator, Authy, 1Password, etc.).
-
-### Enabling 2FA
-
-Users can enable 2FA from their **Profile** page. Teams can also require 2FA for all members -- when enabled, users who have not set up 2FA are redirected to the enrollment page on their next login.
-
-{% stepper %}
-{% step %}
-### Start 2FA setup
-Navigate to your **Profile** page and click **Enable Two-Factor Authentication**, or you will be redirected automatically if your team requires it.
-{% endstep %}
-{% step %}
-### Scan the QR code
-Scan the displayed QR code with your authenticator app. Alternatively, enter the secret key manually.
-{% endstep %}
-{% step %}
-### Save backup codes
-VectorFlow generates **10 single-use backup codes**. Save these in a secure location. Each code can be used once to log in if you lose access to your authenticator app.
-{% endstep %}
-{% step %}
-### Verify
-Enter the 6-digit code from your authenticator app to confirm setup. 2FA is now active on your account.
-{% endstep %}
-{% endstepper %}
-
-### Logging in with 2FA
-
-After entering your email and password, you are prompted for a 6-digit verification code. Enter the code from your authenticator app, or use one of your backup codes.
-
-{% hint style="info" %}
-2FA is only available for local (credentials) accounts. OIDC/SSO users should configure MFA through their identity provider.
-{% endhint %}
-
-## User management
-
-Super Admins can manage users from the **Settings > Users** page.
-
-### Creating users
-
-Super Admins can create new local user accounts. VectorFlow generates a random temporary password that must be shared with the user securely. The user is required to change their password on first login.
-
-When creating a user, you can optionally assign them to a team with a specific role immediately.
-
-{% hint style="info" %}
-When SCIM provisioning or OIDC group sync is enabled, a banner appears in the Add Member dialog: *"SSO users are managed by your identity provider. Only local users can be added manually."* SSO-managed users should be provisioned through your IdP rather than created here.
-{% endhint %}
-
-### Managing users
-
-| Action | Description |
-|--------|-------------|
-| **Assign to team** | Add a user to a team with a specified role (Viewer, Editor, or Admin) |
-| **Remove from team** | Remove a user's membership from a specific team |
-| **Reset password** | Generate a new temporary password (local accounts only) |
-| **Lock account** | Prevent the user from logging in. Locked users see an error on the login page |
-| **Unlock account** | Restore login access for a locked account |
-| **Toggle Super Admin** | Grant or revoke platform-wide Super Admin privileges |
-| **Delete user** | Permanently remove the user and all their data |
-
-{% hint style="warning" %}
-You cannot delete your own account, remove your own Super Admin status, or lock your own account.
-{% endhint %}
-
-## Roles and permissions
-
-VectorFlow uses a hierarchical role system. Roles are assigned **per team**, so a user can be an Admin in one team and a Viewer in another.
-
-| Role | Permissions |
-|------|-------------|
-| **Viewer** | View pipelines, fleet status, dashboards, and audit logs. Cannot make changes. |
-| **Editor** | Everything a Viewer can do, plus: create/edit/delete pipelines, manage secrets and certificates, deploy pipelines, manage alerts. |
-| **Admin** | Everything an Editor can do, plus: manage environments, manage team members and roles, configure team settings (e.g., require 2FA). |
-| **Super Admin** | Platform-wide access. Can manage all teams, configure system settings (OIDC, fleet, backups), create and delete users. Super Admin is a flag on the user, not a team role. |
-
-### Role hierarchy
-
-```
-Viewer < Editor < Admin < Super Admin
- (0) (1) (2) (bypass)
-```
-
-Higher roles inherit all permissions from lower roles. Super Admin bypasses all team-level access checks.
-
-## SSO-only mode
-
-To enforce SSO-only authentication and hide the local login form, set the environment variable:
-
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `VF_DISABLE_LOCAL_AUTH` | `false` | When `true`, hides the email/password form and only shows the SSO button on the login page |
-
-When SSO-only mode is active, the login page shows a simplified layout:
-
-- A centered card with a shield icon
-- The heading **"Sign in"**
-- The message *"Use your organization's single sign-on to access your account."*
-- A single button: **"Sign in with [Provider Name]"** (using the display name configured in OIDC settings)
-
-The email/password form and all local authentication options are hidden.
-
-{% hint style="warning" %}
-Before enabling SSO-only mode, ensure your OIDC provider is correctly configured and tested. The only way to re-enable local login is to change the environment variable and restart the server.
-{% endhint %}
-
-{% hint style="info" %}
-SSO-only mode acts as a breakglass mechanism -- if SSO breaks, an administrator can re-enable local login by setting `VF_DISABLE_LOCAL_AUTH=false` and restarting the container.
-{% endhint %}
-
-## SSO-managed roles
-
-When users authenticate via OIDC or are provisioned via SCIM, their team roles are managed by the identity provider. Admins cannot change roles for SSO-managed users in the VectorFlow UI -- the role dropdown is locked with a tooltip indicating the role is managed by the identity provider.
-
-Role updates happen:
-
-- **On login** -- OIDC group claims are reconciled against team mappings. The user is added to newly matched teams, removed from teams they no longer match, and roles are updated to reflect the highest mapped role.
-- **Via SCIM** -- When SCIM group membership changes are pushed, team memberships are reconciled immediately based on team mappings. Removals cascade correctly — if a user is removed from their only group mapping for a team, they are removed from that team.
-
-{% hint style="info" %}
-Manual team assignments (made in the UI) are not affected by SSO-managed role sync. Only memberships created by group sync are subject to reconciliation.
-{% endhint %}
-
-## Users without team assignments
-
-When a user authenticates successfully (via SSO or local credentials) but is not a member of any team, VectorFlow displays a full-page message instead of the normal dashboard:
-
-- A **"No Team Assigned"** heading with a shield icon
-- A message: *"Your account is active but you haven't been assigned to a team yet. Contact your administrator to get access."*
-- The user's identity (name or email)
-- A **Sign Out** button
-
-This typically occurs when:
-- A new SSO user logs in before group mappings or manual team assignments are configured
-- A SCIM-provisioned user has not been assigned to any group that maps to a VectorFlow team
-- An admin removes a user from all teams
-
-{% hint style="info" %}
-The user can still access the password change dialog if their account requires it. Once an admin assigns them to a team (or group sync places them on one), the full dashboard becomes available on next login or page refresh.
-{% endhint %}
diff --git a/docs/public/operations/backup-restore.md b/docs/public/operations/backup-restore.md
deleted file mode 100644
index e21f3949..00000000
--- a/docs/public/operations/backup-restore.md
+++ /dev/null
@@ -1,464 +0,0 @@
-# Backup & Restore
-
-VectorFlow includes built-in database backup and restore functionality. Backups capture the entire PostgreSQL database, including all pipelines, environments, users, secrets, audit history, and system settings.
-
-## What gets backed up
-
-Backups are full PostgreSQL dumps in compressed custom format (`pg_dump --format=custom`). Everything stored in the database is included:
-
-- Pipeline definitions and version history
-- Environments, teams, and user accounts
-- Encrypted secrets and certificates
-- Agent node registrations
-- Alert rules and webhook configurations
-- Audit log entries
-- System settings (OIDC, fleet, backup schedule)
-
-{% hint style="info" %}
-Backups do **not** include the Vector data directory (`/var/lib/vector/`) on agent nodes. Vector's internal state (e.g., file checkpoints, disk buffers) is managed by each agent independently.
-{% endhint %}
-
-## Integrity verification
-
-Every backup includes a SHA256 checksum computed after the database dump completes. Checksums are stored in the VectorFlow database alongside backup metadata.
-
-When you restore a backup, VectorFlow automatically verifies the checksum before applying it:
-
-- **Checksum matches** -- Restore proceeds normally
-- **Checksum mismatch** -- Restore is blocked with an error message indicating the file may be corrupt
-
-{% hint style="info" %}
-Backups created before this feature was added (legacy backups) do not have stored checksums. VectorFlow skips checksum verification for these backups and proceeds with the restore.
-{% endhint %}
-
-## Remote Storage (S3)
-
-VectorFlow can store backups in any S3-compatible storage service, including AWS S3, MinIO, DigitalOcean Spaces, and Backblaze B2.
-
-### Configuring S3 storage
-
-1. Navigate to **Settings > Backups**
-2. Toggle the storage backend from **Local** to **S3**
-3. Fill in the required fields:
- - **Bucket** -- the S3 bucket name
- - **Region** -- the AWS region (e.g., `us-east-1`)
- - **Access Key ID** -- IAM access key with S3 permissions
- - **Secret Access Key** -- corresponding secret key (stored encrypted)
-4. Optional fields:
- - **Prefix** -- key prefix for organizing backups (e.g., `backups/vectorflow`)
- - **Endpoint URL** -- custom endpoint for MinIO or other S3-compatible services
-5. Click **Test Connection** to verify bucket access and write permissions
-6. Click **Save Storage Settings**
-
-### How it works
-
-- When S3 is configured, backups are uploaded to the S3 bucket immediately after creation. The local dump file is deleted after a successful upload to prevent disk exhaustion.
-- Each backup's storage location is recorded in the database (`s3://bucket/key` for S3, local path for disk).
-- Restoring from an S3-stored backup downloads the file temporarily, runs `pg_restore`, then deletes the temporary file.
-- The backup table shows a cloud icon for S3-stored backups and a disk icon for local backups.
-- Switching from S3 back to Local keeps your S3 credentials saved -- you can switch back without re-entering them.
-
-### Required S3 permissions
-
-The IAM user or role needs the following permissions on the target bucket:
-
-- `s3:HeadBucket` (connection test)
-- `s3:PutObject` (upload backups)
-- `s3:GetObject` (download for restore)
-- `s3:DeleteObject` (delete backups, cleanup test objects)
-- `s3:HeadObject` (check if backup exists)
-
-### MinIO and S3-compatible services
-
-For self-hosted S3-compatible services like MinIO, set the **Endpoint URL** field to the service address (e.g., `https://minio.internal:9000`). VectorFlow automatically enables path-style addressing when a custom endpoint is set.
-
-## Automatic backups
-
-VectorFlow can run backups on a cron schedule with automatic retention cleanup.
-
-### Configuring the schedule
-
-Navigate to **Settings > Backup** (Super Admin required) to configure:
-
-| Setting | Default | Description |
-|---------|---------|-------------|
-| Enabled | Off | Toggle automatic backups on or off |
-| Cron Schedule | `0 2 * * *` | Standard cron expression. Default runs at 2:00 AM daily |
-| Retention Count | 7 | Number of backups to keep. Older backups are automatically deleted |
-
-**Common cron schedules:**
-
-| Schedule | Cron Expression |
-|----------|----------------|
-| Every day at 2:00 AM | `0 2 * * *` |
-| Every 6 hours | `0 */6 * * *` |
-| Every day at midnight | `0 0 * * *` |
-| Every Sunday at 3:00 AM | `0 3 * * 0` |
-| Every weekday at 1:00 AM | `0 1 * * 1-5` |
-
-After each scheduled backup completes, VectorFlow automatically runs retention cleanup to delete the oldest backups beyond the configured retention count.
-
-### Monitoring backup health
-
-The backup settings page shows the status of the most recent backup attempt:
-
-- **Success** -- The last backup completed without errors. The timestamp and file size are displayed.
-- **Failed** -- A red error banner appears at the top of the page showing the error message from the failed backup attempt (e.g., `pg_dump` timeout, disk full, permission denied). The banner persists until the next successful backup.
-
-Failed backups are also visible in the backup list with a red **Failed** status badge and their error details. You can delete failed backup entries to clean them up. Download and Restore actions are only available for successful backups.
-
-{% hint style="warning" %}
-If automatic backups are enabled but consistently failing, the error banner provides the diagnostic message needed to troubleshoot. Common causes include insufficient disk space in `VF_BACKUP_DIR`, PostgreSQL connection issues, or `pg_dump` not being available in the container.
-{% endhint %}
-
-### Orphan cleanup
-
-VectorFlow automatically detects and handles orphaned backup entries:
-
-- **Files without database records** -- If a `.dump` file exists in the backup directory but has no matching database entry, it is automatically deleted during the next scheduled cleanup cycle.
-- **Database records without files** -- If a backup record points to a file that no longer exists (locally or in S3), the record is marked as **Orphaned** in the backup list. Orphaned entries remain visible so operators can see what happened, and can be manually deleted.
-
-Orphan cleanup runs alongside retention cleanup after each scheduled backup. No manual configuration is needed.
-
-## Manual backup
-
-You can trigger a backup at any time from the **Settings > Backup** page by clicking **Create Backup**. The backup runs immediately and appears in the backup list when complete.
-
-The backup list is database-backed and shows all backups — both scheduled and manual — with their type, status, size, and duration. Backups persist across page refreshes and server restarts.
-
-Each backup generates two files:
-- `vectorflow-.dump` -- The compressed PostgreSQL dump
-- `vectorflow-.meta.json` -- Metadata (VectorFlow version, migration count, PostgreSQL version, file size)
-
-{% hint style="info" %}
-When upgrading from a version before v1.2, any existing backup files in `VF_BACKUP_DIR` are automatically imported into the database on first startup. No manual action is required — your existing backups will appear in the list automatically.
-{% endhint %}
-
-## Downloading backups
-
-Super Admins can download backup `.dump` files directly from the **Settings > Backup** page.
-
-Each row in the backup list includes a **Download** button. Clicking it streams the compressed dump file to your browser. Downloaded files can be used for:
-
-- Offline archival storage
-- Restoring on a different VectorFlow instance via the CLI `pg_restore` procedure
-- Disaster recovery from a separate machine
-
-{% hint style="info" %}
-The download button is only visible to Super Admins. The download streams the file directly from the server's backup directory — no temporary copies are created.
-{% endhint %}
-
-## Backup storage
-
-Backups are stored on the server's local filesystem in the directory configured by the `VF_BACKUP_DIR` environment variable (default: `/backups`).
-
-In the Docker Compose setup, this directory is mounted as a Docker volume:
-
-```yaml
-volumes:
- - backups:/backups
-```
-
-{% hint style="warning" %}
-For production deployments, consider mounting `VF_BACKUP_DIR` to a location that is backed up by your infrastructure-level backup system (e.g., an NFS share, or a directory included in your host backup schedule).
-{% endhint %}
-
-## Restore procedure
-
-Restoring from a backup replaces the entire database with the contents of the backup file.
-
-{% hint style="danger" %}
-**Restoring a backup overwrites all current data.** All pipelines, users, secrets, and settings will be replaced with the state from the backup. VectorFlow shows a preview of the backup contents and requires a typed confirmation before proceeding. A safety backup is created automatically before restoring.
-{% endhint %}
-
-### Restore from the UI
-
-{% stepper %}
-{% step %}
-### Navigate to Settings > Backup
-Open the backup management page (Super Admin required).
-{% endstep %}
-{% step %}
-### Click Restore on a backup
-Find the backup you want to restore in the list and click the **Restore** button. A preview dialog opens showing the backup's metadata.
-{% endstep %}
-{% step %}
-### Review the preview
-The preview shows:
-- **VectorFlow version** and **migration level** from when the backup was created
-- **PostgreSQL version** used for the dump
-- **Backup size** and **creation date**
-- **Tables present** in the dump file
-
-This information helps you verify you are restoring the correct backup.
-{% endstep %}
-{% step %}
-### Confirm the restore
-Click **Continue to Confirmation**, then type `RESTORE` in the confirmation field and click **Restore Database**. VectorFlow will:
-1. Validate version compatibility (blocks if the backup has more migrations than the current version)
-2. Verify the backup file checksum
-3. Create a safety backup of the current database
-4. Run `pg_restore --clean --if-exists` to replace the database
-{% endstep %}
-{% step %}
-### Restart the application
-After restore completes, the dialog shows a success message. Restart the application for all changes to take full effect. If running in Docker, restart the container. Database migrations run automatically on startup.
-{% endstep %}
-{% endstepper %}
-
-### Manual restore (CLI)
-
-If you cannot access the UI, you can restore directly using `pg_restore`:
-
-```bash
-# Stop the VectorFlow server first
-docker compose stop vectorflow
-
-# Restore the backup
-docker compose exec postgres pg_restore \
- --clean --if-exists \
- -U vectorflow -d vectorflow \
- /backups/vectorflow-2025-01-15T02-00-00-000Z.dump
-
-# Restart the server (migrations run automatically)
-docker compose start vectorflow
-```
-
-## Version compatibility
-
-VectorFlow tracks the number of database migrations in each backup's metadata. When restoring:
-
-- **Same version or older backup → newer server**: Works. Migrations run automatically on startup to bring the schema up to date.
-- **Newer backup → older server**: Blocked. If the backup contains more migrations than the current server version, the restore is rejected. Upgrade VectorFlow first, then restore.
-
-## Recommended backup strategy
-
-1. **Enable automatic daily backups** with a retention count of at least 7.
-2. **Mount the backup directory** to storage that is included in your infrastructure backup system.
-3. **Test restores periodically** in a staging environment to verify your backups are valid.
-4. **Create a manual backup** before upgrading VectorFlow or making major configuration changes.
-5. **Monitor backup status** on the Settings page. Failed backups are logged with error details.
-6. **Check server logs for disk space warnings.** Before each backup, VectorFlow checks available disk space in `VF_BACKUP_DIR` and logs a warning if it drops below the configured threshold (default: 500 MB). Configure the threshold with the `VF_BACKUP_DISK_WARN_MB` environment variable.
-
-## Recovery targets (RTO/RPO)
-
-Recovery Point Objective (RPO) defines the maximum acceptable data loss. Recovery Time Objective (RTO) defines the maximum acceptable downtime during recovery.
-
-### Default targets
-
-| Metric | Default Target | Basis |
-|--------|---------------|-------|
-| **RPO** (max data loss) | 24 hours | Default daily backup schedule (`0 2 * * *`) |
-| **RTO** (time to recover) | < 15 minutes | pg_restore of < 1 GB database + container startup |
-
-{% hint style="info" %}
-These defaults assume the standard daily backup schedule and a database under 1 GB. Adjust targets based on your backup frequency and database size using the framework below.
-{% endhint %}
-
-### Calculating your targets
-
-**RPO formula:**
-
-```
-RPO = backup_interval + backup_duration + transfer_time
-```
-
-- **backup_interval** — time between scheduled backups (e.g., 24h for daily, 6h for `0 */6 * * *`)
-- **backup_duration** — time to complete `pg_dump` (typically seconds for < 1 GB)
-- **transfer_time** — S3 upload time (0 for local storage)
-
-Data created after the last successful backup and before a failure is at risk.
-
-**RTO formula:**
-
-```
-RTO = download_time + restore_time + app_restart + smoke_test
-```
-
-- **download_time** — time to retrieve backup from S3 (0 for local storage)
-- **restore_time** — `pg_restore` duration (see estimates below)
-- **app_restart** — VectorFlow startup + automatic migration run (~30s typical)
-- **smoke_test** — manual verification of application health (~2-5 min)
-
-### Size-based RTO estimates
-
-| Database Size | Local Restore | S3 Restore (100 Mbps) |
-|--------------|---------------|----------------------|
-| 100 MB | ~1 min | ~2 min |
-| 500 MB | ~3 min | ~5 min |
-| 1 GB | ~5 min | ~8 min |
-| 5 GB | ~15 min | ~25 min |
-
-{% hint style="info" %}
-These estimates include `pg_restore` time and application restart. Actual times depend on disk speed, CPU, and network throughput. Run `scripts/dr-verify.sh` to benchmark your environment.
-{% endhint %}
-
-### Reducing RPO
-
-Increase backup frequency by changing the cron schedule:
-
-| Schedule | Cron Expression | RPO |
-|----------|----------------|-----|
-| Every 24 hours (default) | `0 2 * * *` | 24h |
-| Every 12 hours | `0 2,14 * * *` | 12h |
-| Every 6 hours | `0 */6 * * *` | 6h |
-| Every hour | `0 * * * *` | 1h |
-
-{% hint style="warning" %}
-More frequent backups increase storage usage and I/O load. Adjust the retention count accordingly and monitor disk space warnings in the server logs.
-{% endhint %}
-
-### Reducing RTO
-
-- **Keep local backup copies** alongside S3 to eliminate download time
-- **Use faster storage** (SSD) for the backup directory
-- **Pre-provision a standby PostgreSQL** instance to eliminate container startup time
-- **Automate the runbook** using `scripts/dr-verify.sh` as a starting point
-
-## Disaster recovery runbook
-
-Step-by-step procedure for recovering VectorFlow from a backup after a database failure, corruption, or full system loss.
-
-{% stepper %}
-{% step %}
-### Assess the situation
-
-Determine the failure type:
-- **Database corruption** — PostgreSQL data is damaged but the server host is intact
-- **Hardware failure** — the database host is lost; VectorFlow server may still be running
-- **Full system loss** — both VectorFlow and PostgreSQL are unavailable
-
-This determines whether you restore in-place or provision new infrastructure.
-{% endstep %}
-
-{% step %}
-### Identify the most recent good backup
-
-Check the backup list in **Settings > Backup** (if the UI is accessible) or list files in the backup directory:
-
-```bash
-ls -lt /backups/*.dump | head -5
-```
-
-For S3-stored backups, list objects in the bucket:
-
-```bash
-aws s3 ls s3://your-bucket/your-prefix/ --recursive | sort -r | head -5
-```
-
-Verify the backup checksum before proceeding:
-
-```bash
-sha256sum /backups/vectorflow-2026-01-15T02-00-00-000Z.dump
-```
-
-Compare against the checksum in the corresponding `.meta.json` file or the `BackupRecord` in the database.
-{% endstep %}
-
-{% step %}
-### Provision PostgreSQL
-
-If the existing PostgreSQL instance is recoverable, skip to the next step.
-
-For a new instance, use the same image as production:
-
-```bash
-docker run -d \
- --name vectorflow-postgres \
- -e POSTGRES_DB=vectorflow \
- -e POSTGRES_USER=vectorflow \
- -e POSTGRES_PASSWORD= \
- -v pgdata:/var/lib/postgresql/data \
- timescale/timescaledb:latest-pg16
-```
-
-Wait for it to be ready:
-
-```bash
-docker exec vectorflow-postgres pg_isready -U vectorflow
-```
-{% endstep %}
-
-{% step %}
-### Restore the backup
-
-Stop the VectorFlow server first to prevent writes during restore:
-
-```bash
-docker compose stop vectorflow
-```
-
-Run `pg_restore`:
-
-```bash
-docker compose exec postgres pg_restore \
- --clean --if-exists \
- -U vectorflow -d vectorflow \
- /backups/vectorflow-2026-01-15T02-00-00-000Z.dump
-```
-
-For S3-stored backups, download first:
-
-```bash
-aws s3 cp s3://your-bucket/your-prefix/vectorflow-2026-01-15T02-00-00-000Z.dump /tmp/
-docker cp /tmp/vectorflow-2026-01-15T02-00-00-000Z.dump vectorflow-postgres:/tmp/
-docker exec -e PGPASSWORD= vectorflow-postgres \
- pg_restore --clean --if-exists \
- -U vectorflow -d vectorflow /tmp/vectorflow-2026-01-15T02-00-00-000Z.dump
-```
-{% endstep %}
-
-{% step %}
-### Verify the restore
-
-Run the automated DR verification script:
-
-```bash
-./scripts/dr-verify.sh /backups/vectorflow-2026-01-15T02-00-00-000Z.dump
-```
-
-Or verify manually:
-
-```bash
-docker compose exec postgres psql -U vectorflow -d vectorflow -c "SELECT count(*) FROM \"User\";"
-docker compose exec postgres psql -U vectorflow -d vectorflow -c "SELECT count(*) FROM \"Pipeline\";"
-docker compose exec postgres psql -U vectorflow -d vectorflow -c "SELECT count(*) FROM \"_prisma_migrations\" WHERE finished_at IS NOT NULL;"
-```
-{% endstep %}
-
-{% step %}
-### Restart VectorFlow
-
-```bash
-docker compose start vectorflow
-```
-
-Database migrations run automatically on startup. Check the logs for migration output:
-
-```bash
-docker compose logs vectorflow --tail=50
-```
-{% endstep %}
-
-{% step %}
-### Validate application health
-
-1. **Login** — verify authentication works
-2. **Pipeline list** — confirm pipelines are visible and match expected state
-3. **Agent connectivity** — check the Fleet page for connected agents
-4. **Settings** — verify system settings, backup schedule, and S3 configuration
-
-{% endstep %}
-
-{% step %}
-### Post-incident review
-
-- Was the RPO exceeded? If data loss was unacceptable, increase backup frequency.
-- Was the RTO exceeded? If recovery took too long, consider keeping local backup copies or pre-provisioning standby infrastructure.
-- Update this runbook with any lessons learned.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="info" %}
-**Automated DR verification:** Run `scripts/dr-verify.sh` periodically (or via CI) to confirm your backups remain restorable without waiting for a real incident. See the [recommended backup strategy](#recommended-backup-strategy) section above.
-{% endhint %}
diff --git a/docs/public/operations/configuration.md b/docs/public/operations/configuration.md
deleted file mode 100644
index 963930e1..00000000
--- a/docs/public/operations/configuration.md
+++ /dev/null
@@ -1,190 +0,0 @@
-# Configuration
-
-VectorFlow is configured through environment variables (for the server and agents) and through the Settings page in the UI (for fleet tuning, OIDC, and backups).
-
-## Server environment variables
-
-### Required
-
-{% hint style="warning" %}
-These variables must be set before the server can start. Without them, the application will fail to launch.
-{% endhint %}
-
-| Variable | Description | Example |
-|----------|-------------|---------|
-| `DATABASE_URL` | PostgreSQL connection string | `postgresql://vectorflow:pass@localhost:5432/vectorflow` |
-| `NEXTAUTH_SECRET` | Session encryption key (min 32 characters) | Output of `openssl rand -base64 32` |
-
-{% hint style="danger" %}
-`NEXTAUTH_SECRET` is used to encrypt sessions, TOTP secrets, stored credentials, and all sensitive values in the database. Use a strong, random value and keep it safe. If you lose this key, all encrypted data becomes unrecoverable.
-{% endhint %}
-
-### Optional
-
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `NEXTAUTH_URL` | *(inferred from Host header)* | Canonical server URL. Set this when running behind a reverse proxy (e.g., `https://vectorflow.example.com`) |
-| `PORT` | `3000` | HTTP listen port |
-| `NODE_ENV` | `production` | Set automatically in Docker. Use `production` for standalone deployments |
-| `VF_BACKUP_DIR` | `/backups` | Directory for database backup files |
-| `VF_BACKUP_DISK_WARN_MB` | `500` | Disk space warning threshold in MB. Before each backup, available space in `VF_BACKUP_DIR` is checked. If below this value, a warning is logged |
-
-> S3 remote storage is configured through the Settings UI, not environment variables. See [Remote Storage (S3)](backup-restore.md#remote-storage-s3).
-| `VF_DISABLE_LOCAL_AUTH` | `false` | Hide the local login form and enforce SSO-only authentication. Requires OIDC to be configured |
-| `REDIS_URL` | *(none — single-instance mode)* | Redis connection string for HA mode (e.g., `redis://redis:6379`). Enables leader election, cross-instance SSE broadcast, and metric distribution. When unset, VectorFlow runs as a single instance with no behavioral change |
-| `VF_LOG_LEVEL` | `info` | Server log verbosity: `debug`, `info`, `warn`, `error`. Set to `debug` for verbose logging of OIDC authentication, SCIM provisioning, agent enrollment, and group sync operations |
-
-### Docker Compose variables
-
-When using the Docker Compose setup, these variables go in your `.env` file and are interpolated into the Compose file:
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `POSTGRES_PASSWORD` | Yes | -- | Password for the PostgreSQL `vectorflow` user |
-| `VF_VERSION` | No | `latest` | Docker image tag to pull |
-
-## Agent environment variables
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `VF_URL` | Yes | -- | VectorFlow server URL (e.g., `https://vectorflow.example.com`) |
-| `VF_TOKEN` | First run only | -- | Enrollment token from the environment detail page. Only needed for initial registration |
-| `VF_DATA_DIR` | No | `/var/lib/vf-agent` | Data directory for configs, tokens, and certificates |
-| `VF_VECTOR_BIN` | No | `vector` | Path to the Vector binary |
-| `VF_POLL_INTERVAL` | No | `15s` | How often the agent polls the server for changes |
-| `VF_LOG_LEVEL` | No | `info` | Logging verbosity: `debug`, `info`, `warn`, `error` |
-| `VF_AGENT_USER` | Docker only | — | Run the agent as this user instead of root. The entrypoint creates the user if needed. |
-
-## Database connection
-
-VectorFlow requires PostgreSQL 17 or later. The connection is configured via `DATABASE_URL`.
-
-**Connection string format:**
-
-```
-postgresql://[user]:[password]@[host]:[port]/[database]?[options]
-```
-
-**Common options:**
-
-| Option | Description |
-|--------|-------------|
-| `sslmode=require` | Enforce TLS for the database connection |
-| `connection_limit=10` | Limit the Prisma connection pool size |
-
-## Example `.env` file
-
-### Server (Docker Compose)
-
-```bash
-# Required
-POSTGRES_PASSWORD=my-strong-database-password
-NEXTAUTH_SECRET=Kj8mN2pQ4rT6vX9zA1cE3fG5hI7jL0nO2qR4sU6wY8
-
-# Optional
-NEXTAUTH_URL=https://vectorflow.example.com
-VF_VERSION=latest
-```
-
-### Agent
-
-```bash
-# Required
-VF_URL=https://vectorflow.example.com
-
-# Only for first enrollment
-VF_TOKEN=env_abc123_enrollment_token
-
-# Optional
-VF_DATA_DIR=/var/lib/vf-agent
-VF_VECTOR_BIN=/usr/bin/vector
-VF_POLL_INTERVAL=15s
-VF_LOG_LEVEL=info
-```
-
-## System settings (UI)
-
-The following settings are configured through the **Settings** page in the VectorFlow UI. These values are stored in the database and take effect immediately.
-
-### Settings navigation
-
-The Settings page has its own dedicated sidebar navigation, separate from the main application sidebar. When you click **Settings** in the main navigation, the sidebar transitions to show the settings menu organized into four sections:
-
-| Section | Pages | Visibility |
-|---------|-------|------------|
-| **System** | Fleet, Backup | Super Admin only |
-| **Security** | Authentication, SCIM | Super Admin only |
-| **Organization** | Team, Users, Service Accounts | Team: Admin+, Users: Super Admin, Service Accounts: Admin+ |
-| **Operations** | Audit | Admin+ |
-
-Click the back arrow at the top of the settings sidebar to return to the main navigation. The transition between the main sidebar and settings sidebar is animated for a smooth experience.
-
-{% hint style="info" %}
-Team admins see a subset of the settings pages (Team, Service Accounts, Audit). Super admins see all settings pages. Viewers and editors do not have access to the Settings page.
-{% endhint %}
-
-### Fleet settings
-
-| Setting | Default | Range | Description |
-|---------|---------|-------|-------------|
-| Poll Interval | 15,000 ms | 1,000--300,000 | How frequently agents check in with the server |
-| Unhealthy Threshold | 3 | 1--100 | Number of missed heartbeats before an agent is marked **Unreachable** |
-| Metrics Retention | 7 days | 1--365 | How long node and pipeline metrics are kept |
-| Logs Retention | 3 days | 1--30 | How long pipeline logs are kept |
-
-### Backup settings
-
-| Setting | Default | Description |
-|---------|---------|-------------|
-| Enabled | Off | Toggle automatic scheduled backups |
-| Cron Schedule | `0 2 * * *` | Cron expression for backup timing (default: 2:00 AM daily) |
-| Retention Count | 7 | Number of backups to keep before deleting the oldest |
-
-For more details, see [Backup & Restore](backup-restore.md).
-
-### OIDC / SSO settings
-
-OIDC is configured in the Settings page under the **Authentication** tab. See [Authentication](authentication.md) for full setup instructions.
-
-## Prometheus metrics
-
-The `/api/metrics` endpoint exposes metrics in Prometheus exposition format. It requires a service account API token with the `metrics.read` permission.
-
-Create a service account in **Settings > Service Accounts** with `metrics.read` permission, then configure your Prometheus scrape config:
-
-```yaml
-scrape_configs:
- - job_name: vectorflow
- scheme: https
- metrics_path: /api/metrics
- bearer_token: "vf_your_service_account_key"
- static_configs:
- - targets: ["vectorflow.example.com"]
-```
-
-## Ports reference
-
-| Service | Default Port | Description |
-|---------|-------------|-------------|
-| VectorFlow Server | 3000 | Web UI and API |
-| PostgreSQL | 5432 | Database (not exposed externally in Docker) |
-| Vector API | 8686 | Vector GraphQL API (per node, managed by agent) |
-
-## File paths
-
-### Server
-
-| Path | Description |
-|------|-------------|
-| `/app/.vectorflow/` | Server data directory (Docker volume mount) |
-| `/backups/` | Database backup storage (Docker volume mount) |
-
-### Agent
-
-| Path | Description |
-|------|-------------|
-| `/var/lib/vf-agent/` | Agent data directory (default) |
-| `/var/lib/vf-agent/node-token` | Persistent authentication token (mode `0600`) |
-| `/var/lib/vf-agent/pipelines/` | Pipeline configuration files |
-| `/var/lib/vf-agent/certs/` | Deployed TLS certificates |
-| `/var/lib/vector/` | Vector data directory |
diff --git a/docs/public/operations/gitops.md b/docs/public/operations/gitops.md
deleted file mode 100644
index 32fdf0d9..00000000
--- a/docs/public/operations/gitops.md
+++ /dev/null
@@ -1,158 +0,0 @@
-# GitOps (Pipeline-as-Code)
-
-VectorFlow supports **pipeline-as-code** workflows where pipeline configurations are stored in a Git repository and kept in sync between VectorFlow and your version control system.
-
-## Supported Git Providers
-
-VectorFlow supports the following Git hosting providers:
-
-| Provider | Webhook Verification | API Operations |
-|----------|---------------------|----------------|
-| **GitHub** | HMAC-SHA256 (`X-Hub-Signature-256`) | Contents API, Pulls API |
-| **GitLab** | Shared secret (`X-Gitlab-Token`) | Repository Files API, Merge Requests API |
-| **Bitbucket** | HMAC-SHA256 (`X-Hub-Signature`) | Source API, Pullrequests API |
-
-The provider is **auto-detected** from the repository URL domain (e.g., `github.com`, `gitlab.com`, `bitbucket.org`). For self-hosted instances (e.g., `gitlab.internal.corp`), you can explicitly set the provider in the Git Integration settings.
-
-## Modes
-
-Each environment can operate in one of four GitOps modes:
-
-| Mode | Direction | Description |
-|------|-----------|-------------|
-| **Off** | -- | Git integration is disabled (default). |
-| **Push Only** | VectorFlow -> Git | Pipeline YAML is committed to the repo whenever you deploy or delete a pipeline. The repo serves as an audit trail. |
-| **Bi-directional** | VectorFlow <-> Git | In addition to push, a webhook from your Git provider triggers VectorFlow to import changed YAML files automatically. |
-| **Promotion** | VectorFlow -> Git -> VectorFlow | Promoting a pipeline creates a pull request (or merge request). Merging it automatically deploys the promoted config. |
-
-## Setting up Push Only
-
-Push-only mode commits pipeline YAML files to your Git repository every time you deploy or delete a pipeline.
-
-{% stepper %}
-{% step %}
-### Configure Git Integration
-On the environment detail page, fill in the **Git Integration** card:
-- **Repository URL** -- HTTPS URL of the target repo (e.g., `https://github.com/org/pipeline-configs.git`)
-- **Branch** -- The branch to push to (default: `main`)
-- **Access Token** -- A personal access token with write access
-- **Git Provider** -- Leave as "Auto-detect" for hosted providers, or explicitly select for self-hosted instances
-{% endstep %}
-{% step %}
-### Set GitOps Mode to Push Only
-In the **GitOps Mode** dropdown, select **Push Only**.
-{% endstep %}
-{% step %}
-### Save
-Click **Save**. You can verify connectivity with **Test Connection** before saving.
-{% endstep %}
-{% endstepper %}
-
-From this point forward, every pipeline deploy writes the generated YAML to `{environment-name}/{pipeline-name}.yaml` in the configured repository, and every pipeline deletion removes the file.
-
-{% hint style="info" %}
-Git sync is a post-deploy side effect. If the Git push fails, the pipeline deploy still succeeds -- VectorFlow automatically queues the failed sync for retry (up to 3 attempts with exponential backoff). You can monitor sync status in the **Git Sync Status** section on the environment page.
-{% endhint %}
-
-## Setting up Bi-directional GitOps
-
-Bi-directional mode adds a webhook so that pushes to the Git repository automatically import or update pipelines in VectorFlow.
-
-{% stepper %}
-{% step %}
-### Configure Git Integration
-On the environment detail page, fill in the **Repository URL**, **Branch**, and **Access Token** in the Git Integration card.
-{% endstep %}
-{% step %}
-### Set GitOps Mode to Bi-directional
-Select **Bi-directional** from the **GitOps Mode** dropdown and click **Save**. VectorFlow auto-generates a webhook secret.
-{% endstep %}
-{% step %}
-### Copy the webhook details
-After saving, the card shows:
-- **Webhook URL** -- The endpoint your Git provider should send push events to.
-- **Webhook Secret** -- The secret for signature verification.
-{% endstep %}
-{% step %}
-### Create a Webhook in your Git provider
-{% endstep %}
-{% endstepper %}
-
-{% tabs %}
-{% tab title="GitHub" %}
-In your GitHub repository, go to **Settings > Webhooks > Add webhook**:
-- **Payload URL** -- Paste the Webhook URL from VectorFlow.
-- **Content type** -- Select `application/json`.
-- **Secret** -- Paste the Webhook Secret from VectorFlow.
-- **Events** -- Select **Just the push event** (and **Pull requests** if using Promotion mode).
-{% endtab %}
-{% tab title="GitLab" %}
-In your GitLab project, go to **Settings > Webhooks > Add new webhook**:
-- **URL** -- Paste the Webhook URL from VectorFlow.
-- **Secret token** -- Paste the Webhook Secret from VectorFlow.
-- **Trigger** -- Check **Push events** (and **Merge request events** if using Promotion mode).
-{% endtab %}
-{% tab title="Bitbucket" %}
-In your Bitbucket repository, go to **Repository settings > Webhooks > Add webhook**:
-- **URL** -- Paste the Webhook URL from VectorFlow.
-- **Secret** -- Paste the Webhook Secret from VectorFlow.
-- **Triggers** -- Select **Repository push** (and **Pull request merged/declined** if using Promotion mode).
-{% endtab %}
-{% endtabs %}
-
-## How the import works
-
-When a push event arrives:
-
-1. VectorFlow verifies the webhook signature using the appropriate method for the Git provider.
-2. It checks that the push targets the configured branch.
-3. For each added or modified `.yaml` / `.yml` file in the push, it fetches the file content via the provider's API.
-4. The pipeline name is derived from the filename (e.g., `production/my-pipeline.yaml` becomes `my-pipeline`).
-5. If a pipeline with a matching `gitPath` or name already exists in the environment, its graph is replaced. Otherwise, a new pipeline is created.
-6. If the environment has **Require Deploy Approval** enabled, imported pipelines are saved as drafts with a pending deploy request instead of being deployed immediately.
-
-{% hint style="warning" %}
-Bi-directional mode means the Git repository is the source of truth. Any manual edits made in the VectorFlow UI may be overwritten on the next push to the repository. The pipeline editor shows a banner to remind users of this.
-{% endhint %}
-
-## Pipeline Name / Filename Decoupling
-
-VectorFlow uses a stable `gitPath` field to track the file path in Git for each pipeline. This means:
-
-- **Renaming a pipeline** in VectorFlow does not change its filename in Git. The original path is preserved.
-- **First sync** automatically assigns a `gitPath` based on the environment and pipeline name slugs.
-- **Webhook imports** match files by `gitPath` first, then by name as a fallback.
-
-This prevents broken sync when pipelines are renamed after initial setup.
-
-## Sync Status and Retries
-
-The **Git Sync Status** section on the environment detail page shows:
-
-- **Health badge** -- Green (healthy), yellow (pending retries), or red (failed).
-- **Last successful sync** timestamp.
-- **Recent sync jobs** with status, attempt count, and per-job retry buttons.
-- **Import errors** from webhook events (YAML parse failures, invalid filenames, etc.).
-
-Failed sync operations are automatically retried up to 3 times with exponential backoff (30 seconds, 2 minutes, 10 minutes). After all retries are exhausted, a `git_sync_failed` alert is fired (if subscribed). You can also manually retry failed jobs from the UI.
-
-The environment list page shows a warning badge when an environment has unresolved sync failures.
-
-## File layout
-
-VectorFlow expects pipeline YAML files to follow the standard Vector configuration format:
-
-```
-repo-root/
- environment-name/
- pipeline-a.yaml
- pipeline-b.yaml
- other-environment/
- pipeline-c.yaml
-```
-
-The directory name should match the slugified environment name. Files must have a `.yaml` or `.yml` extension.
-
-## Disabling GitOps
-
-To disable GitOps, set the mode back to **Off** and click **Save**. The webhook secret is cleared, and incoming webhook requests will be rejected.
diff --git a/docs/public/operations/outbound-webhooks.md b/docs/public/operations/outbound-webhooks.md
deleted file mode 100644
index d4d96737..00000000
--- a/docs/public/operations/outbound-webhooks.md
+++ /dev/null
@@ -1,154 +0,0 @@
-# Outbound Webhooks
-
-VectorFlow can send HMAC-signed HTTP notifications to external systems when key events occur. Use outbound webhooks to integrate with incident management tools, CI/CD pipelines, custom dashboards, or any service that accepts HTTP callbacks.
-
-## Overview
-
-Each **webhook endpoint** is a URL that receives POST requests when one or more subscribed events fire. Requests carry Standard-Webhooks-compliant signature headers so receivers can verify authenticity.
-
-Key properties of each endpoint:
-
-- **Name** — A descriptive label shown in the management UI.
-- **URL** — The HTTPS endpoint that receives event payloads.
-- **Event types** — One or more event types that trigger delivery.
-- **Signing secret** — Optional HMAC key. When set, every request includes a `webhook-signature` header.
-- **Enabled / Disabled** — Endpoints can be temporarily disabled without deleting them.
-
-## Supported events
-
-| Event | When it fires |
-|-------|---------------|
-| `deploy_completed` | A pipeline deployment completed successfully |
-| `deploy_rejected` | A deployment request was rejected |
-| `deploy_cancelled` | A pending deployment was cancelled |
-| `pipeline_crashed` | A running pipeline process exited unexpectedly |
-| `node_unreachable` | A fleet node stopped sending heartbeats |
-| `node_joined` | A new fleet node enrolled |
-| `node_left` | A fleet node was removed |
-| `promotion_completed` | A pipeline was promoted to another environment |
-
-## Creating a webhook endpoint
-
-{% stepper %}
-{% step %}
-### Open Webhook Settings
-Navigate to **Settings → Outbound Webhooks**.
-{% endstep %}
-{% step %}
-### Click New Endpoint
-Click the **New Endpoint** button in the top-right corner.
-{% endstep %}
-{% step %}
-### Fill in the form
-- **Name** — A descriptive label (e.g., "PagerDuty Pipeline Alerts").
-- **Endpoint URL** — The HTTPS URL that will receive events.
-- **Signing secret** — Optional. If provided, every request is signed and the secret is shown once — copy it before closing the dialog.
-- **Event types** — Select one or more events this endpoint should receive.
-{% endstep %}
-{% step %}
-### Create
-Click **Create**. If you provided a signing secret, the dialog shows it once — copy it to a secure location.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="warning" %}
-The signing secret is displayed once at creation time and cannot be retrieved afterwards. Store it securely in your receiving application's configuration.
-{% endhint %}
-
-## Payload format
-
-All webhook deliveries use the same envelope format:
-
-```json
-{
- "type": "deploy_completed",
- "timestamp": "2026-03-27T12:00:00.000Z",
- "data": {
- // Event-specific fields
- }
-}
-```
-
-| Field | Description |
-|-------|-------------|
-| `type` | The `AlertMetric` value that triggered this delivery |
-| `timestamp` | ISO-8601 UTC timestamp of the event |
-| `data` | Event-specific payload fields |
-
-## Verifying signatures
-
-When a signing secret is configured, every request includes three headers:
-
-| Header | Description |
-|--------|-------------|
-| `webhook-id` | Unique UUID for this delivery |
-| `webhook-timestamp` | Unix timestamp (integer seconds) |
-| `webhook-signature` | `v1,{base64(HMAC-SHA256)}` |
-
-To verify a request, compute:
-
-```
-signing_string = "{webhook-id}.{webhook-timestamp}.{raw_request_body}"
-expected_sig = base64( HMAC-SHA256(signing_string, secret) )
-```
-
-The received signature header is `v1,{expected_sig}`. Compare the value after the `v1,` prefix.
-
-{% hint style="info" %}
-VectorFlow follows the [Standard Webhooks](https://www.standardwebhooks.com/) specification. Libraries are available for most languages.
-{% endhint %}
-
-## Delivery and retry
-
-VectorFlow attempts delivery immediately when an event fires. If the request fails, it retries with exponential backoff:
-
-| Attempt | Delay |
-|---------|-------|
-| 1 | Immediate |
-| 2 | 30 seconds |
-| 3 | 5 minutes |
-| 4 | 30 minutes |
-| 5+ | 2 hours |
-
-**Permanent failures** (HTTP 4xx excluding 429, DNS errors, connection refused) are moved to **dead-letter** immediately and are not retried.
-
-**Transient failures** (HTTP 5xx, HTTP 429, timeouts) are retried up to the schedule above.
-
-## Delivery history
-
-Each endpoint row in the settings UI can be expanded to show recent deliveries:
-
-- **Event type** — Which event triggered the delivery.
-- **Status** — `success`, `failed`, `dead_letter`, or `pending`.
-- **HTTP status** — The HTTP status code returned by the receiver.
-- **Attempt** — Which retry attempt this represents.
-- **Requested / Completed** — Relative timestamps.
-
-## Test delivery
-
-To send a test delivery to an endpoint without waiting for a real event, click the **Play** button (▶) in the endpoint row. The test payload is:
-
-```json
-{
- "type": "test",
- "timestamp": "2026-03-27T12:00:00.000Z",
- "data": {
- "message": "Test delivery from VectorFlow",
- "endpointId": "..."
- }
-}
-```
-
-The UI shows a success or failure notification. Check delivery history for the HTTP status code and any error details.
-
-## Managing endpoints
-
-| Action | How |
-|--------|-----|
-| **Enable / Disable** | Click the toggle icon in the endpoint row |
-| **Edit** | Click the pencil icon to update name, URL, events, or rotate the secret |
-| **Delete** | Click the trash icon — all delivery history is also deleted |
-
-{% hint style="info" %}
-Disabling an endpoint stops deliveries immediately without deleting the endpoint or its history. Re-enable it when ready to receive events again.
-{% endhint %}
diff --git a/docs/public/operations/scim.md b/docs/public/operations/scim.md
deleted file mode 100644
index fac80161..00000000
--- a/docs/public/operations/scim.md
+++ /dev/null
@@ -1,215 +0,0 @@
-# SCIM Provisioning
-
-VectorFlow supports SCIM 2.0 (System for Cross-domain Identity Management) for automated user provisioning and deprovisioning from your identity provider. When SCIM is enabled, your IdP can automatically create, update, and deactivate user accounts in VectorFlow.
-
-## Overview
-
-SCIM provisioning automates the user lifecycle:
-
-| Action | What happens in VectorFlow |
-|--------|---------------------------|
-| **Create user** | A new VectorFlow user account is created with a random password (SSO users authenticate via their IdP, not local credentials) |
-| **Update user** | User attributes (name, email) are updated |
-| **Deactivate user** | The user account is locked, preventing login |
-| **Delete user** | The user account is locked (not deleted, to preserve audit history) |
-
-SCIM group membership is tracked internally — VectorFlow knows exactly which IdP groups each user belongs to. When your IdP pushes group membership changes, VectorFlow reconciles team memberships based on the configured [group mapping table](authentication.md#group-mapping), adding users to mapped teams and removing them when they no longer qualify.
-
-## Setup
-
-{% stepper %}
-{% step %}
-### Enable SCIM in VectorFlow
-
-Navigate to **Settings > Auth** (Super Admin required). Toggle **Enable SCIM** on.
-{% endstep %}
-{% step %}
-### Generate a bearer token
-
-Click **Generate Token**. A bearer token is displayed once -- copy it and store it securely. This token authenticates your IdP's SCIM requests to VectorFlow.
-
-{% hint style="warning" %}
-The token is shown only once. If you lose it, generate a new one. The previous token is immediately invalidated.
-{% endhint %}
-{% endstep %}
-{% step %}
-### Copy the SCIM base URL
-
-The SCIM base URL is displayed on the settings page:
-
-```
-https://your-vectorflow-url/api/scim/v2
-```
-{% endstep %}
-{% step %}
-### Configure your identity provider
-
-Enter the SCIM base URL and bearer token into your IdP's SCIM provisioning settings. See the IdP-specific instructions below.
-{% endstep %}
-{% step %}
-### Test and assign
-
-Test the SCIM connection from your IdP, then assign users and groups to the VectorFlow application in your IdP.
-{% endstep %}
-{% endstepper %}
-
-## Group lifecycle and reconciliation
-
-SCIM group membership is tracked internally — VectorFlow maintains a record of exactly which IdP groups each user belongs to via SCIM. When group membership changes, VectorFlow reconciles team memberships using the shared [group mapping table](authentication.md#group-mapping).
-
-### How SCIM Group operations work
-
-| Operation | What happens |
-|-----------|-------------|
-| **POST /Groups** | Creates the group and processes initial members. Each member's team memberships are reconciled against the mapping table. |
-| **PATCH add members** | Adds users to the group and reconciles their team memberships — users gain access to mapped teams with the configured role. |
-| **PATCH remove members** | Removes users from the group and reconciles — if a user no longer belongs to any group that maps to a given team, their membership on that team is removed. |
-| **PUT /Groups** | Full member sync. VectorFlow compares the provided member list against the current membership, adds missing members, removes absent members, and reconciles all affected users. |
-| **DELETE /Groups** | Deletes the group, cascading to all group membership records. All affected users' team memberships are reconciled (memberships that were only justified by the deleted group are removed). |
-| **PATCH displayName** | Updates the group name. If the new name matches a different mapping, team memberships are reconciled accordingly for all group members. |
-
-### Role assignment
-
-When SCIM pushes group membership changes, VectorFlow assigns roles using the same team mappings configured for OIDC:
-
-1. If **Team Mappings** are configured in **Settings > Team & Role Mapping**, the mapping's role is used
-2. If a user is in multiple groups that map to the same team, the **highest role wins** (Admin > Editor > Viewer)
-3. If no mapping matches, the **Default Role** is used
-4. If no default role is set, `VIEWER` is assigned
-
-This ensures consistent role assignment regardless of whether sync happens via SCIM push or OIDC login.
-
-{% hint style="info" %}
-**Manual assignments are preserved.** Team memberships assigned manually in the VectorFlow UI are never modified by SCIM group sync. Only memberships created by group sync are subject to reconciliation.
-{% endhint %}
-
-## IdP-specific instructions
-
-{% tabs %}
-{% tab title="Okta" %}
-1. In your Okta admin console, open the VectorFlow application (or create a new SAML/OIDC app)
-2. Go to the **Provisioning** tab and click **Configure API Integration**
-3. Check **Enable API Integration**
-4. Set **SCIM connector base URL** to your VectorFlow SCIM URL (e.g., `https://vectorflow.example.com/api/scim/v2`)
-5. Set **API Token** to the bearer token generated in VectorFlow
-6. Click **Test API Credentials** to verify the connection
-7. Save the integration
-8. Under **Provisioning > To App**, enable:
- - Create Users
- - Update User Attributes
- - Deactivate Users
-9. Go to the **Assignments** tab and assign users or groups
-{% endtab %}
-{% tab title="Entra ID (Azure AD)" %}
-1. In the Azure portal, navigate to **Enterprise Applications** and select your VectorFlow application
-2. Go to **Provisioning** and set the mode to **Automatic**
-3. Under **Admin Credentials**:
- - **Tenant URL**: Your VectorFlow SCIM URL (e.g., `https://vectorflow.example.com/api/scim/v2`)
- - **Secret Token**: The bearer token generated in VectorFlow
-4. Click **Test Connection** to verify
-5. Configure **Attribute Mappings** as needed (the defaults usually work)
-6. Set **Provisioning Status** to **On**
-7. Save and assign users/groups to the application
-{% endtab %}
-{% tab title="OneLogin" %}
-1. In the OneLogin admin console, open your VectorFlow application
-2. Go to **Configuration**
-3. Set **SCIM Base URL** to your VectorFlow SCIM URL
-4. Set **SCIM Bearer Token** to the token generated in VectorFlow
-5. Under **Provisioning**, enable the desired actions
-6. Assign users via the **Users** tab
-{% endtab %}
-{% tab title="Other IdPs" %}
-Any SCIM 2.0 compatible identity provider can integrate with VectorFlow. Configure:
-
-- **Base URL**: `https://your-vectorflow-url/api/scim/v2`
-- **Authentication**: Bearer token (HTTP header)
-- **Supported resources**: Users, Groups
-{% endtab %}
-{% endtabs %}
-
-## Supported endpoints
-
-| Method | Path | Description |
-|--------|------|-------------|
-| `GET` | `/api/scim/v2/Users` | List users (supports `filter`, `startIndex`, `count`) |
-| `POST` | `/api/scim/v2/Users` | Create a user |
-| `GET` | `/api/scim/v2/Users/:id` | Get a user |
-| `PUT` | `/api/scim/v2/Users/:id` | Replace a user |
-| `PATCH` | `/api/scim/v2/Users/:id` | Partial update (commonly used for deactivation) |
-| `DELETE` | `/api/scim/v2/Users/:id` | Deactivate a user (locks the account) |
-| `GET` | `/api/scim/v2/Groups` | List groups |
-| `POST` | `/api/scim/v2/Groups` | Create a group and process initial members |
-| `GET` | `/api/scim/v2/Groups/:id` | Get a group |
-| `PATCH` | `/api/scim/v2/Groups/:id` | Update group membership (add/remove members, rename) |
-| `PUT` | `/api/scim/v2/Groups/:id` | Replace group (full member sync) |
-| `DELETE` | `/api/scim/v2/Groups/:id` | Delete group and cascade membership removal |
-
-### Group identity fields
-
-Group responses include both the VectorFlow-assigned `id` and the IdP-provided `externalId`. The `externalId` is set when the IdP includes it in the initial POST or subsequent PUT/PATCH requests.
-
-{% hint style="info" %}
-The `externalId` field is critical for IdP sync stability. Without it, some identity providers cannot match remote groups to local groups during reconciliation, causing unnecessary delete-and-recreate cycles. If your IdP performs full group syncs, ensure it sends `externalId` in group creation requests.
-{% endhint %}
-
-### Filtering
-
-The Users endpoint supports basic SCIM filtering:
-
-```
-GET /api/scim/v2/Users?filter=userName eq "john@example.com"
-GET /api/scim/v2/Users?filter=externalId eq "abc123"
-```
-
-The Groups endpoint supports:
-
-```
-GET /api/scim/v2/Groups?filter=displayName eq "Platform Team"
-```
-
-## Security
-
-- The SCIM bearer token is encrypted with AES-256-GCM before storage (same encryption used for OIDC client secrets)
-- The token is shown only once when generated; VectorFlow does not store the plaintext
-- SCIM endpoints require a valid bearer token on every request
-- Disabling SCIM clears the stored token
-- All SCIM operations are recorded in the audit log under the **ScimUser** entity type
-
-### Audit logging
-
-SCIM user operations (create, update, deactivate, delete) are logged with the `ScimUser` entity type to distinguish them from manual user operations. On the **Audit** page, you can filter by:
-
-- **ScimUser** -- shows only SCIM user provisioning events
-- **ScimGroup** -- shows only SCIM group operations
-- **SCIM (All)** -- a combined filter that shows all SCIM-related activity (both user and group operations) in a single view
-
-This makes it easy to audit all identity provider-driven changes for compliance purposes.
-
-{% hint style="info" %}
-SCIM provisioning works best alongside OIDC/SSO. Users created via SCIM receive a random password and should authenticate through your identity provider, not with local credentials.
-{% endhint %}
-
-## Troubleshooting
-
-| Issue | Solution |
-|-------|----------|
-| IdP test connection fails | Verify the SCIM base URL is reachable from your IdP. Check that the bearer token is correct and SCIM is enabled in VectorFlow settings. |
-| Users not being created | Check that "Create Users" is enabled in your IdP's provisioning settings. Review the IdP provisioning logs for error details. |
-| Users not being deactivated | Check that "Deactivate Users" is enabled in your IdP. VectorFlow locks the account (sets `lockedAt`) rather than deleting it. |
-| Group membership not syncing | SCIM Groups are mapped to VectorFlow Teams via the shared group mapping table in **Settings > Team & Role Mapping**. Ensure groups are assigned to the VectorFlow application in your IdP and that corresponding mappings exist. Without a matching mapping, group membership is tracked but no team assignment is created. |
-| Token expired/invalid | Generate a new token from **Settings > Auth** and update it in your IdP. The previous token is invalidated immediately. |
-
-### SCIM sync returns HTML error
-
-If your IdP reports an error like `invalid character '<' looking for beginning of value`, the SCIM base URL may be incorrect. Ensure it is set to:
-
-```
-https://your-vectorflow-url/api/scim/v2
-```
-
-VectorFlow exposes a `ServiceProviderConfig` endpoint at `/api/scim/v2/ServiceProviderConfig` that your IdP can use to verify connectivity.
-
-### Roles not updating via SCIM
-
-Ensure that **Team Mappings** are configured in **Settings > Team & Role Mapping**. Without team mappings, all SCIM-provisioned members default to the **VIEWER** role. If a user belongs to multiple groups that map to the same team, the highest role wins (Admin > Editor > Viewer).
diff --git a/docs/public/operations/security.md b/docs/public/operations/security.md
deleted file mode 100644
index 32ea47be..00000000
--- a/docs/public/operations/security.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# Security
-
-This page covers VectorFlow's security architecture: how secrets are managed, how data is encrypted, and recommended hardening practices for production deployments.
-
-## Secret management
-
-VectorFlow provides a built-in secret store for each environment. Secrets hold sensitive values -- API keys, database passwords, authentication tokens -- that pipelines need at runtime but should not be stored in plain text in pipeline configurations.
-
-### Creating secrets
-
-Secrets are created on the **environment detail page** under **Secrets & Certificates**. Each secret has a name and a value. Secret names must start with a letter or number and can contain letters, numbers, hyphens, and underscores.
-
-Secrets are scoped to a single environment. The same secret name can hold different values in different environments (e.g., a `DB_PASSWORD` secret with a test value in dev and a production value in prod).
-
-### How secrets are stored
-
-When you create or update a secret, the value is encrypted with **AES-256-GCM** before being written to the database. The plaintext value is never stored. Only the encrypted ciphertext is persisted.
-
-### How secrets are resolved
-
-When a pipeline is deployed, VectorFlow generates the Vector configuration file. During generation, it scans the configuration for **secret references** and converts them into environment variable placeholders. The actual secret values are delivered separately to the agent and injected as environment variables into the Vector process.
-
-Secret references use the syntax:
-
-```
-SECRET[secret-name]
-```
-
-For example, a Kafka sink node configured with:
-
-```yaml
-sasl:
- username: my-user
- password: SECRET[KAFKA_PASSWORD]
-```
-
-At deploy time, VectorFlow converts this to:
-
-```yaml
-sasl:
- username: "my-user"
- password: "${VF_SECRET_KAFKA_PASSWORD}"
-```
-
-The actual decrypted value of `KAFKA_PASSWORD` is delivered to the agent in a separate `secrets` dictionary alongside the YAML. The agent sets `VF_SECRET_KAFKA_PASSWORD` as an environment variable when starting the Vector process, and Vector's built-in environment variable interpolation resolves the placeholder at runtime.
-
-{% hint style="info" %}
-Secret values never appear in pipeline YAML -- not in the database, not in config files on disk, and not in the YAML sent to agents. Only environment variable placeholders are embedded in the configuration. The actual values are delivered separately in the config response and injected as process environment variables.
-{% endhint %}
-
-#### Secret name normalization
-
-Secret names are converted to valid environment variable names using these rules:
-
-- Prefixed with `VF_SECRET_`
-- All non-alphanumeric characters replaced with underscores
-- Converted to uppercase
-
-For example, `my-api.key` becomes `VF_SECRET_MY_API_KEY`. If two secrets normalize to the same environment variable name (e.g., `db-password` and `db_password` both become `VF_SECRET_DB_PASSWORD`), VectorFlow logs a warning and the last match in alphabetical order takes precedence.
-
-### Sensitive fields
-
-Pipeline component fields that contain credentials -- passwords, API keys, tokens -- are restricted to **secret references only** in the pipeline editor. You cannot type a plaintext value into a sensitive field. Instead, the editor presents a secret picker that lets you select an existing secret or create a new one inline.
-
-Fields are treated as sensitive when:
-
-- The Vector component schema marks them as `sensitive: true`
-- The field name matches a pattern like `password`, `secret`, `token`, or `api_key`
-
-### Certificate references
-
-TLS certificates work the same way, using the `CERT[name]` syntax. When a pipeline references a certificate, VectorFlow decrypts the certificate data and deploys it as a file on the agent node:
-
-```yaml
-tls:
- crt_file: CERT[my-tls-cert]
-```
-
-The agent receives the certificate file and writes it to `/var/lib/vf-agent/certs/`.
-
-## Encryption
-
-### At rest
-
-VectorFlow encrypts sensitive data before storing it in PostgreSQL:
-
-| Data | Algorithm | Key derivation |
-|------|-----------|---------------|
-| Secrets (user-created) | AES-256-GCM | SHA-256 hash of `NEXTAUTH_SECRET` |
-| Certificates | AES-256-GCM | SHA-256 hash of `NEXTAUTH_SECRET` |
-| OIDC client secret | AES-256-GCM | SHA-256 hash of `NEXTAUTH_SECRET` |
-| Sensitive node config fields | AES-256-GCM | SHA-256 hash of `NEXTAUTH_SECRET` |
-| Git access tokens (audit trail) | AES-256-GCM | SHA-256 hash of `NEXTAUTH_SECRET` |
-| User passwords | bcrypt (cost 12) | Built-in salt |
-| TOTP secrets | AES-256-GCM | SHA-256 hash of `NEXTAUTH_SECRET` |
-| 2FA backup codes | SHA-256 hash | -- |
-| Webhook signing | HMAC-SHA256 | Per-webhook secret |
-
-{% hint style="danger" %}
-`NEXTAUTH_SECRET` is the master encryption key for all sensitive data. If this value is changed or lost, all encrypted data (secrets, certificates, OIDC config) becomes permanently unrecoverable. Back up this value securely.
-{% endhint %}
-
-### Sensitive field auto-encryption
-
-Pipeline node configurations may contain sensitive fields (passwords, API keys, tokens). VectorFlow automatically detects and encrypts these fields when saving a pipeline, based on:
-
-1. Fields marked as `sensitive: true` in the Vector component schema
-2. Field names matching patterns like `password`, `secret`, `token`, or `api_key`
-
-These fields are encrypted before database storage and decrypted only when generating the Vector configuration for deployment.
-
-### In transit
-
-- **Browser to server** -- HTTPS (TLS termination via reverse proxy or load balancer)
-- **Agent to server** -- HTTPS over the same endpoint. Agents authenticate with a bearer token issued during enrollment.
-- **Server to database** -- Configurable via `sslmode` in the `DATABASE_URL` connection string
-
-## Network security
-
-### Agent connections
-
-Agents initiate all connections to the server. The server never connects outbound to agents. This means:
-
-- Agents can run behind firewalls and NATs
-- No inbound ports need to be opened on agent nodes
-- Only outbound HTTPS (port 443) to the VectorFlow server is required
-
-### Reverse proxy
-
-In production, place VectorFlow behind a reverse proxy (Nginx, Caddy, Traefik) for TLS termination. See [Deploy the Server](../getting-started/deploy-server.md) for example configurations.
-
-### Agent authentication
-
-Each agent authenticates using a **node token** -- a unique bearer token issued during enrollment. The token is stored at `/var/lib/vf-agent/node-token` with file permissions `0600` (readable only by the owner).
-
-Enrollment tokens (used for initial registration) can be regenerated or revoked from the environment detail page.
-
-## Audit logging
-
-Every mutation in VectorFlow is logged to an audit trail. Audit entries include:
-
-- **Who** -- The authenticated user
-- **What** -- The action performed and entity affected
-- **When** -- Timestamp
-- **Where** -- Client IP address
-- **Changes** -- A diff of the fields that were modified
-
-Sensitive fields (passwords, tokens, secrets) are automatically redacted in audit log entries.
-
-View the audit log from the **Audit** page in the sidebar. The audit log supports filtering by entity type, including dedicated filters for SCIM operations:
-
-- **ScimUser** -- SCIM user provisioning events (create, update, deactivate)
-- **ScimGroup** -- SCIM group operations (create, update members, delete)
-- **SCIM (All)** -- Combined filter showing all SCIM activity in one view
-
-These filters make it straightforward to review all identity provider-driven changes for compliance audits.
-
-## Security hardening checklist
-
-Use this checklist to harden your VectorFlow deployment for production:
-
-{% hint style="warning" %}
-Complete all items before exposing VectorFlow to untrusted networks.
-{% endhint %}
-
-- [ ] **Generate a strong `NEXTAUTH_SECRET`** -- Use `openssl rand -base64 32` to generate a random 32+ character secret. Never use default or weak values.
-
-- [ ] **Generate a strong `POSTGRES_PASSWORD`** -- Use a random, high-entropy password for the database.
-
-- [ ] **Enable TLS/HTTPS** -- Place VectorFlow behind a reverse proxy with TLS termination. All agent communication should use HTTPS.
-
-- [ ] **Enable 2FA for all users** -- Use team-level "Require 2FA" settings to enforce two-factor authentication for all team members.
-
-- [ ] **Use OIDC/SSO** -- Integrate with your organization's identity provider for centralized authentication and MFA.
-
-- [ ] **Restrict network access** -- Limit access to the VectorFlow server to trusted networks. Use firewall rules or network policies.
-
-- [ ] **Enable database TLS** -- Add `sslmode=require` to your `DATABASE_URL` to encrypt the connection between VectorFlow and PostgreSQL.
-
-- [ ] **Regular backups** -- Enable automatic daily backups and verify restores periodically. See [Backup & Restore](backup-restore.md).
-
-- [ ] **Keep software updated** -- Regularly update VectorFlow server and agents to get security patches. See [Upgrading](upgrading.md).
-
-- [ ] **Review audit logs** -- Periodically review the audit log for unexpected actions or unauthorized access attempts.
-
-- [ ] **Lock unused accounts** -- Lock user accounts that are no longer active instead of leaving them accessible.
diff --git a/docs/public/operations/service-accounts.md b/docs/public/operations/service-accounts.md
deleted file mode 100644
index 4749cf05..00000000
--- a/docs/public/operations/service-accounts.md
+++ /dev/null
@@ -1,142 +0,0 @@
-# Service Accounts
-
-Service accounts provide API keys for programmatic access to VectorFlow's REST API. Unlike user sessions (cookie-based), service account keys use Bearer token authentication and are scoped to a single environment with granular permissions.
-
----
-
-## Overview
-
-| Feature | Details |
-|---------|---------|
-| Authentication | `Authorization: Bearer vf_live_...` |
-| Scope | One environment per service account |
-| Permissions | Granular per-resource (read/manage) |
-| Key format | `vf_live_<48 hex chars>` |
-| Storage | SHA-256 hashed (raw key shown once at creation) |
-| Expiration | Optional (30, 60, 90 days, or never) |
-
-{% hint style="warning" %}
-API keys are shown **only once** at creation time. If you lose a key, revoke the service account and create a new one.
-{% endhint %}
-
----
-
-## Creating a Service Account
-
-{% stepper %}
-{% step %}
-### Navigate to Settings
-Go to **Settings** and click **Service Accounts & API Keys**.
-{% endstep %}
-
-{% step %}
-### Click Create Service Account
-Fill in the following fields:
-
-| Field | Required | Description |
-|-------|----------|-------------|
-| Name | Yes | A descriptive name (e.g., "CI/CD Deployer") |
-| Description | No | Optional context about the account's purpose |
-| Environment | Yes | The environment this key can access |
-| Expiration | No | Auto-expire after 30, 60, or 90 days (default: never) |
-| Permissions | Yes | At least one permission must be selected |
-{% endstep %}
-
-{% step %}
-### Copy Your API Key
-After creation, a modal displays the raw API key. **Copy it immediately** -- it cannot be retrieved again.
-{% endstep %}
-{% endstepper %}
-
----
-
-## Permissions
-
-Permissions control what the service account can do via the REST API.
-
-| Permission | Description |
-|-----------|-------------|
-| `pipelines.read` | List and view pipeline details, versions |
-| `pipelines.deploy` | Deploy, undeploy, and rollback pipelines |
-| `nodes.read` | List and view node details |
-| `nodes.manage` | Toggle node maintenance mode |
-| `secrets.read` | List secret names (values are never exposed) |
-| `secrets.manage` | Create, update, and delete secrets |
-| `alerts.read` | List alert rules |
-| `alerts.manage` | Create alert rules |
-| `audit.read` | Read audit log events |
-
-{% hint style="info" %}
-Permissions are enforced per-request. A `403 Forbidden` response indicates the service account lacks the required permission.
-{% endhint %}
-
----
-
-## Managing Service Accounts
-
-### Revoking
-
-Revoking a service account **immediately disables** the API key. The service account record is preserved for audit purposes.
-
-1. Navigate to **Settings > Service Accounts**
-2. Click the **ban icon** next to the account
-3. Confirm revocation
-
-### Deleting
-
-Deleting permanently removes the service account and its audit trail association.
-
-1. Navigate to **Settings > Service Accounts**
-2. Click the **trash icon** next to the account
-3. Confirm deletion
-
-{% hint style="danger" %}
-Deletion is irreversible. If you only need to disable access temporarily, use **Revoke** instead.
-{% endhint %}
-
----
-
-## Using the API Key
-
-Include the key in the `Authorization` header of your HTTP requests:
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/pipelines \
- -H "Authorization: Bearer vf_live_abc123..."
-```
-
-All REST API endpoints are under `/api/v1/`. See the [API Reference](../reference/api.md) for the complete endpoint list.
-
----
-
-## Security Best Practices
-
-- **Least privilege**: Only grant the permissions the service account needs
-- **Set expiration**: Use 30-90 day expiration for CI/CD keys and rotate regularly
-- **Store securely**: Keep API keys in a secrets manager (not in source code)
-- **Monitor usage**: Check the "Last Used" column to detect unused accounts
-- **Revoke promptly**: Revoke keys when they are no longer needed or if compromised
-- **One per integration**: Create separate service accounts for each CI/CD pipeline or integration
-
----
-
-## Key Rotation
-
-VectorFlow does not support in-place key rotation. To rotate a key:
-
-{% stepper %}
-{% step %}
-### Create a new service account
-Use the same name (with a version suffix) and permissions.
-{% endstep %}
-
-{% step %}
-### Update your integration
-Replace the old key with the new one in your CI/CD pipeline or integration.
-{% endstep %}
-
-{% step %}
-### Revoke the old account
-Once the new key is confirmed working, revoke the old service account.
-{% endstep %}
-{% endstepper %}
diff --git a/docs/public/operations/telemetry.md b/docs/public/operations/telemetry.md
deleted file mode 100644
index fa8fc4f1..00000000
--- a/docs/public/operations/telemetry.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Telemetry
-
-VectorFlow can send anonymous, aggregate usage stats to a centralised receiver to help the maintainer understand how it's used and prioritise improvements. This is **opt-in** — VectorFlow does not send any data unless you explicitly turn it on.
-
-## What is collected
-
-When telemetry is enabled, VectorFlow sends a single JSON payload **once per day** to `https://pulse.terrifiedbug.com/api/v1/ping`. The payload contains:
-
-| Field | Source | Why |
-|---|---|---|
-| `schema_version` | hardcoded `1` | lets the receiver evolve without breaking old clients |
-| `instance_id` | random ULID generated when you first opt in | a stable identifier for your installation. Anonymous — not linked to any user, organisation, or hostname. |
-| `instance_created_at` | timestamp of your first opt-in | cohort tracking — lets us see retention over time |
-| `sent_at` | current timestamp | when the heartbeat was sent |
-| `vf_version` | `process.env.VF_VERSION` (or `"unknown"`) | which version of VectorFlow you're running |
-| `agent_count` | row count of the `VectorNode` table (fleet agents managed by VectorFlow) | scale signal — how many fleet agents are managed |
-| `pipeline_count` | derived from the `Pipeline` table — `active` = `isDraft: false` with `deployedAt` set, `paused` = `isDraft: false` with `deployedAt` null, `draft` = `isDraft: true` | `{ active, paused, draft }` — scale and lifecycle signal |
-| `auth_method` | derived from OIDC issuer setting | `"credentials"` or `"oidc"` — informs which auth path matters |
-| `deployment_mode` | `process.env.VF_DEPLOYMENT_MODE` (or `"unknown"`) | `"docker"`, `"helm"`, `"bare"`, or `"unknown"` |
-
-### What is NOT collected
-
-- Hostnames, IP addresses (the receiver's IP-based country lookup is server-side only — your IP is never stored, only a hash for rate limiting)
-- Pipeline names, configurations, or VRL code
-- User emails, names, or identifiers
-- Source/sink endpoint URLs (Kafka brokers, S3 buckets, syslog servers, etc.)
-- Any data flowing through your pipelines
-
-## Example payload
-
-```json
-{
- "schema_version": 1,
- "instance_id": "01HX0000000000000000000000",
- "instance_created_at": "2026-04-25T10:00:00.000Z",
- "sent_at": "2026-04-26T03:42:00.000Z",
- "vf_version": "1.4.2",
- "agent_count": 5,
- "pipeline_count": { "active": 12, "paused": 2, "draft": 3 },
- "auth_method": "credentials",
- "deployment_mode": "docker"
-}
-```
-
-## How to opt out
-
-In VectorFlow, go to **Settings → Telemetry** and turn the toggle off. Telemetry stops immediately. Your instance ID is preserved in case you re-enable later (so you appear as the same anonymous instance, not a new one).
-
-You can also choose "No thanks" during initial setup if you want telemetry off from day one.
-
-## Receiver source
-
-The telemetry receiver is private. Its data-handling code is documented in its own `PRIVACY.md`. No telemetry data is shared per-instance publicly — only aggregate counts on a community stats dashboard.
diff --git a/docs/public/operations/upgrading.md b/docs/public/operations/upgrading.md
deleted file mode 100644
index 7213ba38..00000000
--- a/docs/public/operations/upgrading.md
+++ /dev/null
@@ -1,237 +0,0 @@
-# Upgrading
-
-VectorFlow is designed for zero-downtime upgrades. The server handles database migrations automatically on startup, and agents can self-update without manual intervention.
-
-## Pre-upgrade checklist
-
-Before upgrading, complete these steps:
-
-- [ ] **Create a database backup** -- Navigate to Settings > Backup and click **Create Backup**, or verify that a recent automatic backup exists. See [Backup & Restore](backup-restore.md).
-- [ ] **Review release notes** -- Check the [Releases](https://github.com/TerrifiedBug/vectorflow/releases) page for breaking changes, required actions, or migration notes.
-- [ ] **Verify agent compatibility** -- Server and agent versions should be kept in sync. The server is backward-compatible with older agents, but newer agents may require a newer server.
-
-## Version checking
-
-VectorFlow automatically checks for new releases every 24 hours by querying the GitHub Releases API. When a new version is available, a notification appears on the Settings page showing:
-
-- Current server version
-- Latest available version
-- Link to the release notes
-
-You can force a version check from **Settings** by clicking **Check for Updates**.
-
-## Server upgrade
-
-The server upgrade process is the same regardless of how you deployed: replace the binary or image, restart, and migrations run automatically.
-
-{% tabs %}
-{% tab title="Docker" %}
-### Docker upgrade
-
-{% stepper %}
-{% step %}
-### Pull the new image
-```bash
-docker compose pull vectorflow
-```
-
-Or pin a specific version in your `.env` file:
-```bash
-VF_VERSION=v0.4.0
-```
-{% endstep %}
-{% step %}
-### Restart the server
-```bash
-docker compose up -d
-```
-
-The entrypoint runs `prisma migrate deploy` automatically. Database schema changes are applied before the application starts.
-{% endstep %}
-{% step %}
-### Verify
-Check the logs to confirm the server started successfully:
-```bash
-docker compose logs -f vectorflow
-```
-
-Look for the migration output and the "Ready" message.
-{% endstep %}
-{% endstepper %}
-
-{% endtab %}
-{% tab title="Standalone" %}
-### Standalone upgrade
-
-{% stepper %}
-{% step %}
-### Download the new release
-```bash
-curl -sSfL -o vectorflow.tar.gz \
- https://github.com/TerrifiedBug/vectorflow/releases/latest/download/vectorflow-server.tar.gz
-```
-{% endstep %}
-{% step %}
-### Stop the server
-```bash
-sudo systemctl stop vectorflow
-```
-{% endstep %}
-{% step %}
-### Extract the new release
-```bash
-tar xzf vectorflow.tar.gz -C /opt/vectorflow
-```
-{% endstep %}
-{% step %}
-### Run migrations
-```bash
-cd /opt/vectorflow
-npx prisma migrate deploy
-```
-{% endstep %}
-{% step %}
-### Start the server
-```bash
-sudo systemctl start vectorflow
-```
-{% endstep %}
-{% step %}
-### Verify
-```bash
-sudo systemctl status vectorflow
-journalctl -u vectorflow -f
-```
-{% endstep %}
-{% endstepper %}
-
-{% endtab %}
-{% endtabs %}
-
-## Agent upgrade
-
-### Automatic self-update
-
-Agents can update themselves automatically. When the server detects that a newer agent version is available, it includes a **self-update action** in the heartbeat response. The agent then:
-
-1. Downloads the new binary from the release URL
-2. Computes a SHA-256 checksum and verifies it against the expected value
-3. Writes the new binary to a temporary file alongside the current executable
-4. Atomically replaces the current binary (`rename`)
-5. Re-executes the process (`syscall.Exec`) with the same arguments and environment
-
-The update is seamless -- running Vector pipelines are not interrupted during the agent binary swap. After re-exec, the agent resumes its heartbeat loop with the new version.
-
-{% hint style="info" %}
-Self-update requires the agent binary to be writable by the process. If the agent runs as a restricted user, ensure it has write permission to its own executable path.
-{% endhint %}
-
-### Manual agent update
-
-If automatic updates are not suitable for your environment, you can update agents manually:
-
-{% tabs %}
-{% tab title="Docker" %}
-```bash
-# Pull the new agent image
-docker compose pull vf-agent
-
-# Restart the agent
-docker compose up -d vf-agent
-```
-{% endtab %}
-{% tab title="Standalone" %}
-```bash
-# Download the new binary
-curl -sSfL -o /usr/local/bin/vf-agent \
- https://github.com/TerrifiedBug/vectorflow/releases/latest/download/vf-agent-linux-amd64
-
-# Make it executable
-chmod +x /usr/local/bin/vf-agent
-
-# Restart the agent
-sudo systemctl restart vf-agent
-```
-{% endtab %}
-{% endtabs %}
-
-## Database migrations
-
-VectorFlow uses Prisma ORM for database schema management. Migrations are:
-
-- **Automatically applied** on server startup in the Docker image (the entrypoint runs `prisma migrate deploy`)
-- **Forward-only** -- there is no automatic rollback of migrations
-- **Non-destructive** where possible -- VectorFlow avoids dropping columns or tables in migrations
-
-If a migration fails, the server will not start. Check the logs for the specific error and resolve it before restarting.
-
-## Rollback
-
-### Server rollback
-
-If an upgrade causes issues, you can roll back to the previous version:
-
-{% tabs %}
-{% tab title="Docker" %}
-Pin the previous version in your `.env` file and restart:
-
-```bash
-# Set the previous version
-VF_VERSION=v0.3.0
-
-# Restart with the old image
-docker compose up -d
-```
-
-{% hint style="warning" %}
-Rolling back the server after a database migration has run may cause errors if the application code expects the old schema. If migrations were applied, restore from a pre-upgrade backup instead.
-{% endhint %}
-{% endtab %}
-{% tab title="Standalone" %}
-Replace the application files with the previous release archive:
-
-```bash
-sudo systemctl stop vectorflow
-tar xzf vectorflow-v0.3.0.tar.gz -C /opt/vectorflow
-sudo systemctl start vectorflow
-```
-
-If database migrations were applied, restore from a backup:
-
-```bash
-# Stop the server
-sudo systemctl stop vectorflow
-
-# Restore the pre-upgrade backup
-pg_restore --clean --if-exists \
- -U vectorflow -d vectorflow \
- /backups/vectorflow-pre-upgrade.dump
-
-# Start the old version
-sudo systemctl start vectorflow
-```
-{% endtab %}
-{% endtabs %}
-
-### Agent rollback
-
-For Docker-based agents, pin the previous image tag. For standalone agents, replace the binary with the previous version:
-
-```bash
-# Download the specific previous version
-curl -sSfL -o /usr/local/bin/vf-agent \
- https://github.com/TerrifiedBug/vectorflow/releases/download/v0.3.0/vf-agent-linux-amd64
-
-chmod +x /usr/local/bin/vf-agent
-sudo systemctl restart vf-agent
-```
-
-## Version compatibility
-
-| Server Version | Minimum Agent Version | Notes |
-|---------------|----------------------|-------|
-| Current | Current - 2 minor versions | Agents within 2 minor versions of the server are fully supported |
-
-{% hint style="info" %}
-The server is generally backward-compatible with older agents. Older agents may not support newer features (e.g., new pipeline actions), but they will continue to run existing pipelines without issues. It is recommended to keep agents updated to match the server version.
-{% endhint %}
diff --git a/docs/public/reference/agent-architecture.md b/docs/public/reference/agent-architecture.md
deleted file mode 100644
index c3ac4db4..00000000
--- a/docs/public/reference/agent-architecture.md
+++ /dev/null
@@ -1,266 +0,0 @@
-# Go Agent: Architecture & Internals
-
-This document describes how the `vf-agent` binary is structured internally. It is aimed at operators who need a deep understanding of the agent's runtime behavior, and contributors working on the agent codebase.
-
-For user-facing installation and configuration, see [Agent Reference](./agent.md).
-
----
-
-## Component map
-
-```mermaid
-graph TD
- A[main.go] --> B[agent.Agent]
-
- B --> C[client.Client\nHTTP to VF server]
- B --> D[poller\nConfig diff engine]
- B --> E[supervisor.Supervisor\nVector process manager]
- B --> F[push.Client\nSSE push channel]
- B --> G[tapper.Manager\nLive event tapping]
- B --> H[selfmetrics.Metrics\nAgent self-observability]
-
- D -- GET /api/agent/config --> C
- C -- POST /api/agent/heartbeat --> C
-
- E --> E1[Vector process\npipeline-a]
- E --> E2[Vector process\npipeline-b]
- E --> E3[Vector process\npipeline-n]
-
- E1 -- scrape :8688/metrics --> I[metrics.ScrapePrometheus]
- E2 -- scrape :8690/metrics --> I
- I --> B
-
- E1 -- tap via API :8689 --> G
- G -- POST /api/agent/tap-events --> C
-
- F -- SSE stream --> J[VF server push endpoint]
- J -- config_changed\nsample_request\naction --> B
-
- H -- :9090/metrics --> K[Prometheus scraper]
-```
-
----
-
-## Package responsibilities
-
-| Package | Path | Responsibility |
-|---------|------|----------------|
-| `agent` | `internal/agent/` | Top-level orchestrator: enrollment, main event loop, push message routing, log flushing, sample collection |
-| `client` | `internal/client/` | Typed HTTP client for all VF server endpoints (enroll, config, heartbeat, logs, samples, tap-events) |
-| `config` | `internal/config/` | Environment variable parsing and validation |
-| `supervisor` | `internal/supervisor/` | Spawns, monitors, and restarts Vector child processes; allocates ports; writes sidecar configs |
-| `metrics` | `internal/metrics/` | Scrapes Vector's Prometheus endpoint and parses component/host/pipeline metrics |
-| `selfmetrics` | `internal/selfmetrics/` | Tracks agent-internal health counters (poll errors, reconnects, heartbeat errors) and exposes them as a Prometheus endpoint |
-| `push` | `internal/push/` | Maintains a persistent SSE connection to the server push endpoint with automatic reconnection and fallback URL support |
-| `tapper` | `internal/tapper/` | Spawns `vector tap` subprocesses for live event inspection; streams results to the server |
-| `sampler` | `internal/sampler/` | Runs `vector tap` for one-shot event sampling; extracts field schema from sampled events |
-| `logbuf` | `internal/logbuf/` | Fixed-size ring buffer (500 lines) capturing stdout/stderr from each Vector process |
-
----
-
-## Main event loop
-
-The agent's `Run()` method executes a single goroutine select loop. This keeps the critical state machine single-threaded and eliminates the need for locks on most fields.
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ Run() select loop │
-│ │
-│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │
-│ │ ticker.C │ │ pushCh │ │ immediateHeartbeat│ │
-│ │ (poll │ │ (SSE push │ │ Ch │ │
-│ │ interval) │ │ messages) │ │ (debounced 1s) │ │
-│ └──────┬───────┘ └──────┬───────┘ └────────┬──────────┘ │
-│ │ │ │ │
-│ ▼ ▼ ▼ │
-│ pollAndApply() handlePushMessage() sendHeartbeat() │
-│ sendHeartbeat() │
-└─────────────────────────────────────────────────────────────┘
-```
-
-The `immediateHeartbeatCh` debounces rapid push notifications — multiple `config_changed` events within one second collapse into a single heartbeat with the latest state.
-
----
-
-## Process lifecycle in detail
-
-### Startup sequence
-
-```
-main()
- │
- ├─ config.Load() # parse all VF_* env vars
- ├─ agent.New(cfg) # construct all subsystems
- │ ├─ client.New(url)
- │ ├─ supervisor.New(vectorBin)
- │ ├─ tapper.New(vectorBin)
- │ └─ selfmetrics.New(pipelinesRunningFn)
- │
- └─ agent.Run()
- ├─ loadOrEnroll() # load node-token from disk or enroll with server
- ├─ (if VF_METRICS_PORT > 0) go metrics.Serve(port)
- ├─ pollAndApply() # first poll immediately
- ├─ push.Client.Connect() → go routine
- ├─ runLogFlusher() → go routine
- └─ sendHeartbeat() # first heartbeat
-```
-
-### Pipeline start
-
-```
-pollAndApply()
- └─ poller.Poll()
- └─ returns ActionStart for new pipelines
- │
- └─ supervisor.Start(pipelineID, configPath, ...)
- ├─ allocates metricsPort = basePort + (portSeq*2 - 1)
- ├─ allocates apiPort = basePort + (portSeq*2)
- ├─ writeSidecarConfig() → .vf-metrics.yaml
- ├─ exec.Command(vectorBin, --config, configPath,
- │ --config, sidecarPath)
- └─ go monitor(info) # goroutine watches for exit
-```
-
-### Crash recovery
-
-```
-monitor(info) goroutine
- ├─ time.Sleep(2s) # startup grace period → sets status RUNNING
- ├─ cmd.Wait() # blocks until process exits
- └─ if err != nil (crash):
- info.Status = "CRASHED"
- info.restarts++
- backoff = min(1s << (restarts-1), 60s)
- go func() {
- time.Sleep(backoff)
- startProcess(...) # restart with same ports
- }()
-```
-
-Backoff sequence: 1s → 2s → 4s → 8s → 16s → 32s → 60s (capped).
-
----
-
-## Port allocation
-
-Each pipeline gets two consecutive TCP ports on `127.0.0.1`, allocated by a sequential counter starting at 8688:
-
-| Pipeline | Prometheus metrics port | Vector API port |
-|----------|------------------------|-----------------|
-| First | 8688 | 8689 |
-| Second | 8690 | 8691 |
-| Third | 8692 | 8693 |
-| N-th | 8688 + (2N-2) | 8688 + (2N-1) |
-
-The counter is **never reset** within a process lifetime. If a pipeline restarts (crash recovery or config change), it reuses the same ports it was originally allocated. If a pipeline is removed and a new one added, the new pipeline receives the next available ports in the sequence.
-
-{% hint style="info" %}
-Ports are allocated from a monotonically increasing sequence, never recycled. On a long-running agent with many pipeline additions/removals, the port sequence drifts upward. This is intentional — reusing ports too soon after a process exit can cause bind failures if the old process hasn't fully released the socket.
-{% endhint %}
-
----
-
-## Push vs. poll
-
-The agent uses **both** SSE push and periodic polling, working together:
-
-| Mechanism | Trigger | Purpose |
-|-----------|---------|---------|
-| Poll | Every `VF_POLL_INTERVAL` (default 15s) | Baseline reliability — ensures config is always eventually consistent |
-| Push (SSE) | Server sends `config_changed` | Near-instant reaction to deploys (~200ms vs. up to 15s) |
-
-**Push does not replace poll.** Push messages only trigger an immediate poll cycle — the full config is always fetched from the config endpoint, never embedded in push messages. This keeps the push channel lightweight and the agent logic simple.
-
-**Push fallback**: If the primary push URL fails 3 times consecutively with short-lived connections, the agent switches to the derived fallback URL (`/api/agent/push`). This handles cases where the server provides a WebSocket proxy URL that becomes unavailable.
-
-**Push reconnection**: On any connection drop, the push client reconnects with exponential backoff (1s → 30s max). Each reconnection increments `vf_agent_push_reconnects_total`.
-
----
-
-## SSE push message types
-
-| Type | Payload | Action |
-|------|---------|--------|
-| `config_changed` | `pipelineId`, `reason` | Triggers an immediate `pollAndApply()` + debounced heartbeat |
-| `sample_request` | `requestId`, `pipelineId`, `componentKeys`, `limit` | Launches `vector tap` for each component; results sent to `/api/agent/samples` |
-| `tap_start` | `requestId`, `pipelineId`, `componentId` | Starts a long-running `vector tap` subprocess |
-| `tap_stop` | `requestId` | Cancels a running tap subprocess |
-| `action` | `action`, `targetVersion`, `downloadUrl`, `checksum` | Handles `self_update` or `restart` |
-| `poll_interval` | `intervalMs` | Immediately resets the poll ticker |
-
----
-
-## Metrics sidecar
-
-Every pipeline launched by the supervisor gets a second Vector config file (`.vf-metrics.yaml`) merged in alongside the user's config. The sidecar adds three components:
-
-```yaml
-sources:
- vf_internal_metrics: # Vector's own internal metrics
- type: internal_metrics
- vf_host_metrics: # CPU, memory, disk, network from the host
- type: host_metrics
-
-sinks:
- vf_metrics_exporter: # Prometheus exporter for the agent to scrape
- type: prometheus_exporter
- inputs: ["vf_internal_metrics", "vf_host_metrics"]
- address: "127.0.0.1:"
-
-api:
- enabled: true
- address: "127.0.0.1:" # Vector API for tap and sampling
-```
-
-The `vf_` prefix on component IDs is how the scraper distinguishes sidecar metrics from user pipeline metrics when computing pipeline totals.
-
----
-
-## Agent self-metrics
-
-As of v0.6.0, the agent exports its own health metrics at `:9090/metrics` (configurable via `VF_METRICS_PORT`):
-
-| Metric | Type | Description |
-|--------|------|-------------|
-| `vf_agent_poll_errors_total` | counter | Failed poll requests to the VF server |
-| `vf_agent_poll_duration_seconds` | gauge | Duration of the most recent poll cycle |
-| `vf_agent_push_reconnects_total` | counter | SSE push channel reconnection events |
-| `vf_agent_push_connected` | gauge | 1 if connected, 0 if disconnected |
-| `vf_agent_heartbeat_errors_total` | counter | Failed heartbeat sends |
-| `vf_agent_heartbeat_duration_seconds` | gauge | Duration of the most recent heartbeat send |
-| `vf_agent_pipelines_running` | gauge | Number of pipelines currently RUNNING |
-| `vf_agent_uptime_seconds` | gauge | Agent process uptime |
-
-A subset of these (poll errors, reconnects, push connected, pipelines running, uptime) is embedded in every heartbeat payload as `agentHealth` for server-side visibility without requiring a Prometheus scrape.
-
----
-
-## Configuration reference
-
-All configuration is via environment variables. There are no config files.
-
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `VF_URL` | *(required)* | VectorFlow server URL |
-| `VF_TOKEN` | *(required first run)* | Enrollment token (not needed after node-token is written to disk) |
-| `VF_DATA_DIR` | `/var/lib/vf-agent` | Storage for node-token, pipeline configs, and cert files |
-| `VF_VECTOR_BIN` | `vector` | Path to the Vector binary |
-| `VF_POLL_INTERVAL` | `5s` | Poll cycle duration (Go duration syntax: `5s`, `1m`, `30s`) |
-| `VF_LOG_FLUSH_INTERVAL` | `2s` | How often to flush pipeline log buffers to the server |
-| `VF_LOG_LEVEL` | `info` | Agent log verbosity: `debug`, `info`, `warn`, `error` |
-| `VF_NODE_LABELS` | *(none)* | Comma-separated `key=value` labels (e.g., `region=us-east-1,tier=prod`) |
-| `VF_METRICS_PORT` | `9090` | Port for agent self-metrics Prometheus endpoint; set to `0` to disable |
-
----
-
-## Concurrency model
-
-The agent deliberately minimises shared mutable state:
-
-- **Main goroutine** owns the event loop, `pollAndApply()`, and `sendHeartbeat()`. No locks needed for most fields.
-- **`supervisor.Supervisor`** uses a single `sync.Mutex` protecting its `processes` map and port counter.
-- **`sampleResults`** is the only field in `Agent` that is written from goroutines spawned by `processSampleRequests`. Protected by `Agent.mu`.
-- **`selfmetrics.Metrics`** uses `sync/atomic` for all counters and the push-connected gauge. Mutex only for the float64 duration gauges (atomic float64 requires wider-than-pointer-width CAS on 32-bit platforms).
-- **`push.Client`** uses a single mutex to protect the context cancel function.
-
-**Race detector**: All packages are tested with `go test -race ./...`.
diff --git a/docs/public/reference/agent-troubleshooting.md b/docs/public/reference/agent-troubleshooting.md
deleted file mode 100644
index b4d101c1..00000000
--- a/docs/public/reference/agent-troubleshooting.md
+++ /dev/null
@@ -1,302 +0,0 @@
-# Agent Troubleshooting
-
-This guide covers the most common operational issues with `vf-agent`. For a reference of all configuration options, see [Agent Reference](./agent.md). For an explanation of how the agent works internally, see [Agent Architecture](./agent-architecture.md).
-
----
-
-## 1. Agent not enrolling
-
-The agent enrolls once on first startup. If enrollment fails, it exits immediately with a non-zero status.
-
-### Diagnosis checklist
-
-```bash
-# Check the error message
-journalctl -u vf-agent -n 50 --no-pager
-
-# Or if running manually:
-vf-agent 2>&1 | head -20
-```
-
-### Common causes
-
-| Error message | Cause | Fix |
-|---------------|-------|-----|
-| `config error: VF_URL is required` | `VF_URL` env var not set | Set `VF_URL` to your server URL (e.g., `https://vectorflow.example.com`) |
-| `enrollment failed: ... connection refused` | Server unreachable | Verify the server is running and `VF_URL` is correct including the scheme (`https://`) |
-| `enrollment failed: (status 401)` | Invalid or expired enrollment token | Generate a new token in VectorFlow → Settings → Tokens |
-| `enrollment failed: (status 403)` | Token already used or maximum node count reached | Generate a new token or check your node limit |
-| `no node token found and VF_TOKEN is not set` | Re-enrollment needed but token not provided | Delete `/node-token` and restart with `VF_TOKEN` set, **or** re-set `VF_TOKEN` and restart |
-| `enrollment failed: ... certificate verify failed` | TLS certificate issue | Check that your server's TLS certificate is valid; use `curl -v $VF_URL` to diagnose |
-
-### Network connectivity test
-
-```bash
-# Verify the agent can reach the server
-curl -v "$VF_URL/api/health"
-
-# If using a proxy, ensure HTTP_PROXY / HTTPS_PROXY is set correctly
-```
-
-### Re-enrollment
-
-To force re-enrollment (for example, after replacing a node):
-
-```bash
-rm /var/lib/vf-agent/node-token
-VF_TOKEN=vf_enroll_newtoken... vf-agent
-```
-
----
-
-## 2. Pipeline stuck in STARTING
-
-After a pipeline is deployed, it shows **STARTING** in the fleet UI and never transitions to **RUNNING**.
-
-### How STARTING works
-
-The supervisor marks a pipeline RUNNING 2 seconds after the Vector process starts (a fixed startup grace period). If the process crashes within those 2 seconds, the status remains STARTING until the crash recovery goroutine restarts it.
-
-### Diagnosis
-
-```bash
-# Enable debug logging to see Vector startup details
-VF_LOG_LEVEL=debug vf-agent
-
-# Check if Vector is actually running
-ps aux | grep vector
-
-# Check agent stderr for crash messages
-journalctl -u vf-agent -n 100 --no-pager | grep -E "CRASH|error|failed"
-```
-
-### Common causes
-
-| Symptom in logs | Cause | Fix |
-|-----------------|-------|-----|
-| `exec: "vector": executable file not found` | Vector not installed or not on PATH | Install Vector or set `VF_VECTOR_BIN=/path/to/vector` |
-| Vector process exits immediately with config errors | Pipeline YAML is invalid | Check pipeline config in VectorFlow editor; look at Vector log output for `Configuration error:` |
-| `address already in use` | Port conflict on 8688+ | Check if another Vector process or service is using ports starting at 8688; restart agent to get new port assignments |
-| `failed to write sidecar config` | `VF_DATA_DIR` permissions | Ensure agent has write access to `VF_DATA_DIR/pipelines/` |
-| Pipeline starts then keeps crashing (CRASHED) | Runtime error in pipeline (e.g., source unreachable) | View pipeline logs in the VectorFlow UI or agent stdout/stderr |
-
-### Port conflict resolution
-
-The agent allocates ports sequentially starting at 8688. If another process is using those ports:
-
-```bash
-# Check what's using ports in the 8688+ range
-ss -tlnp | grep 868
-lsof -i :8688-8750
-```
-
-Restart the agent to get new port assignments from the next available sequence position.
-
-### Crash recovery backoff
-
-If a pipeline crashes repeatedly, the agent enters exponential backoff (1s → 2s → 4s → ... → 60s cap). A pipeline stuck in repeated crash/restart cycles will show alternating CRASHED/STARTING in the UI. To force a clean restart, stop and redeploy the pipeline from the VectorFlow UI.
-
----
-
-## 3. Metrics not appearing
-
-Pipeline metrics (events in/out, bytes, errors) show as zeros or are absent in the fleet dashboard.
-
-### Architecture context
-
-The agent scrapes Vector's Prometheus endpoint on `127.0.0.1:` during each heartbeat. If this scrape fails or returns no data, heartbeats will contain zero metrics.
-
-### Diagnosis
-
-```bash
-# Find the metrics port for a running pipeline (look in agent debug logs)
-VF_LOG_LEVEL=debug vf-agent 2>&1 | grep "metrics"
-
-# Try scraping manually (first pipeline uses port 8688)
-curl -s http://127.0.0.1:8688/metrics | head -30
-
-# Check if Vector is listening on that port
-ss -tlnp | grep 8688
-```
-
-### Common causes
-
-| Symptom | Cause | Fix |
-|---------|-------|-----|
-| Curl returns connection refused | Sidecar config write failed, or Vector API not started | Check `VF_DATA_DIR/pipelines/` for `.vf-metrics.yaml` files; check permissions |
-| Curl returns data but dashboard shows zeros | Metrics haven't accumulated yet | Wait for the first poll cycle; Vector metrics start at 0 and only appear after events flow through |
-| Host metrics missing but pipeline metrics present | `host_metrics` source failing | Check that the agent has access to `/proc`, `/sys`, `/dev/disk` (common in restricted containers) |
-| Sidecar config exists but Vector rejects it | Component ID collision (`vf_internal_metrics` already defined) | Rename conflicting components in your pipeline config |
-| Agent shows no metrics port | Pipeline in STARTING/CRASHED state | Metrics are only scraped for RUNNING pipelines |
-
-### Agent self-metrics not appearing
-
-If the agent's own metrics endpoint (`VF_METRICS_PORT`, default 9090) is not reachable:
-
-```bash
-# Check if the agent is listening on 9090
-curl http://127.0.0.1:9090/metrics
-
-# Disable with VF_METRICS_PORT=0 if port conflicts exist
-```
-
----
-
-## 4. High memory usage
-
-The agent or its Vector child processes are consuming unexpectedly high memory.
-
-### Agent memory
-
-The agent itself is lightweight (typically 10–30 MB RSS). If it's growing:
-
-- **Log buffer growth**: Each pipeline gets a 500-line ring buffer (`internal/logbuf`). This is bounded and won't grow unboundedly. If many pipelines are running, the total buffer is `500 lines × N pipelines`.
-- **Sample results**: If the server is sending many sample requests and the agent can't deliver results fast enough, `sampleResults` can accumulate. This is bounded by rate of sampling goroutines completing.
-- **Memory leak**: If agent memory grows without bound, enable debug logging to check for unusual activity and file an issue.
-
-```bash
-# Monitor agent memory over time
-while true; do ps -o pid,rss,vsz,comm -p $(pgrep vf-agent); sleep 5; done
-```
-
-### Vector process memory
-
-High memory in Vector child processes is almost always caused by pipeline configuration:
-
-| Cause | Symptom | Fix |
-|-------|---------|-----|
-| Buffered events (slow sink) | Memory grows over time | Check sink health; add backpressure or drop policies |
-| `global.data_dir` defaults | Vector writes to disk buffer | Set `data_dir` explicitly to a volume with sufficient space |
-| Many concurrent pipelines | Linear memory growth with pipeline count | Each Vector process is independent; reduce active pipelines or use larger nodes |
-| `host_metrics` scraping everything | High baseline memory | Restrict `host_metrics` collectors in the sidecar if not needed |
-
-```bash
-# Check memory per Vector process
-ps aux --sort=-%mem | grep vector
-```
-
----
-
-## 5. Push connection drops
-
-The SSE push connection drops frequently, causing delayed config updates.
-
-### Understanding push reconnection
-
-The push client maintains a persistent SSE connection to the server. On any drop, it reconnects with exponential backoff (1s → 30s max). Each reconnection increments `vf_agent_push_reconnects_total` and is logged at WARN level.
-
-Push drops are non-critical — the polling fallback ensures config updates still arrive within one poll interval.
-
-### Diagnosis
-
-```bash
-# Watch for push reconnection warnings in real time
-journalctl -u vf-agent -f | grep "push:"
-
-# Check the self-metrics endpoint for reconnect count
-curl http://127.0.0.1:9090/metrics | grep reconnect
-# vf_agent_push_reconnects_total 42
-
-# Check push connected status
-curl http://127.0.0.1:9090/metrics | grep connected
-# vf_agent_push_connected 1
-```
-
-### Common causes
-
-| Cause | Symptom | Fix |
-|-------|---------|-----|
-| Reverse proxy timeout | Connection drops every N minutes | Increase proxy idle/read timeout; set `proxy_read_timeout 3600` in nginx or equivalent |
-| Load balancer session affinity | Connection works then switches servers | Enable sticky sessions (same-server routing) for the push endpoint |
-| TLS handshake failures | Rapid reconnect loop with TLS errors | Check certificate validity and that the agent trusts the server CA |
-| Network interruptions | Sporadic drops correlated with network issues | Normal; push reconnects automatically — monitor `vf_agent_push_reconnects_total` |
-| Large push payload | Scanner buffer overflow (>256KB) | Reduce sample payload size; this is typically a server-side configuration issue |
-
-### Proxy configuration examples
-
-**nginx:**
-```nginx
-location /api/agent/push {
- proxy_pass http://vectorflow:3000;
- proxy_http_version 1.1;
- proxy_set_header Connection ''; # disable keep-alive timeout
- proxy_read_timeout 3600s;
- proxy_buffering off;
- chunked_transfer_encoding on;
-}
-```
-
-**Caddy:**
-```caddy
-reverse_proxy /api/agent/push vectorflow:3000 {
- flush_interval -1 # immediate flush (required for SSE)
- transport http {
- read_timeout 0 # no timeout
- }
-}
-```
-
-**Traefik:** Set `ReadTimeout` on the backend service, or use a `Middleware` with `InFlightReq` to avoid idle connection limits.
-
----
-
-## General diagnostics
-
-### Enable debug logging
-
-Debug logging shows every HTTP request, poll result, and pipeline event:
-
-```bash
-VF_LOG_LEVEL=debug vf-agent
-```
-
-Key things to look for:
-- `http request` / `http response` lines show the server communication
-- `poll complete` shows how many pipeline actions were taken
-- `heartbeat sent` confirms successful delivery
-- `push: connected` / `push: connection lost` shows SSE state
-
-### Check agent self-metrics
-
-The `/metrics` endpoint exposes counters that summarize agent health over its lifetime:
-
-```bash
-curl -s http://127.0.0.1:9090/metrics
-```
-
-Key counters:
-- `vf_agent_poll_errors_total`: Non-zero means the agent can't reach the server
-- `vf_agent_push_reconnects_total`: High value means frequent push drops
-- `vf_agent_heartbeat_errors_total`: Non-zero means heartbeats failing (usually same root cause as poll errors)
-- `vf_agent_pipelines_running`: Should match the number of deployed pipelines on this node
-
-### Fleet UI status mapping
-
-| Fleet UI status | Agent status string | Meaning |
-|-----------------|---------------------|---------|
-| Running | `RUNNING` | Vector process is live and healthy |
-| Starting | `STARTING` | Vector process started; 2s startup grace period |
-| Stopped | `STOPPED` | Pipeline cleanly stopped (undeploy) |
-| Crashed | `CRASHED` | Vector process exited unexpectedly; restart pending |
-| Unreachable | *(no heartbeat)* | Agent is not sending heartbeats; agent likely down |
-
-### Collect a support bundle
-
-When filing a bug report or support request, include:
-
-```bash
-# Agent version
-vf-agent --version
-
-# Agent logs (last 200 lines)
-journalctl -u vf-agent -n 200 --no-pager
-
-# Self-metrics snapshot
-curl -s http://127.0.0.1:9090/metrics
-
-# System info
-uname -a
-vector --version
-free -h
-df -h /var/lib/vf-agent
-```
diff --git a/docs/public/reference/agent.md b/docs/public/reference/agent.md
deleted file mode 100644
index 2bbdd410..00000000
--- a/docs/public/reference/agent.md
+++ /dev/null
@@ -1,423 +0,0 @@
-# Agent Reference
-
-The VectorFlow agent is a lightweight Go binary that runs on each node where you want to execute Vector pipelines. It has zero external dependencies -- a single binary is all you need. The agent communicates with the VectorFlow server to receive pipeline configurations, report status and metrics, and apply updates.
-
-See also: [Agent Architecture](./agent-architecture.md) — internal component map, port allocation, and concurrency model. [Agent Troubleshooting](./agent-troubleshooting.md) — step-by-step fixes for the most common operational issues.
-
-## Overview
-
-- **Single binary**: No runtime dependencies, no package managers. Download and run.
-- **Zero config files**: All configuration is via environment variables.
-- **Process-per-pipeline**: Each deployed pipeline runs as a separate Vector child process, providing isolation and independent lifecycle management.
-- **Stateless**: The agent stores only its node token on disk. All pipeline configuration comes from the server on every poll.
-
----
-
-## Lifecycle
-
-The agent follows a predictable lifecycle from first startup to steady-state operation:
-
-```
-┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌────────────┐
-│ Start │───▶│ Enroll │───▶│ Poll + Run │───▶│ Heartbeat │
-│ │ │ │ │ │ │ │
-│ Load env │ │ Send │ │ Fetch config │ │ Report │
-│ vars, │ │ hostname │ │ Start/stop │ │ pipeline │
-│ detect │ │ + token │ │ pipelines │ │ status, │
-│ Vector │ │ to server│ │ │ │ metrics, │
-└──────────┘ └──────────┘ └──────────────┘ │ logs │
- │ ▲ └─────┬──────┘
- │ │ │
- │ └──────────────────┘
- │ (every poll interval)
- │
- ┌─────▼──────┐
- │ Save node │
- │ token to │
- │ disk │
- └────────────┘
-```
-
-### Enrollment
-
-On first startup, the agent enrolls with the server using the enrollment token (`VF_TOKEN`). The server responds with a **node token** -- a unique credential for this specific agent instance. The node token is saved to `/node-token` and reused on subsequent starts. After enrollment, the `VF_TOKEN` is no longer needed.
-
-The enrollment request includes the agent's hostname, OS/architecture, agent version, and Vector version.
-
-### Polling
-
-After enrollment, the agent enters a poll loop. On each tick (default: every 15 seconds), it:
-
-1. **Fetches configuration** from the server (`GET /api/agent/config`)
-2. **Compares** the received pipeline configs against locally known state (by checksum)
-3. **Takes action**: starts new pipelines, restarts pipelines with changed configs, stops removed pipelines
-4. **Reconciles** orphaned config files on disk from previous runs
-5. **Processes** any pending sample requests or server-initiated actions (e.g., self-update)
-
-### Heartbeat
-
-After each poll, the agent sends a heartbeat (`POST /api/agent/heartbeat`) that includes:
-
-- Status of each running pipeline (RUNNING, STARTING, STOPPED, CRASHED)
-- Per-pipeline metrics scraped from Vector's Prometheus endpoint (events in/out, bytes, errors)
-- Per-component metrics for the visual editor node overlays
-- Host system metrics (CPU, memory, disk, network)
-- Recent stdout/stderr log lines from each pipeline process
-- Agent and Vector version information
-- Node labels (optional key-value metadata for selective deployment)
-
----
-
-## Environment variables
-
-| Variable | Required | Default | Description |
-|----------|----------|---------|-------------|
-| `VF_URL` | Yes | -- | VectorFlow server URL (e.g., `https://vectorflow.example.com`) |
-| `VF_TOKEN` | On first run | -- | Enrollment token from the VectorFlow UI. Not needed after initial enrollment. |
-| `VF_DATA_DIR` | No | `/var/lib/vf-agent` | Directory for node token, pipeline configs, and certificate files |
-| `VF_VECTOR_BIN` | No | `vector` | Path to the Vector binary. Use if Vector is not on the system `PATH`. |
-| `VF_POLL_INTERVAL` | No | `5s` | How often to poll the server for config changes. Accepts Go duration syntax (e.g., `10s`, `1m`). |
-| `VF_LOG_FLUSH_INTERVAL` | No | `2s` | How often to flush pipeline log buffers to the server. |
-| `VF_LOG_LEVEL` | No | `info` | Agent log level: `debug`, `info`, `warn`, `error` |
-| `VF_NODE_LABELS` | No | -- | Comma-separated key=value pairs reported to the server on each heartbeat (e.g., `region=us-east-1,tier=production`). Labels set via the UI take precedence over agent-reported values. Used for selective pipeline deployment. |
-| `VF_METRICS_PORT` | No | `9090` | Port for the agent's own Prometheus metrics endpoint (`/metrics`). Set to `0` to disable. |
-
-{% hint style="warning" %}
-`VF_URL` is the only strictly required variable. However, `VF_TOKEN` must be set on the first run for enrollment. After the agent writes its node token to disk, `VF_TOKEN` can be removed.
-{% endhint %}
-
----
-
-## CLI flags
-
-The agent accepts the following flags:
-
-| Flag | Description |
-|------|-------------|
-| `--version`, `-v` | Print the agent version and exit |
-| `--help`, `-h` | Show usage help including the environment variable reference |
-| `--channel ` | Set the update channel at first enrollment: `stable` (default) or `dev`. Only effective on initial startup — the channel cannot be changed afterward without re-installing the agent. Dev channel agents receive pre-release binaries and are tracked separately in the fleet UI. |
-
-All runtime configuration is via environment variables -- there are no flags for server URL, token, etc.
-
----
-
-## Agent communication protocol
-
-The agent communicates with the VectorFlow server over three HTTP endpoints. All requests use JSON. Authenticated requests include the node token as a Bearer token.
-
-### `POST /api/agent/enroll`
-
-Called once on first startup. No authentication required (the enrollment token is in the request body).
-
-**Request:**
-```json
-{
- "token": "vf_enroll_abc123...",
- "hostname": "web-server-01",
- "os": "linux/amd64",
- "agentVersion": "0.5.0",
- "vectorVersion": "vector 0.41.1 (x86_64-unknown-linux-gnu)"
-}
-```
-
-**Response:**
-```json
-{
- "nodeId": "clxyz789",
- "nodeToken": "vfn_abc123...",
- "environmentId": "clxyz456",
- "environmentName": "Production"
-}
-```
-
-### `GET /api/agent/config`
-
-Called on every poll cycle. Returns all deployed pipeline configurations for this node's environment.
-
-**Headers:** `Authorization: Bearer `
-
-**Response:**
-```json
-{
- "pipelines": [
- {
- "pipelineId": "clxyz001",
- "pipelineName": "syslog-to-s3",
- "version": 3,
- "configYaml": "sources:\n syslog_in:\n type: syslog\n ...",
- "checksum": "sha256:abc123...",
- "logLevel": "info",
- "secrets": {
- "VF_SECRET_AWS_KEY": "AKIAIOSFODNN7EXAMPLE"
- },
- "certFiles": [
- {
- "name": "ca-cert",
- "filename": "ca.pem",
- "data": ""
- }
- ]
- }
- ],
- "pollIntervalMs": 15000,
- "secretBackend": "BUILTIN",
- "sampleRequests": [],
- "pendingAction": null
-}
-```
-
-Key fields:
-- **`configYaml`**: The generated Vector YAML. Secret references are converted to environment variable placeholders (e.g., `${VF_SECRET_AWS_KEY}`) rather than containing decrypted values.
-- **`secrets`**: Pre-resolved secret values keyed by their `VF_SECRET_` environment variable name. The agent injects these as environment variables into the Vector process, where Vector's interpolation resolves the placeholders.
-- **`checksum`**: Includes both the YAML and the secrets dictionary, so rotating a secret triggers a pipeline restart even if the YAML itself hasn't changed.
-- **`certFiles`**: Certificate data written to `/certs/` before starting the pipeline.
-- **`pendingAction`**: Server-initiated action (currently only `self_update`).
-
-{% hint style="info" %}
-When a pipeline has a **node selector** configured (via the deploy dialog), the config response only includes pipelines whose selector labels match this node's labels. A pipeline with no node selector deploys to all nodes.
-{% endhint %}
-
-{% hint style="info" %}
-When a node is in **maintenance mode**, the config response returns an empty `pipelines` array. The agent stops all running pipelines but continues sending heartbeats. See [Fleet Management](../user-guide/fleet.md#maintenance-mode) for details.
-{% endhint %}
-
-### `POST /api/agent/heartbeat`
-
-Called after every poll. Sends status and metrics for all managed pipelines.
-
-**Headers:** `Authorization: Bearer `, `Content-Type: application/json`
-
-Key fields:
-- **`labels`** (optional): Key-value pairs describing this node. Labels set via the `VF_LABELS` environment variable are reported here. The server merges them with any labels set through the UI, with UI-set labels taking precedence.
-
-**Request:**
-```json
-{
- "pipelines": [
- {
- "pipelineId": "clxyz001",
- "version": 3,
- "status": "RUNNING",
- "pid": 12345,
- "uptimeSeconds": 3600,
- "eventsIn": 150000,
- "eventsOut": 148500,
- "bytesIn": 75000000,
- "bytesOut": 72000000,
- "errorsTotal": 12,
- "componentMetrics": [
- {
- "componentId": "syslog_in",
- "componentKind": "source",
- "receivedEvents": 150000,
- "sentEvents": 150000,
- "receivedBytes": 75000000
- }
- ],
- "recentLogs": ["2025-01-15T10:30:00Z INFO vector: Pipeline running"]
- }
- ],
- "hostMetrics": {
- "memoryTotalBytes": 8589934592,
- "memoryUsedBytes": 4294967296,
- "cpuSecondsTotal": 12345.67,
- "loadAvg1": 1.5
- },
- "agentVersion": "0.5.0",
- "vectorVersion": "vector 0.41.1",
- "deploymentMode": "STANDALONE",
- "labels": {
- "region": "us-east-1",
- "tier": "production"
- }
-}
-```
-
----
-
-## Process supervision
-
-The agent manages Vector processes with full lifecycle control:
-
-- **Start**: Spawns `vector --config .yaml --config .yaml.vf-metrics.yaml`. The second config file is a sidecar that adds internal metrics, host metrics, a Prometheus exporter, and the Vector API.
-- **Stop**: Sends `SIGTERM`, waits up to 30 seconds for graceful shutdown, then sends `SIGKILL` if needed.
-- **Restart**: Stops the running process then starts a new one with the updated config.
-- **Crash recovery**: If a Vector process exits unexpectedly, the agent automatically restarts it with exponential backoff (1s, 2s, 4s, ... up to 60s).
-
-### Environment injection
-
-Each Vector process receives:
-- `VECTOR_LOG=` -- controls Vector's log verbosity
-- All resolved secrets as environment variables with `VF_SECRET_` prefix (e.g., `VF_SECRET_AWS_KEY=value`)
-
-Pipeline YAML references secrets as `${VF_SECRET_NAME}` placeholders. Vector's built-in environment variable interpolation resolves these at startup, so secret values never appear in configuration files on disk.
-
-### Metrics sidecar
-
-The agent automatically generates a sidecar config for each pipeline that adds:
-- `vf_internal_metrics` source (Vector internal metrics)
-- `vf_host_metrics` source (host system metrics)
-- `vf_metrics_exporter` sink (Prometheus exporter on a dynamic port)
-- Vector API enabled on `127.0.0.1:`
-
-The agent scrapes the Prometheus endpoint on each heartbeat to collect per-component and host metrics.
-
----
-
-## Auto-update mechanism
-
-Standalone agents (not Docker) support in-place binary updates:
-
-1. An admin triggers an update from the VectorFlow UI, specifying a target version and download URL
-2. The server stores a `pendingAction` of type `self_update` on the node
-3. On the next poll, the agent receives the pending action
-4. The agent downloads the new binary to a temp file next to the current executable
-5. The SHA-256 checksum is verified against the expected value
-6. The temp file is atomically renamed over the current executable
-7. The agent re-executes itself via `syscall.Exec`, replacing the process in-place
-
-{% hint style="info" %}
-Docker agents ignore `self_update` actions. Update Docker agents by pulling a new image version instead.
-{% endhint %}
-
-### Update channels
-
-Agents operate on one of two update channels:
-
-| Channel | Version prefix | Description |
-|---------|---------------|-------------|
-| **stable** | `v1.2.3` | Production-ready releases. This is the default. |
-| **dev** | `dev-abc1234` | Pre-release builds for testing. Set via `--channel dev` at first startup. |
-
-The fleet UI displays the channel for each node. When triggering updates, VectorFlow targets the correct channel — stable agents receive the latest stable release, and dev agents receive the latest dev build. An agent's channel is determined by its version string prefix and cannot be changed without re-installing.
-
-{% hint style="info" %}
-Dev channel agents are intended for testing new agent features before rolling them out to production. They report a separate "latest version" in the fleet UI so you can track dev and stable fleets independently.
-{% endhint %}
-
----
-
-## Deployment mode detection
-
-The agent automatically detects whether it is running inside a container:
-
-- Checks for `/.dockerenv`
-- Inspects `/proc/1/cgroup` for `docker`, `containerd`, or `kubepods` entries
-
-The detected mode (`STANDALONE` or `DOCKER`) is reported in every heartbeat and displayed in the fleet UI.
-
----
-
-## Running as Non-Root
-
-By default the agent runs as root, which gives Vector access to the Docker socket and host filesystem. For environments that require non-root execution, both Docker and binary deployments support running as a dedicated user.
-
-### Docker
-
-Set the `VF_AGENT_USER` environment variable:
-
-```yaml
-services:
- vf-agent:
- image: ghcr.io/terrifiedbug/vectorflow-agent:latest
- environment:
- - VF_AGENT_USER=vfagent
- volumes:
- - agent-data:/var/lib/vf-agent
- - vector-data:/var/lib/vector
-```
-
-The entrypoint creates the user (if needed), sets ownership on data directories, and runs the agent as that user.
-
-### Binary (systemd)
-
-Use the `--user` flag during installation:
-
-```bash
-curl -sSfL .../install.sh | sudo bash -s -- \
- --url https://vf.example.com \
- --token abc123 \
- --user vfagent
-```
-
-Or run the installer interactively (from a terminal) and select non-root when prompted.
-
-The installer creates a system user, sets directory ownership, and configures the systemd unit with `User=` and `Group=` directives.
-
-### Granting Permissions
-
-When running as non-root, pipelines that access privileged resources will fail with permission errors. Grant the agent user access as needed:
-
-| Resource | Command |
-|----------|---------|
-| Docker socket (`docker_logs` source) | `sudo usermod -aG docker vfagent` |
-| Host log files (`file` source) | Ensure the user has read access to the paths |
-| Network ports below 1024 | Use `setcap` or a reverse proxy |
-
-Permission errors are displayed on the pipeline card in the dashboard.
-
-### Viewing the Running User
-
-The fleet node detail page shows "Running As" when the agent reports its user.
-
----
-
-## Data directory layout
-
-```
-/var/lib/vf-agent/ # VF_DATA_DIR
- node-token # Persisted node credential (0600)
- pipelines/
- .yaml # Pipeline config from server (0600)
- .yaml.vf-metrics.yaml # Auto-generated metrics sidecar
- certs/
- ca.pem # Certificate files (0600)
- server.crt
- server.key
-```
-
----
-
-## Troubleshooting
-
-### Agent won't enroll
-
-| Symptom | Cause | Fix |
-|---------|-------|-----|
-| `config error: VF_URL is required` | `VF_URL` not set | Set the `VF_URL` environment variable |
-| `enrollment failed: ... connection refused` | Server unreachable | Verify `VF_URL` is correct and the server is running |
-| `enrollment failed: (status 401)` | Invalid enrollment token | Generate a new enrollment token in the VectorFlow UI |
-| `enrollment failed: (status 403)` | Token already used or revoked | Generate a new enrollment token |
-| `no node token found at ... and VF_TOKEN is not set` | First run without `VF_TOKEN` | Set `VF_TOKEN` to the enrollment token from the UI |
-
-### Agent shows offline
-
-| Symptom | Cause | Fix |
-|---------|-------|-----|
-| Node shows "Unreachable" in fleet UI | Agent not sending heartbeats | Check agent process is running, check network connectivity to server |
-| Heartbeat errors in agent logs | Network issue or server down | Check `VF_URL`, firewalls, and server health |
-| Agent enrolled but no heartbeats | Node token was revoked | Re-enroll by deleting `/node-token` and restarting with a new `VF_TOKEN` |
-
-### Pipeline won't start
-
-| Symptom | Cause | Fix |
-|---------|-------|-----|
-| `start vector for pipeline ...: exec: "vector": executable file not found` | Vector binary not on PATH | Install Vector or set `VF_VECTOR_BIN` to the full path |
-| Pipeline status shows CRASHED | Vector config error or runtime crash | Check the pipeline logs in the VectorFlow UI or agent stderr |
-| Pipeline stuck in STARTING | Vector process started but may have issues | Check agent logs at `debug` level (`VF_LOG_LEVEL=debug`) |
-
-### Diagnostic logging
-
-Enable debug logging to see all HTTP requests, poll results, and pipeline actions:
-
-```bash
-VF_LOG_LEVEL=debug vf-agent
-```
-
-This logs:
-- Every HTTP request and response status to the server
-- Poll results including the number of pipeline actions taken
-- Pipeline start/stop/restart events with PIDs
-- Heartbeat payloads including pipeline and metrics data
-- Certificate file writes and sample request processing
diff --git a/docs/public/reference/api.md b/docs/public/reference/api.md
deleted file mode 100644
index ba92e082..00000000
--- a/docs/public/reference/api.md
+++ /dev/null
@@ -1,1322 +0,0 @@
-# API Reference
-
-VectorFlow exposes its API via [tRPC](https://trpc.io/) -- a type-safe RPC framework built on HTTP. All API calls go through a single endpoint at `/api/trpc`, rather than traditional REST paths. This page documents every router, its procedures, and how to call them programmatically.
-
-## Calling convention
-
-tRPC uses a URL-based calling convention where the procedure name is encoded in the path:
-
-```
-# Query (read operation) — HTTP GET
-GET /api/trpc/.?input=
-
-# Mutation (write operation) — HTTP POST
-POST /api/trpc/.
-Content-Type: application/json
-
-{"json": }
-```
-
-Responses are JSON-wrapped:
-
-```json
-{
- "result": {
- "data": {
- "json": { ... }
- }
- }
-}
-```
-
-VectorFlow uses [SuperJSON](https://github.com/blitz-js/superjson) as its serialization transformer, which means Date objects and BigInts are automatically serialized and deserialized. When calling the API with raw HTTP, you receive SuperJSON-encoded output -- dates appear as ISO strings with type annotations.
-
-### Example: list pipelines
-
-{% tabs %}
-{% tab title="curl" %}
-```bash
-# Query — GET with URL-encoded JSON input
-curl -s 'https://vectorflow.example.com/api/trpc/pipeline.list?input=%7B%22json%22%3A%7B%22environmentId%22%3A%22clxyz123%22%7D%7D' \
- -H 'Cookie: authjs.session-token='
-```
-{% endtab %}
-{% tab title="fetch" %}
-```typescript
-const input = encodeURIComponent(
- JSON.stringify({ json: { environmentId: "clxyz123" } })
-);
-
-const res = await fetch(
- `https://vectorflow.example.com/api/trpc/pipeline.list?input=${input}`,
- {
- headers: { Cookie: `authjs.session-token=${sessionToken}` },
- }
-);
-
-const { result } = await res.json();
-const pipelines = result.data.json;
-```
-{% endtab %}
-{% endtabs %}
-
-### Example: create a pipeline
-
-{% tabs %}
-{% tab title="curl" %}
-```bash
-# Mutation — POST with JSON body
-curl -s -X POST 'https://vectorflow.example.com/api/trpc/pipeline.create' \
- -H 'Content-Type: application/json' \
- -H 'Cookie: authjs.session-token=' \
- -d '{"json": {"name": "syslog-to-s3", "environmentId": "clxyz123"}}'
-```
-{% endtab %}
-{% tab title="fetch" %}
-```typescript
-const res = await fetch(
- "https://vectorflow.example.com/api/trpc/pipeline.create",
- {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Cookie: `authjs.session-token=${sessionToken}`,
- },
- body: JSON.stringify({
- json: { name: "syslog-to-s3", environmentId: "clxyz123" },
- }),
- }
-);
-
-const { result } = await res.json();
-const pipeline = result.data.json;
-```
-{% endtab %}
-{% endtabs %}
-
----
-
-## Authentication
-
-All API procedures (except the agent enrollment endpoint) require an authenticated session.
-
-VectorFlow supports two authentication methods:
-
-1. **Session cookies** -- Used by the web UI. When you sign in, an `authjs.session-token` cookie is set. Include this cookie in tRPC API requests.
-2. **Service account API keys** -- Used for programmatic access via the REST API (`/api/v1/*`). Pass the key as a Bearer token in the `Authorization` header.
-
-{% hint style="info" %}
-For CI/CD and automation, create a [Service Account](../operations/service-accounts.md) to get a dedicated API key with scoped permissions.
-{% endhint %}
-
-### Roles
-
-Every procedure enforces a minimum role. VectorFlow has three roles, in ascending order of privilege:
-
-| Role | Level | Description |
-|------|-------|-------------|
-| `VIEWER` | 0 | Read-only access to pipelines, fleet, metrics, and logs |
-| `EDITOR` | 1 | Create, update, deploy, and delete pipelines, secrets, and alerts |
-| `ADMIN` | 2 | Manage environments, teams, members, enrollment tokens, and agent revocation |
-
-Some procedures require **Super Admin** access -- this is a server-wide flag on the user account, separate from team roles.
-
----
-
-## Router index
-
-| Router | Prefix | Description |
-|--------|--------|-------------|
-| `pipeline` | `pipeline.*` | Pipeline CRUD, graph saving, versioning, deployment status, metrics, logs, event sampling |
-| `deploy` | `deploy.*` | Deploy preview, deploy to agents, undeploy |
-| `fleet` | `fleet.*` | Fleet node management, node logs, node metrics, agent updates |
-| `environment` | `environment.*` | Environment CRUD, enrollment tokens |
-| `alert` | `alert.*` | Alert rules, webhooks, alert events |
-| `template` | `template.*` | Pipeline template management |
-| `secret` | `secret.*` | Encrypted secret management |
-| `certificate` | `certificate.*` | TLS certificate management |
-| `dashboard` | `dashboard.*` | Dashboard statistics and chart data |
-| `team` | `team.*` | Team management, member roles |
-| `user` | `user.*` | User profile, password changes, TOTP setup |
-| `audit` | `audit.*` | Audit log queries |
-| `vrl` | `vrl.*` | VRL expression testing |
-| `vrlSnippet` | `vrlSnippet.*` | VRL snippet library |
-| `admin` | `admin.*` | User management, super admin operations |
-| `settings` | `settings.*` | System settings — OIDC, fleet, backup, SCIM (Super Admin) |
-| `metrics` | `metrics.*` | Pipeline and component metrics, live rates |
-| `validator` | `validator.*` | Pipeline config validation |
-| `serviceAccount` | `serviceAccount.*` | Service account API key management |
-| `userPreference` | `userPreference.*` | Per-user UI preferences |
-| `sharedComponent` | `sharedComponent.*` | Reusable pipeline components shared across pipelines |
-| `ai` | `ai.*` | AI assistant conversations and suggestions |
-| `pipelineGroup` | `pipelineGroup.*` | Pipeline folder organization |
-| `pipelineDependency` | `pipelineDependency.*` | Inter-pipeline dependency graph |
-| `promotion` | `promotion.*` | Cross-environment pipeline promotion with approval workflow |
-| `stagedRollout` | `stagedRollout.*` | Canary and staged pipeline deployments |
-| `nodeGroup` | `nodeGroup.*` | Node grouping with label-based criteria and health stats |
-| `webhookEndpoint` | `webhookEndpoint.*` | Outbound webhook endpoint management and delivery history |
-| `gitSync` | `gitSync.*` | GitOps sync status, jobs, and error tracking |
-| `migration` | `migration.*` | Config migration from Fluentd to Vector |
-| `analytics` | `analytics.*` | Cost analytics, per-pipeline breakdown, CSV export |
-| `costRecommendation` | `costRecommendation.*` | Cost optimization recommendations and analysis |
-| `anomaly` | `anomaly.*` | Anomaly detection events, acknowledgement, dismissal |
-| `filterPreset` | `filterPreset.*` | Saved filter presets for pipeline and fleet views |
-
----
-
-## Pipeline router
-
-Manage pipeline definitions, graphs, versions, and runtime data.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `pipeline.list` | query | VIEWER | `{ environmentId: string }` | List all pipelines in an environment |
-| `pipeline.get` | query | VIEWER | `{ id: string }` | Get a pipeline with its nodes, edges, and config change status |
-| `pipeline.create` | mutation | EDITOR | `{ name: string, description?: string, environmentId: string }` | Create a new draft pipeline |
-| `pipeline.update` | mutation | EDITOR | `{ id: string, name?: string, description?: string \| null }` | Update pipeline name or description |
-| `pipeline.delete` | mutation | EDITOR | `{ id: string }` | Delete a pipeline (undeploys first if deployed) |
-| `pipeline.clone` | mutation | EDITOR | `{ pipelineId: string }` | Clone a pipeline within the same environment |
-| `pipeline.promote` | mutation | EDITOR | `{ pipelineId: string, targetEnvironmentId: string, name?: string }` | Copy a pipeline to a different environment (strips secrets) |
-| `pipeline.saveGraph` | mutation | EDITOR | `{ pipelineId: string, nodes: Node[], edges: Edge[], globalConfig?: object }` | Save the visual pipeline graph |
-| `pipeline.versions` | query | VIEWER | `{ pipelineId: string }` | List all deployed versions of a pipeline |
-| `pipeline.getVersion` | query | VIEWER | `{ versionId: string }` | Get a specific version with its config YAML |
-| `pipeline.createVersion` | mutation | EDITOR | `{ pipelineId: string, configYaml: string, changelog?: string }` | Create a new pipeline version |
-| `pipeline.rollback` | mutation | EDITOR | `{ pipelineId: string, targetVersionId: string }` | Roll back to a previous version |
-| `pipeline.deploymentStatus` | query | VIEWER | `{ pipelineId: string }` | Get per-node deployment status for a pipeline |
-| `pipeline.metrics` | query | VIEWER | `{ pipelineId: string, hours?: number }` | Get pipeline metrics (events, bytes, errors) over time |
-| `pipeline.logs` | query | VIEWER | `{ pipelineId: string, cursor?: string, limit?: number, levels?: LogLevel[], nodeId?: string, since?: Date }` | Paginated pipeline logs |
-| `pipeline.requestSamples` | mutation | EDITOR | `{ pipelineId: string, componentKeys: string[], limit?: number }` | Request live event samples from running components |
-| `pipeline.sampleResult` | query | VIEWER | `{ requestId: string }` | Poll for event sample results |
-| `pipeline.eventSchemas` | query | VIEWER | `{ pipelineId: string }` | Get discovered event schemas per component |
-
-
-Pipeline name validation
-
-Pipeline names must match the pattern `^[a-zA-Z0-9][a-zA-Z0-9 _-]*$` and be between 1 and 100 characters long. The name must start with a letter or number and may contain letters, numbers, spaces, hyphens, and underscores.
-
-
-
-Node schema
-
-Each node in the `saveGraph` input:
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `string?` | Optional ID (auto-generated if omitted) |
-| `componentKey` | `string` | Auto-generated unique identifier within the pipeline (e.g., `syslog_k7xMp2nQ`). Must match `^[a-zA-Z_][a-zA-Z0-9_]*$` |
-| `displayName` | `string?` | Optional human-readable name for the component (e.g., "Syslog Source") |
-| `componentType` | `string` | Vector component type (e.g., `syslog`, `remap`, `aws_s3`) |
-| `kind` | `"SOURCE" \| "TRANSFORM" \| "SINK"` | Component category |
-| `config` | `object` | Component configuration fields |
-| `positionX` | `number` | X coordinate in the visual editor |
-| `positionY` | `number` | Y coordinate in the visual editor |
-| `disabled` | `boolean` | Whether the node is excluded from the generated config |
-
-
----
-
-## Deploy router
-
-Preview and execute pipeline deployments.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `deploy.preview` | query | VIEWER | `{ pipelineId: string }` | Generate and validate the YAML config, return diff against deployed version |
-| `deploy.agent` | mutation | EDITOR | `{ pipelineId: string, changelog: string }` | Deploy a pipeline -- validates config, creates a version, marks as deployed |
-| `deploy.undeploy` | mutation | EDITOR | `{ pipelineId: string }` | Undeploy a pipeline (agents stop it on next poll) |
-| `deploy.environmentInfo` | query | VIEWER | `{ pipelineId: string }` | Get the environment and node list for a pipeline |
-
----
-
-## Fleet router
-
-Manage agent nodes and view their status, logs, and metrics.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `fleet.list` | query | VIEWER | `{ environmentId: string }` | List all nodes in an environment |
-| `fleet.get` | query | VIEWER | `{ id: string }` | Get a node with its pipeline statuses |
-| `fleet.create` | mutation | EDITOR | `{ name: string, host: string, apiPort?: number, environmentId: string }` | Register a node manually |
-| `fleet.update` | mutation | EDITOR | `{ id: string, name?: string }` | Update node name |
-| `fleet.delete` | mutation | EDITOR | `{ id: string }` | Delete a node |
-| `fleet.revokeNode` | mutation | ADMIN | `{ id: string }` | Revoke a node's token (prevents further communication) |
-| `fleet.nodeLogs` | query | VIEWER | `{ nodeId: string, cursor?: string, limit?: number, levels?: LogLevel[], pipelineId?: string }` | Paginated logs for a node |
-| `fleet.nodeMetrics` | query | VIEWER | `{ nodeId: string, hours?: number }` | System metrics for a node (CPU, memory, disk, network) |
-| `fleet.triggerAgentUpdate` | mutation | ADMIN | `{ nodeId: string, targetVersion: string, downloadUrl: string, checksum: string }` | Trigger a self-update on a standalone agent |
-| `fleet.listWithPipelineStatus` | query | VIEWER | `{ environmentId: string }` | List nodes with per-pipeline deployment status |
-
----
-
-## Environment router
-
-Manage environments and enrollment tokens.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `environment.list` | query | VIEWER | `{ teamId: string }` | List environments for a team |
-| `environment.get` | query | VIEWER | `{ id: string }` | Get environment details including node count |
-| `environment.create` | mutation | EDITOR | `{ name: string, teamId: string }` | Create a new environment |
-| `environment.update` | mutation | EDITOR | `{ id: string, name?: string, secretBackend?: string, secretBackendConfig?: any }` | Update environment name or secret backend |
-| `environment.delete` | mutation | ADMIN | `{ id: string }` | Delete an environment and all its pipelines and nodes |
-| `environment.generateEnrollmentToken` | mutation | ADMIN | `{ environmentId: string }` | Generate a new enrollment token for agent enrollment |
-| `environment.revokeEnrollmentToken` | mutation | ADMIN | `{ environmentId: string }` | Revoke the enrollment token |
-
-
-Secret backend options
-
-The `secretBackend` field accepts one of:
-
-| Value | Description |
-|-------|-------------|
-| `BUILTIN` | Secrets encrypted in the VectorFlow database (default) |
-| `VAULT` | HashiCorp Vault |
-| `AWS_SM` | AWS Secrets Manager |
-| `EXEC` | External command execution |
-
-
----
-
-## Alert router
-
-Manage alert rules, webhook destinations, and view alert events.
-
-### Alert rules
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `alert.listRules` | query | VIEWER | `{ environmentId: string }` | List alert rules for an environment |
-| `alert.createRule` | mutation | EDITOR | `{ name: string, environmentId: string, pipelineId?: string, metric: AlertMetric, condition: AlertCondition, threshold: number, durationSeconds?: number, teamId: string }` | Create an alert rule |
-| `alert.updateRule` | mutation | EDITOR | `{ id: string, name?: string, enabled?: boolean, threshold?: number, durationSeconds?: number }` | Update an alert rule |
-| `alert.deleteRule` | mutation | EDITOR | `{ id: string }` | Delete an alert rule |
-
-
-AlertMetric values
-
-| Value | Description |
-|-------|-------------|
-| `node_unreachable` | Node has not sent a heartbeat |
-| `cpu_usage` | Host CPU utilization percentage |
-| `memory_usage` | Host memory utilization percentage |
-| `disk_usage` | Host disk utilization percentage |
-| `error_rate` | Pipeline error events per second |
-| `discarded_rate` | Pipeline discarded events per second |
-| `pipeline_crashed` | Pipeline process has crashed |
-
-
-
-AlertCondition values
-
-| Value | Description |
-|-------|-------------|
-| `gt` | Greater than threshold |
-| `lt` | Less than threshold |
-| `eq` | Equal to threshold |
-
-
-### Alert webhooks
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `alert.listWebhooks` | query | VIEWER | `{ environmentId: string }` | List webhook destinations |
-| `alert.createWebhook` | mutation | EDITOR | `{ environmentId: string, url: string, headers?: Record, hmacSecret?: string }` | Create a webhook |
-| `alert.updateWebhook` | mutation | EDITOR | `{ id: string, url?: string, headers?: Record \| null, hmacSecret?: string \| null, enabled?: boolean }` | Update a webhook |
-| `alert.deleteWebhook` | mutation | EDITOR | `{ id: string }` | Delete a webhook |
-| `alert.testWebhook` | mutation | EDITOR | `{ id: string }` | Send a test alert payload to a webhook |
-
-### Alert events
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `alert.listEvents` | query | VIEWER | `{ environmentId: string, limit?: number, cursor?: string }` | Paginated list of alert events |
-
----
-
-## Template router
-
-Manage reusable pipeline templates.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `template.list` | query | VIEWER | `{ teamId: string }` | List all templates for a team |
-| `template.get` | query | VIEWER | `{ id: string }` | Get a template with its nodes and edges |
-| `template.create` | mutation | EDITOR | `{ name: string, description: string, category: string, teamId: string, nodes: Node[], edges: Edge[] }` | Create a template |
-| `template.delete` | mutation | EDITOR | `{ id: string }` | Delete a template |
-
----
-
-## Secret router
-
-Manage encrypted secrets for pipeline configurations.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `secret.list` | query | VIEWER | `{ environmentId: string }` | List secrets (names only, no values) |
-| `secret.create` | mutation | EDITOR | `{ environmentId: string, name: string, value: string }` | Create a secret |
-| `secret.update` | mutation | EDITOR | `{ id: string, environmentId: string, value: string }` | Update a secret value |
-| `secret.delete` | mutation | EDITOR | `{ id: string, environmentId: string }` | Delete a secret |
-
-{% hint style="info" %}
-Secret values are never returned by the API. The `list` endpoint returns only names and timestamps. Values are encrypted at rest and only decrypted during pipeline deployment.
-{% endhint %}
-
----
-
-## Certificate router
-
-Manage TLS certificates for pipeline components.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `certificate.list` | query | VIEWER | `{ environmentId: string }` | List certificates (metadata only) |
-| `certificate.upload` | mutation | EDITOR | `{ environmentId: string, name: string, filename: string, fileType: "ca" \| "cert" \| "key", dataBase64: string }` | Upload a PEM-encoded certificate |
-| `certificate.delete` | mutation | EDITOR | `{ id: string, environmentId: string }` | Delete a certificate |
-
----
-
-## Dashboard router
-
-Fetch dashboard statistics and chart data.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `dashboard.stats` | query | VIEWER | `{ environmentId: string }` | Pipeline count, node count, fleet health, data reduction |
-| `dashboard.recentPipelines` | query | VIEWER | *(none)* | Last 5 recently updated pipelines |
-| `dashboard.recentAudit` | query | VIEWER | *(none)* | Last 10 audit log entries |
-| `dashboard.nodeCards` | query | VIEWER | *(none)* | Node overview cards with metrics and sparklines |
-| `dashboard.pipelineCards` | query | VIEWER | `{ environmentId: string }` | Pipeline cards with metrics, rates, and deployment status |
-| `dashboard.operationalOverview` | query | VIEWER | *(none)* | Unhealthy nodes, deployed pipelines, recent aggregate metrics |
-| `dashboard.chartMetrics` | query | VIEWER | `{ environmentId: string, nodeIds?: string[], pipelineIds?: string[], range?: "1h" \| "6h" \| "1d" \| "7d", groupBy?: "pipeline" \| "node" \| "aggregate" }` | Time-series chart data for dashboards |
-
----
-
-## Team router
-
-Manage teams and team membership.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `team.list` | query | VIEWER | *(none)* | List teams the current user belongs to |
-| `team.get` | query | VIEWER | `{ id: string }` | Get team details with members |
-| `team.myRole` | query | VIEWER | *(none)* | Get the current user's highest role |
-| `team.teamRole` | query | VIEWER | `{ teamId: string }` | Get the current user's role in a specific team |
-| `team.create` | mutation | Super Admin | `{ name: string }` | Create a new team |
-| `team.delete` | mutation | Super Admin | `{ teamId: string }` | Delete a team (must have no environments) |
-| `team.rename` | mutation | ADMIN | `{ teamId: string, name: string }` | Rename a team |
-| `team.addMember` | mutation | ADMIN | `{ teamId: string, email: string, role: "VIEWER" \| "EDITOR" \| "ADMIN" }` | Add a user to a team |
-| `team.removeMember` | mutation | ADMIN | `{ teamId: string, userId: string }` | Remove a member from a team |
-| `team.updateMemberRole` | mutation | ADMIN | `{ teamId: string, userId: string, role: "VIEWER" \| "EDITOR" \| "ADMIN" }` | Change a member's role |
-| `team.lockMember` | mutation | ADMIN | `{ teamId: string, userId: string }` | Lock a user account |
-| `team.unlockMember` | mutation | ADMIN | `{ teamId: string, userId: string }` | Unlock a user account |
-| `team.resetMemberPassword` | mutation | ADMIN | `{ teamId: string, userId: string }` | Reset a member's password (returns temporary password) |
-| `team.updateRequireTwoFactor` | mutation | ADMIN | `{ teamId: string, requireTwoFactor: boolean }` | Require 2FA for all team members |
-
----
-
-## User router
-
-Manage the current user's profile and two-factor authentication.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `user.me` | query | VIEWER | *(none)* | Get current user info (name, email, auth method, 2FA status) |
-| `user.changePassword` | mutation | VIEWER | `{ currentPassword: string, newPassword: string }` | Change password (min 8 characters) |
-| `user.updateProfile` | mutation | VIEWER | `{ name: string }` | Update display name |
-| `user.setupTotp` | mutation | VIEWER | *(none)* | Begin TOTP 2FA setup (returns QR URI and backup codes) |
-| `user.verifyAndEnableTotp` | mutation | VIEWER | `{ code: string }` | Verify a TOTP code and enable 2FA |
-| `user.disableTotp` | mutation | VIEWER | `{ code: string }` | Disable 2FA (requires valid TOTP or backup code) |
-
----
-
-## Audit router
-
-Query the audit log.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `audit.list` | query | VIEWER | `{ action?: string, userId?: string, entityType?: string, search?: string, teamId?: string, environmentId?: string, startDate?: string, endDate?: string, cursor?: string }` | Paginated, filterable audit log |
-| `audit.actions` | query | VIEWER | *(none)* | List distinct action values |
-| `audit.entityTypes` | query | VIEWER | *(none)* | List distinct entity type values |
-| `audit.users` | query | VIEWER | *(none)* | List users who appear in the audit log |
-
----
-
-## VRL router
-
-Test VRL (Vector Remap Language) expressions.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `vrl.test` | mutation | VIEWER | `{ source: string, input: string }` | Execute a VRL program against a test event and return the result |
-
----
-
-## VRL Snippet router
-
-Manage the VRL snippet library.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `vrlSnippet.list` | query | VIEWER | `{ teamId: string }` | List built-in and custom VRL snippets |
-| `vrlSnippet.create` | mutation | EDITOR | `{ teamId: string, name: string, description?: string, category: string, code: string }` | Create a custom snippet |
-| `vrlSnippet.update` | mutation | EDITOR | `{ id: string, name?: string, description?: string, category?: string, code?: string }` | Update a custom snippet |
-| `vrlSnippet.delete` | mutation | EDITOR | `{ id: string }` | Delete a custom snippet |
-
----
-
-## Admin router
-
-Manage platform users, super admin privileges, and team assignments. All procedures require **Super Admin** access.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `admin.listUsers` | query | Super Admin | *(none)* | List all users with team memberships and auth status |
-| `admin.createUser` | mutation | Super Admin | `{ email: string, name: string, teamId?: string, role?: Role }` | Create a local user account (returns generated password) |
-| `admin.deleteUser` | mutation | Super Admin | `{ userId: string }` | Delete a user and all their data |
-| `admin.toggleSuperAdmin` | mutation | Super Admin | `{ userId: string, isSuperAdmin: boolean }` | Grant or revoke super admin privileges |
-| `admin.assignToTeam` | mutation | Super Admin | `{ userId: string, teamId: string, role: "VIEWER" \| "EDITOR" \| "ADMIN" }` | Assign a user to a team with a role |
-| `admin.removeFromTeam` | mutation | Super Admin | `{ userId: string, teamId: string }` | Remove a user from a team |
-| `admin.lockUser` | mutation | Super Admin | `{ userId: string }` | Lock a user account (prevents sign-in) |
-| `admin.unlockUser` | mutation | Super Admin | `{ userId: string }` | Unlock a locked user account |
-| `admin.resetPassword` | mutation | Super Admin | `{ userId: string }` | Generate a temporary password for a user |
-| `admin.listTeams` | query | Super Admin | *(none)* | List all teams |
-
----
-
-## Settings router
-
-Configure system-wide settings. All procedures require **Super Admin** access except `checkVersion`.
-
-### General
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.get` | query | Super Admin | *(none)* | Get all system settings (secrets are masked) |
-| `settings.checkVersion` | query | VIEWER | `{ force?: boolean }` | Check server, agent, and dev agent versions |
-
-### OIDC
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.testOidc` | mutation | Super Admin | `{ issuer: string }` | Test OIDC provider discovery |
-| `settings.updateOidc` | mutation | Super Admin | `{ issuer: string, clientId: string, clientSecret: string, displayName?: string, tokenEndpointAuthMethod?: string }` | Update OIDC provider configuration |
-| `settings.updateOidcRoleMapping` | mutation | Super Admin | `{ defaultRole: Role, groupsClaim?: string, adminGroups?: string, editorGroups?: string }` | Map OIDC groups to VectorFlow roles |
-| `settings.updateOidcTeamMappings` | mutation | Super Admin | `{ mappings: TeamMapping[], defaultTeamId?: string, defaultRole: Role, groupSyncEnabled: boolean, groupsScope?: string, groupsClaim?: string }` | Map OIDC groups to teams with roles |
-
-
-TeamMapping schema
-
-Each entry in the `mappings` array:
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `group` | `string` | OIDC group name to match |
-| `teamId` | `string` | VectorFlow team to map to |
-| `role` | `"VIEWER" \| "EDITOR" \| "ADMIN"` | Role to assign in that team |
-
-
-### Fleet
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.updateFleet` | mutation | Super Admin | `{ pollIntervalMs: number, unhealthyThreshold: number, metricsRetentionDays?: number, logsRetentionDays?: number }` | Update fleet polling interval and data retention |
-
-### Anomaly detection
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.updateAnomalyConfig` | mutation | Super Admin | `{ baselineWindowDays: number, sigmaThreshold: number, minStddevFloorPercent: number, dedupWindowHours: number, enabledMetrics: string }` | Configure anomaly detection parameters (enabledMetrics is comma-delimited) |
-
-### Backup & restore
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.createBackup` | mutation | Super Admin | *(none)* | Create a database backup |
-| `settings.listBackups` | query | Super Admin | *(none)* | List available backups |
-| `settings.previewBackup` | query | Super Admin | `{ filename: string }` | Preview backup contents |
-| `settings.deleteBackup` | mutation | Super Admin | `{ filename: string }` | Delete a backup file |
-| `settings.restoreBackup` | mutation | Super Admin | `{ filename: string }` | Restore the database from a backup |
-| `settings.updateBackupSchedule` | mutation | Super Admin | `{ enabled: boolean, cron: string, retentionCount: number }` | Configure automatic backup schedule |
-
-### Storage backend
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.testS3Connection` | mutation | Super Admin | `{ bucket: string, region: string, prefix?: string, accessKeyId: string, secretAccessKey: string, endpoint?: string }` | Test S3 bucket connectivity |
-| `settings.updateStorageBackend` | mutation | Super Admin | `{ backend: "local" \| "s3", bucket?: string, region?: string, prefix?: string, accessKeyId?: string, secretAccessKey?: string, endpoint?: string }` | Switch backup storage between local filesystem and S3 |
-
-### SCIM
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `settings.updateScim` | mutation | Super Admin | `{ enabled: boolean }` | Enable or disable SCIM provisioning |
-| `settings.generateScimToken` | mutation | Super Admin | *(none)* | Generate a new SCIM bearer token |
-
----
-
-## Metrics router
-
-Query pipeline and component metrics from both the database (historical) and in-memory store (live).
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `metrics.getPipelineMetrics` | query | VIEWER | `{ pipelineId: string, minutes?: number }` | Aggregated pipeline metrics from database (default 60 min, max 10080) |
-| `metrics.getComponentMetrics` | query | VIEWER | `{ pipelineId: string, minutes?: number }` | Live per-component metrics from in-memory store (default 5 min, max 60) |
-| `metrics.getComponentLatencyHistory` | query | VIEWER | `{ pipelineId: string, minutes?: number }` | Per-component latency time series from database (default 60 min, max 1440) |
-| `metrics.getNodePipelineRates` | query | VIEWER | `{ nodeId: string }` | Per-pipeline event and byte rates for a specific node |
-| `metrics.getLiveRates` | query | VIEWER | `{ environmentId: string }` | Per-pipeline live event and byte rates for the pipelines table |
-
----
-
-## Analytics router
-
-Cost analytics and data volume tracking for pipelines, teams, and environments.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `analytics.costSummary` | query | VIEWER | `{ environmentId: string, range: Range }` | Aggregated cost summary for KPI cards |
-| `analytics.costByPipeline` | query | VIEWER | `{ environmentId: string, range: Range }` | Per-pipeline cost breakdown table |
-| `analytics.topPipelines` | query | VIEWER | `{ environmentId: string, range: Range }` | Top 5 pipelines by bytes processed |
-| `analytics.costByTeam` | query | VIEWER | `{ environmentId: string, range: Range }` | Team-level cost rollup for chargeback (super admins see all teams) |
-| `analytics.costByEnvironment` | query | VIEWER | `{ environmentId: string, range: Range }` | Environment comparison view |
-| `analytics.costTimeSeries` | query | VIEWER | `{ environmentId: string, range: Range, groupBy?: "pipeline" \| "team" }` | Volume trend time series, grouped by pipeline or team |
-| `analytics.costCsv` | query | VIEWER | `{ environmentId: string, range: Range }` | Export cost data as CSV |
-
-
-Range values
-
-All analytics queries accept the same range parameter:
-
-| Value | Description |
-|-------|-------------|
-| `1h` | Last hour |
-| `6h` | Last 6 hours |
-| `1d` | Last 24 hours |
-| `7d` | Last 7 days |
-| `30d` | Last 30 days |
-
-
----
-
-## Anomaly router
-
-View and manage anomaly detection events. Anomalies are automatically detected based on the [anomaly configuration](#anomaly-detection) in system settings.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `anomaly.list` | query | VIEWER | `{ environmentId: string, pipelineId?: string, status?: "open" \| "acknowledged" \| "dismissed", limit?: number, cursor?: string }` | Paginated list of anomalies with optional filters |
-| `anomaly.countByPipeline` | query | VIEWER | `{ environmentId: string }` | Count of open anomalies per pipeline |
-| `anomaly.maxSeverityByPipeline` | query | VIEWER | `{ environmentId: string }` | Maximum severity level per pipeline |
-| `anomaly.acknowledge` | mutation | EDITOR | `{ environmentId: string, anomalyId: string }` | Acknowledge an anomaly |
-| `anomaly.dismiss` | mutation | EDITOR | `{ environmentId: string, anomalyId: string }` | Dismiss an anomaly |
-
----
-
-## Cost Recommendation router
-
-View and act on cost optimization recommendations generated by automated analysis.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `costRecommendation.list` | query | VIEWER | `{ environmentId: string, status?: "PENDING" \| "DISMISSED" \| "APPLIED", limit?: number }` | List cost recommendations with optional status filter |
-| `costRecommendation.getById` | query | VIEWER | `{ environmentId: string, id: string }` | Get a recommendation with affected pipeline nodes |
-| `costRecommendation.summary` | query | VIEWER | `{ environmentId: string }` | Summary stats — pending count and estimated savings |
-| `costRecommendation.dismiss` | mutation | EDITOR | `{ environmentId: string, id: string }` | Dismiss a recommendation |
-| `costRecommendation.markApplied` | mutation | EDITOR | `{ environmentId: string, id: string }` | Mark a recommendation as applied |
-| `costRecommendation.triggerAnalysis` | mutation | ADMIN | `{ environmentId: string }` | Manually trigger cost analysis |
-
----
-
-## Validator router
-
-Validate pipeline configuration YAML.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `validator.validate` | mutation | VIEWER | `{ yaml: string }` | Validate a YAML configuration string and return any errors |
-
----
-
-## Pipeline Group router
-
-Organize pipelines into nested folder groups (max 3 levels deep).
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `pipelineGroup.list` | query | VIEWER | `{ environmentId: string }` | List all pipeline groups in an environment |
-| `pipelineGroup.create` | mutation | EDITOR | `{ environmentId: string, name: string, color?: string, parentId?: string }` | Create a pipeline group (unique name per parent, max 3-level nesting) |
-| `pipelineGroup.update` | mutation | EDITOR | `{ id: string, name?: string, color?: string, parentId?: string }` | Update group name, color, or parent |
-| `pipelineGroup.delete` | mutation | ADMIN | `{ id: string }` | Delete a group (child groups are orphaned to root) |
-
----
-
-## Pipeline Dependency router
-
-Define and query dependencies between pipelines. Used to warn before deploying or undeploying pipelines with active dependencies.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `pipelineDependency.list` | query | VIEWER | `{ pipelineId: string }` | List upstream dependencies for a pipeline |
-| `pipelineDependency.listCandidates` | query | VIEWER | `{ pipelineId: string, environmentId: string }` | List pipelines available to link as dependencies |
-| `pipelineDependency.add` | mutation | EDITOR | `{ upstreamId: string, downstreamId: string, description?: string }` | Add a dependency between two pipelines |
-| `pipelineDependency.remove` | mutation | EDITOR | `{ id: string }` | Remove a dependency |
-| `pipelineDependency.deployWarnings` | query | VIEWER | `{ pipelineId: string }` | List undeployed upstream dependencies (deploy warnings) |
-| `pipelineDependency.undeployWarnings` | query | VIEWER | `{ pipelineId: string }` | List deployed downstream dependents (undeploy warnings) |
-| `pipelineDependency.graph` | query | VIEWER | `{ environmentId: string }` | Full dependency graph for an environment |
-
----
-
-## Promotion router
-
-Promote pipelines across environments with an optional approval workflow.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `promotion.preflight` | query | VIEWER | `{ pipelineId: string, targetEnvironmentId: string, name?: string }` | Check promotion readiness — missing secrets, name collisions |
-| `promotion.diffPreview` | query | VIEWER | `{ pipelineId: string }` | Generate a YAML diff between source and target |
-| `promotion.initiate` | mutation | EDITOR | `{ pipelineId: string, targetEnvironmentId: string, name?: string }` | Initiate a promotion request |
-| `promotion.approve` | mutation | EDITOR | `{ requestId: string }` | Approve a pending promotion (self-approval blocked) |
-| `promotion.reject` | mutation | EDITOR | `{ requestId: string, note?: string }` | Reject a pending promotion |
-| `promotion.cancel` | mutation | EDITOR | `{ requestId: string }` | Cancel a promotion (only the original promoter) |
-| `promotion.history` | query | VIEWER | `{ pipelineId: string }` | List promotion history (last 20 requests) |
-
-
-Promotion statuses
-
-| Status | Description |
-|--------|-------------|
-| `PENDING` | Awaiting approval from another team member |
-| `DEPLOYED` | Auto-approved and deployed to the target environment |
-| `AWAITING_PR_MERGE` | GitOps mode — waiting for the generated PR to be merged |
-| `REJECTED` | Rejected by a reviewer |
-| `CANCELLED` | Cancelled by the original promoter |
-
-
----
-
-## Staged Rollout router
-
-Perform canary deployments by rolling out pipeline changes to a subset of nodes before broadening to the full fleet.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `stagedRollout.create` | mutation | EDITOR | `{ pipelineId: string, canarySelector: object, healthCheckWindowMinutes: number, changelog: string }` | Create a staged rollout (deploys to canary nodes first) |
-| `stagedRollout.broaden` | mutation | EDITOR | `{ rolloutId: string }` | Broaden the rollout to additional nodes |
-| `stagedRollout.rollback` | mutation | EDITOR | `{ rolloutId: string }` | Roll back the staged rollout |
-| `stagedRollout.getActive` | query | VIEWER | `{ pipelineId: string }` | Get the active rollout for a pipeline (if any) |
-| `stagedRollout.list` | query | VIEWER | `{ pipelineId: string }` | List recent rollouts (last 10) |
-
-
-Rollout statuses
-
-| Status | Description |
-|--------|-------------|
-| `CANARY_DEPLOYED` | Deployed to canary nodes, awaiting health check or broadening |
-| `HEALTH_CHECK` | Health check window in progress |
-| `BROADENED` | Rolled out to all nodes |
-| `ROLLED_BACK` | Rolled back to previous version |
-
-
----
-
-## Node Group router
-
-Organize fleet nodes into groups based on label criteria. Groups provide aggregated health statistics and compliance tracking.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `nodeGroup.list` | query | VIEWER | `{ environmentId: string }` | List node groups in an environment |
-| `nodeGroup.create` | mutation | ADMIN | `{ environmentId: string, name: string, criteria: Record, labelTemplate?: Record, requiredLabels?: string[] }` | Create a node group with label-based membership criteria |
-| `nodeGroup.update` | mutation | ADMIN | `{ id: string, name?: string, criteria?: Record, labelTemplate?: Record, requiredLabels?: string[] }` | Update group properties |
-| `nodeGroup.delete` | mutation | ADMIN | `{ id: string }` | Delete a node group |
-| `nodeGroup.groupHealthStats` | query | VIEWER | `{ environmentId: string }` | Aggregated health stats per group (includes `__ungrouped__` synthetic entry) |
-| `nodeGroup.nodesInGroup` | query | VIEWER | `{ groupId: string, environmentId: string }` | List nodes in a group with status, labels, and compliance info |
-
----
-
-## Service Account router
-
-Manage service accounts for programmatic API access via the [REST API](#rest-api-v1).
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `serviceAccount.list` | query | ADMIN | `{ environmentId: string }` | List service accounts for an environment |
-| `serviceAccount.create` | mutation | ADMIN | `{ environmentId: string, name: string, description?: string, permissions: Permission[], expiresInDays?: number }` | Create a service account (returns the raw API key once) |
-| `serviceAccount.revoke` | mutation | ADMIN | `{ id: string }` | Disable a service account |
-| `serviceAccount.delete` | mutation | ADMIN | `{ id: string }` | Permanently delete a service account |
-
-
-Permission values
-
-| Permission | Description |
-|------------|-------------|
-| `pipelines.read` | Read pipeline definitions, versions, and metrics |
-| `pipelines.deploy` | Deploy, undeploy, and rollback pipelines |
-| `nodes.read` | Read node status, logs, and metrics |
-| `nodes.manage` | Manage nodes (maintenance mode, updates) |
-| `secrets.read` | List secret names |
-| `secrets.manage` | Create, update, and delete secrets |
-| `alerts.read` | Read alert rules and events |
-| `alerts.manage` | Create, update, and delete alert rules |
-| `audit.read` | Read audit log |
-
-
-{% hint style="info" %}
-The raw API key is only returned once during creation. Store it securely — it cannot be retrieved later. Keys are prefixed with `vf_` and hashed (SHA-256) before storage.
-{% endhint %}
-
----
-
-## Webhook Endpoint router
-
-Manage outbound webhook endpoints for alert notifications. See also [Outbound Webhooks](../operations/outbound-webhooks.md).
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `webhookEndpoint.list` | query | VIEWER | `{ teamId: string }` | List webhook endpoints (secrets excluded) |
-| `webhookEndpoint.create` | mutation | ADMIN | `{ teamId: string, name: string, url: string, eventTypes: AlertMetric[], secret?: string }` | Create a webhook endpoint (secret is encrypted at rest) |
-| `webhookEndpoint.update` | mutation | ADMIN | `{ id: string, teamId: string, name?: string, url?: string, eventTypes?: AlertMetric[], secret?: string }` | Update a webhook endpoint |
-| `webhookEndpoint.delete` | mutation | ADMIN | `{ id: string, teamId: string }` | Delete a webhook endpoint and its delivery history |
-| `webhookEndpoint.toggleEnabled` | mutation | ADMIN | `{ id: string, teamId: string }` | Toggle the endpoint enabled/disabled |
-| `webhookEndpoint.testDelivery` | mutation | ADMIN | `{ id: string, teamId: string }` | Send a test delivery to the endpoint |
-| `webhookEndpoint.listDeliveries` | query | VIEWER | `{ webhookEndpointId: string, teamId: string, take?: number, skip?: number }` | Paginated delivery history (default 20, max 100) |
-
----
-
-## Git Sync router
-
-Monitor and manage GitOps synchronization. See also [GitOps](../operations/gitops.md).
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `gitSync.status` | query | VIEWER | `{ environmentId: string }` | Get sync status summary (repo URL, branch, mode, pending/failed counts, last sync) |
-| `gitSync.jobs` | query | VIEWER | `{ environmentId: string, status?: "pending" \| "completed" \| "failed", limit?: number }` | List recent sync jobs (default 25, max 100) |
-| `gitSync.retryJob` | mutation | EDITOR | `{ jobId: string }` | Retry a single failed sync job |
-| `gitSync.retryAllFailed` | mutation | EDITOR | `{ environmentId: string }` | Retry all failed sync jobs |
-| `gitSync.importErrors` | query | VIEWER | `{ environmentId: string, limit?: number }` | Get recent import errors from the audit log (default 10, max 50) |
-
----
-
-## Migration router
-
-Migrate pipeline configurations from other platforms to VectorFlow. Currently supports Fluentd.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `migration.list` | query | VIEWER | `{ teamId: string }` | List migration projects for a team |
-| `migration.get` | query | VIEWER | `{ id: string, teamId: string }` | Get a migration project with its blocks and status |
-| `migration.create` | mutation | EDITOR | `{ teamId: string, name: string, platform: "FLUENTD", originalConfig: string }` | Create a migration project (config max 500 KB) |
-| `migration.delete` | mutation | EDITOR | `{ id: string, teamId: string }` | Delete a migration project |
-| `migration.parse` | mutation | EDITOR | `{ id: string, teamId: string }` | Parse the original config into translatable blocks |
-| `migration.translate` | mutation | EDITOR | `{ id: string, teamId: string }` | Translate all blocks to Vector config via AI |
-| `migration.retranslateBlock` | mutation | EDITOR | `{ id: string, teamId: string, blockId: string }` | Re-translate a single block |
-| `migration.updateBlockConfig` | mutation | EDITOR | `{ id: string, teamId: string, blockId: string, config: Record }` | Manually edit a translated block's config |
-| `migration.validate` | mutation | EDITOR | `{ id: string, teamId: string }` | Validate the translated configuration |
-| `migration.generate` | mutation | EDITOR | `{ id: string, teamId: string, environmentId: string, pipelineName: string }` | Generate a VectorFlow pipeline from the translated config |
-
-{% hint style="info" %}
-The `translate` procedure requires an AI provider to be configured for the team. The migration workflow is: **create** → **parse** → **translate** → (optional: edit blocks) → **validate** → **generate**.
-{% endhint %}
-
----
-
-## AI router
-
-Manage AI assistant conversations for pipeline building, debugging, and VRL authoring.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `ai.getConversation` | query | VIEWER | `{ pipelineId: string }` | Get the AI conversation for a pipeline |
-| `ai.startNewConversation` | mutation | EDITOR | `{ pipelineId: string }` | Start a new AI conversation (replaces existing) |
-| `ai.markSuggestionsApplied` | mutation | EDITOR | `{ pipelineId: string, conversationId: string, messageId: string, suggestionIds: string[] }` | Mark AI suggestions as applied to the pipeline |
-| `ai.getDebugConversation` | query | VIEWER | `{ pipelineId: string }` | Get the debug-mode AI conversation |
-| `ai.getVrlConversation` | query | VIEWER | `{ pipelineId: string, componentKey: string }` | Get the VRL assistant conversation for a component |
-| `ai.markVrlSuggestionsApplied` | mutation | EDITOR | `{ pipelineId: string, conversationId: string, messageId: string, suggestionIds: string[] }` | Mark VRL suggestions as applied |
-
----
-
-## User Preference router
-
-Store and retrieve per-user UI preferences (e.g., theme, default views, collapsed sidebar sections).
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `userPreference.get` | query | VIEWER | *(none)* | Get all preferences for the current user |
-| `userPreference.set` | mutation | VIEWER | `{ key: string, value: string }` | Set a preference (key max 100 chars, value max 500 chars) |
-| `userPreference.delete` | mutation | VIEWER | `{ key: string }` | Delete a preference |
-
----
-
-## Shared Component router
-
-Manage reusable pipeline components that can be linked across multiple pipelines. When a shared component is updated, linked pipelines show a staleness indicator until the update is accepted.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `sharedComponent.list` | query | VIEWER | `{ environmentId: string }` | List shared components with linked pipeline count |
-| `sharedComponent.getById` | query | VIEWER | `{ id: string, environmentId: string }` | Get a component with linked pipeline details and staleness info |
-| `sharedComponent.create` | mutation | EDITOR | `{ environmentId: string, name: string, description?: string, componentType: string, kind: "SOURCE" \| "TRANSFORM" \| "SINK", config: object }` | Create a shared component |
-| `sharedComponent.createFromNode` | mutation | EDITOR | `{ nodeId: string, pipelineId: string, name: string, description?: string, environmentId: string }` | Create a shared component from an existing pipeline node |
-| `sharedComponent.update` | mutation | EDITOR | `{ id: string, environmentId: string, name?: string, description?: string, config?: object }` | Update a shared component (config changes bump the version) |
-| `sharedComponent.delete` | mutation | EDITOR | `{ id: string, environmentId: string }` | Delete a shared component |
-| `sharedComponent.acceptUpdate` | mutation | EDITOR | `{ nodeId: string, pipelineId: string }` | Accept the latest shared component config for a single node |
-| `sharedComponent.acceptUpdateBulk` | mutation | EDITOR | `{ pipelineId: string }` | Accept updates for all stale nodes in a pipeline |
-| `sharedComponent.unlink` | mutation | EDITOR | `{ nodeId: string, pipelineId: string }` | Unlink a node from its shared component (keeps current config) |
-| `sharedComponent.linkExisting` | mutation | EDITOR | `{ nodeId: string, pipelineId: string, sharedComponentId: string }` | Link a node to an existing shared component |
-
----
-
-## Filter Preset router
-
-Save and manage filter presets for the pipeline list and fleet matrix views.
-
-| Procedure | Type | Min Role | Input | Description |
-|-----------|------|----------|-------|-------------|
-| `filterPreset.list` | query | VIEWER | `{ environmentId: string, scope: "pipeline_list" \| "fleet_matrix" }` | List filter presets for a scope |
-| `filterPreset.create` | mutation | EDITOR | `{ environmentId: string, name: string, scope: "pipeline_list" \| "fleet_matrix", filters: Record, isDefault?: boolean }` | Create a filter preset (max 20 per scope) |
-| `filterPreset.update` | mutation | EDITOR | `{ environmentId: string, id: string, name?: string, filters?: Record }` | Update a preset |
-| `filterPreset.delete` | mutation | EDITOR | `{ environmentId: string, id: string }` | Delete a preset |
-| `filterPreset.setDefault` | mutation | EDITOR | `{ environmentId: string, id: string, scope: "pipeline_list" \| "fleet_matrix" }` | Set a preset as the default for its scope |
-| `filterPreset.clearDefault` | mutation | EDITOR | `{ environmentId: string, scope: "pipeline_list" \| "fleet_matrix" }` | Clear the default preset for a scope |
-
----
-
-## Error handling
-
-tRPC errors are returned with a standard error shape:
-
-```json
-{
- "error": {
- "json": {
- "message": "Pipeline not found",
- "code": -32004,
- "data": {
- "code": "NOT_FOUND",
- "httpStatus": 404
- }
- }
- }
-}
-```
-
-Common error codes:
-
-| tRPC Code | HTTP Status | Meaning |
-|-----------|-------------|---------|
-| `UNAUTHORIZED` | 401 | Not signed in |
-| `FORBIDDEN` | 403 | Insufficient role or not a team member |
-| `NOT_FOUND` | 404 | Resource does not exist |
-| `BAD_REQUEST` | 400 | Invalid input |
-| `CONFLICT` | 409 | Resource already exists (duplicate name) |
-| `PRECONDITION_FAILED` | 412 | Operation requires a precondition (e.g., pipeline must be deployed) |
-
----
-
-## OpenAPI Specification
-
-VectorFlow provides a machine-readable [OpenAPI 3.1](https://spec.openapis.org/oas/v3.1.0) specification covering all REST v1 endpoints and key tRPC procedures.
-
-### Fetching the spec
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/openapi.json | jq .info
-```
-
-The spec is served at `/api/v1/openapi.json` with CORS enabled — you can fetch it from any origin without credentials.
-
-### Importing into tools
-
-**Postman:** File > Import > paste URL `https://vectorflow.example.com/api/v1/openapi.json`
-
-**Swagger UI / Stoplight:** Point to the spec URL or paste the JSON content.
-
-### Client generation
-
-Generate a typed API client in any language using [openapi-generator](https://openapi-generator.tech/):
-
-```bash
-npx @openapitools/openapi-generator-cli generate \
- -i https://vectorflow.example.com/api/v1/openapi.json \
- -g python \
- -o ./vectorflow-client
-```
-
-### What's included
-
-The spec documents two API surfaces:
-
-| Surface | Auth | Endpoints |
-|---------|------|-----------|
-| REST v1 (`/api/v1/*`) | Service account Bearer token | Pipeline CRUD, deploy, rollback, nodes, secrets, alerts, audit |
-| tRPC (`/api/trpc/*`) | Session cookie | Pipeline management, fleet, environments, secrets, deploy, alerts, service accounts |
-
-{% hint style="info" %}
-**tRPC encoding note:** tRPC endpoints use [SuperJSON](https://github.com/blitz-js/superjson) encoding. For queries, input is URL-encoded JSON in `?input=` (wrap as `{"json": }`). For mutations, the body is `{"json": }`. Using a tRPC client is recommended for full type safety; the OpenAPI spec is provided for discoverability and non-TypeScript integrations.
-{% endhint %}
-
----
-
-## REST API (v1)
-
-The REST API provides a standard HTTP interface for automation and CI/CD. All endpoints require a [Service Account](../operations/service-accounts.md) API key.
-
-### Authentication
-
-Include your API key in the `Authorization` header:
-
-```bash
-curl -H "Authorization: Bearer vf_live_abc123..." \
- https://vectorflow.example.com/api/v1/pipelines
-```
-
-Responses use standard HTTP status codes and return JSON:
-
-```json
-{ "error": "Unauthorized" } // 401
-{ "error": "Forbidden" } // 403
-{ "error": "Pipeline not found" } // 404
-```
-
-### Service Account Management
-
-Service accounts are managed via the tRPC API (Settings UI) or programmatically:
-
-| Procedure | Type | Min Role | Description |
-|-----------|------|----------|-------------|
-| `serviceAccount.list` | query | ADMIN | List service accounts for an environment |
-| `serviceAccount.create` | mutation | ADMIN | Create a service account (returns raw key once) |
-| `serviceAccount.revoke` | mutation | ADMIN | Disable a service account |
-| `serviceAccount.delete` | mutation | ADMIN | Permanently delete a service account |
-
----
-
-### Pipelines
-
-#### List pipelines
-
-```bash
-GET /api/v1/pipelines
-```
-
-Permission: `pipelines.read`
-
-Returns all pipelines in the service account's environment.
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/pipelines \
- -H "Authorization: Bearer vf_live_..."
-```
-
-Response:
-
-```json
-{
- "pipelines": [
- {
- "id": "clxyz123",
- "name": "syslog-to-s3",
- "description": "Ship syslog to S3",
- "isDraft": false,
- "deployedAt": "2026-03-01T12:00:00.000Z",
- "createdAt": "2026-02-15T10:00:00.000Z",
- "updatedAt": "2026-03-01T12:00:00.000Z"
- }
- ]
-}
-```
-
-#### Get pipeline details
-
-```bash
-GET /api/v1/pipelines/:id
-```
-
-Permission: `pipelines.read`
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/pipelines/clxyz123 \
- -H "Authorization: Bearer vf_live_..."
-```
-
-#### Deploy pipeline
-
-```bash
-POST /api/v1/pipelines/:id/deploy
-```
-
-Permission: `pipelines.deploy`
-
-Validates the pipeline config, creates a new version, and marks it as deployed. Agents pick up the change on their next poll.
-
-```bash
-curl -s -X POST https://vectorflow.example.com/api/v1/pipelines/clxyz123/deploy \
- -H "Authorization: Bearer vf_live_..." \
- -H "Content-Type: application/json" \
- -d '{"changelog": "Deployed from CI"}'
-```
-
-Response:
-
-```json
-{
- "success": true,
- "versionId": "clversion456",
- "versionNumber": 5
-}
-```
-
-#### Undeploy pipeline
-
-```bash
-POST /api/v1/pipelines/:id/undeploy
-```
-
-Permission: `pipelines.deploy`
-
-Marks the pipeline as a draft. Agents stop running it on their next poll.
-
-```bash
-curl -s -X POST https://vectorflow.example.com/api/v1/pipelines/clxyz123/undeploy \
- -H "Authorization: Bearer vf_live_..."
-```
-
-#### List pipeline versions
-
-```bash
-GET /api/v1/pipelines/:id/versions
-```
-
-Permission: `pipelines.read`
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/pipelines/clxyz123/versions \
- -H "Authorization: Bearer vf_live_..."
-```
-
-Response:
-
-```json
-{
- "versions": [
- {
- "id": "clversion456",
- "version": 5,
- "changelog": "Added error handling transform",
- "createdById": "user123",
- "createdAt": "2026-03-01T12:00:00.000Z"
- }
- ]
-}
-```
-
-#### Rollback pipeline
-
-```bash
-POST /api/v1/pipelines/:id/rollback
-```
-
-Permission: `pipelines.deploy`
-
-Rolls back to a previous version by creating a new version with the target version's config.
-
-```bash
-curl -s -X POST https://vectorflow.example.com/api/v1/pipelines/clxyz123/rollback \
- -H "Authorization: Bearer vf_live_..." \
- -H "Content-Type: application/json" \
- -d '{"targetVersionId": "clversion123"}'
-```
-
----
-
-### Nodes
-
-#### List nodes
-
-```bash
-GET /api/v1/nodes
-GET /api/v1/nodes?label=role:production
-```
-
-Permission: `nodes.read`
-
-Supports optional label filtering via `?label=key:value`.
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/nodes \
- -H "Authorization: Bearer vf_live_..."
-```
-
-#### Get node details
-
-```bash
-GET /api/v1/nodes/:id
-```
-
-Permission: `nodes.read`
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/nodes/clnode789 \
- -H "Authorization: Bearer vf_live_..."
-```
-
-#### Toggle maintenance mode
-
-```bash
-POST /api/v1/nodes/:id/maintenance
-```
-
-Permission: `nodes.manage`
-
-```bash
-curl -s -X POST https://vectorflow.example.com/api/v1/nodes/clnode789/maintenance \
- -H "Authorization: Bearer vf_live_..." \
- -H "Content-Type: application/json" \
- -d '{"enabled": true}'
-```
-
----
-
-### Secrets
-
-#### List secrets
-
-```bash
-GET /api/v1/secrets
-```
-
-Permission: `secrets.read`
-
-Returns secret names and timestamps (never values).
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/secrets \
- -H "Authorization: Bearer vf_live_..."
-```
-
-#### Create secret
-
-```bash
-POST /api/v1/secrets
-```
-
-Permission: `secrets.manage`
-
-```bash
-curl -s -X POST https://vectorflow.example.com/api/v1/secrets \
- -H "Authorization: Bearer vf_live_..." \
- -H "Content-Type: application/json" \
- -d '{"name": "AWS_ACCESS_KEY", "value": "AKIA..."}'
-```
-
-#### Update secret
-
-```bash
-PUT /api/v1/secrets
-```
-
-Permission: `secrets.manage`
-
-Identify the secret by `id` or `name`:
-
-```bash
-curl -s -X PUT https://vectorflow.example.com/api/v1/secrets \
- -H "Authorization: Bearer vf_live_..." \
- -H "Content-Type: application/json" \
- -d '{"name": "AWS_ACCESS_KEY", "value": "AKIA_NEW..."}'
-```
-
-#### Delete secret
-
-```bash
-DELETE /api/v1/secrets?name=AWS_ACCESS_KEY
-DELETE /api/v1/secrets?id=clsecret123
-```
-
-Permission: `secrets.manage`
-
-```bash
-curl -s -X DELETE "https://vectorflow.example.com/api/v1/secrets?name=AWS_ACCESS_KEY" \
- -H "Authorization: Bearer vf_live_..."
-```
-
----
-
-### Alert Rules
-
-#### List alert rules
-
-```bash
-GET /api/v1/alerts/rules
-```
-
-Permission: `alerts.read`
-
-```bash
-curl -s https://vectorflow.example.com/api/v1/alerts/rules \
- -H "Authorization: Bearer vf_live_..."
-```
-
-#### Create alert rule
-
-```bash
-POST /api/v1/alerts/rules
-```
-
-Permission: `alerts.manage`
-
-```bash
-curl -s -X POST https://vectorflow.example.com/api/v1/alerts/rules \
- -H "Authorization: Bearer vf_live_..." \
- -H "Content-Type: application/json" \
- -d '{
- "name": "High CPU Alert",
- "metric": "cpu_usage",
- "condition": "gt",
- "threshold": 80,
- "durationSeconds": 120,
- "teamId": "clteam123"
- }'
-```
-
----
-
-### Audit Log
-
-#### Poll audit events
-
-```bash
-GET /api/v1/audit
-GET /api/v1/audit?after=cursor123&limit=100&action=deploy.agent
-```
-
-Permission: `audit.read`
-
-Supports cursor-based pagination for polling:
-
-| Parameter | Type | Default | Description |
-|-----------|------|---------|-------------|
-| `after` | string | -- | Cursor from previous response (for pagination) |
-| `limit` | number | 50 | Max events to return (1-200) |
-| `action` | string | -- | Filter by action type (e.g., `deploy.agent`) |
-
-```bash
-curl -s "https://vectorflow.example.com/api/v1/audit?limit=100" \
- -H "Authorization: Bearer vf_live_..."
-```
-
-Response:
-
-```json
-{
- "events": [ ... ],
- "cursor": "claudit789",
- "hasMore": true
-}
-```
-
-To poll for new events, pass the `cursor` from the previous response:
-
-```bash
-curl -s "https://vectorflow.example.com/api/v1/audit?after=claudit789&limit=100" \
- -H "Authorization: Bearer vf_live_..."
-```
diff --git a/docs/public/reference/database.md b/docs/public/reference/database.md
deleted file mode 100644
index 07b68d36..00000000
--- a/docs/public/reference/database.md
+++ /dev/null
@@ -1,297 +0,0 @@
-# Database Schema
-
-{% hint style="info" %}
-This reference is for advanced self-hosters who need to understand the data model for backup planning, integrations, or troubleshooting. The schema is managed by Prisma migrations -- you do not need to create tables manually. Running `npx prisma migrate deploy` (or starting the Docker container) applies all pending migrations automatically.
-{% endhint %}
-
-VectorFlow uses **PostgreSQL** as its sole data store. All state -- pipeline definitions, fleet status, metrics, audit logs, secrets, and user accounts -- lives in the database.
-
----
-
-## Entity relationship diagram
-
-```mermaid
-erDiagram
- Team ||--o{ TeamMember : has
- Team ||--o{ Environment : owns
- Team ||--o{ Template : owns
- Team ||--o{ AlertRule : owns
- Team ||--o{ VrlSnippet : owns
-
- User ||--o{ TeamMember : belongs_to
- User ||--o{ AuditLog : creates
- User ||--o{ VrlSnippet : creates
-
- Environment ||--o{ VectorNode : contains
- Environment ||--o{ Pipeline : contains
- Environment ||--o{ Secret : stores
- Environment ||--o{ Certificate : stores
- Environment ||--o{ AlertRule : has
- Environment ||--o{ AlertWebhook : has
-
- Pipeline ||--o{ PipelineNode : has
- Pipeline ||--o{ PipelineEdge : has
- Pipeline ||--o{ PipelineVersion : tracks
- Pipeline ||--o{ NodePipelineStatus : reports
- Pipeline ||--o{ PipelineMetric : records
- Pipeline ||--o{ PipelineLog : records
- Pipeline ||--o{ AlertRule : monitors
- Pipeline ||--o{ EventSampleRequest : requests
- Pipeline ||--o{ EventSample : stores
-
- VectorNode ||--o{ NodePipelineStatus : reports
- VectorNode ||--o{ NodeMetric : records
- VectorNode ||--o{ PipelineLog : emits
- VectorNode ||--o{ AlertEvent : triggers
-
- AlertRule ||--o{ AlertEvent : fires
-```
-
----
-
-## Core entities
-
-| Table | Description |
-|-------|-------------|
-| `User` | User accounts with authentication credentials, TOTP secrets, and super admin flag |
-| `Team` | Organizational unit that groups environments, templates, and members |
-| `TeamMember` | Join table linking users to teams with a role (VIEWER, EDITOR, ADMIN) |
-| `Environment` | Logical grouping of nodes and pipelines (e.g., Production, Staging) |
-| `VectorNode` | An agent node registered in an environment |
-| `Pipeline` | A pipeline definition with its visual graph, deployment state, and global config |
-| `PipelineNode` | A single component (source, transform, or sink) within a pipeline graph |
-| `PipelineEdge` | A connection between two pipeline nodes |
-| `PipelineVersion` | An immutable snapshot of a pipeline's generated YAML config at deploy time |
-| `NodePipelineStatus` | Per-node runtime status for a deployed pipeline |
-| `PipelineMetric` | Time-series pipeline throughput data (events, bytes, errors) |
-| `NodeMetric` | Time-series host system metrics (CPU, memory, disk, network) |
-| `PipelineLog` | Log lines from pipeline processes, forwarded by agents |
-| `Secret` | Encrypted secret values scoped to an environment |
-| `Certificate` | Encrypted TLS certificate files scoped to an environment |
-| `Template` | Reusable pipeline template stored as JSON nodes/edges |
-| `AuditLog` | Immutable record of every significant action |
-| `SystemSettings` | Singleton row for global server configuration |
-| `AlertRule` | Alert condition definition (metric, threshold, duration) |
-| `AlertWebhook` | Webhook destination for alert notifications |
-| `AlertEvent` | Record of a fired or resolved alert |
-| `VrlSnippet` | Custom VRL code snippet in the team library |
-| `EventSampleRequest` | Request to sample live events from a running pipeline |
-| `EventSample` | Sampled event data and inferred schema for a pipeline component |
-| `Account` | OAuth/OIDC provider accounts linked to users |
-
----
-
-## Key table details
-
-### Pipeline
-
-The central entity. Stores the pipeline definition and tracks deployment state.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `name` | `String` | Display name |
-| `description` | `String?` | Optional description |
-| `environmentId` | `String` | FK to Environment |
-| `globalConfig` | `Json?` | Global Vector config (API settings, enrichment tables, log level) |
-| `isDraft` | `Boolean` | `true` = not deployed, `false` = actively deployed |
-| `isSystem` | `Boolean` | `true` = system pipeline (audit log shipping) |
-| `deployedAt` | `DateTime?` | Timestamp of last deployment (null if never deployed) |
-| `createdById` | `String?` | FK to User who created the pipeline |
-| `updatedById` | `String?` | FK to User who last modified the pipeline |
-| `createdAt` | `DateTime` | Creation timestamp |
-| `updatedAt` | `DateTime` | Last modification timestamp |
-
-Relationships: `nodes`, `edges`, `versions`, `nodeStatuses`, `metrics`, `pipelineLogs`, `alertRules`, `sampleRequests`, `eventSamples`.
-
-### VectorNode
-
-Represents an enrolled agent node.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `name` | `String` | Display name (defaults to hostname at enrollment) |
-| `host` | `String` | Hostname or IP address |
-| `apiPort` | `Int` | Vector API port (default: 8686) |
-| `environmentId` | `String` | FK to Environment |
-| `status` | `NodeStatus` | Current health: `HEALTHY`, `DEGRADED`, `UNREACHABLE`, `UNKNOWN` |
-| `lastSeen` | `DateTime?` | Last time the server processed a heartbeat from this node |
-| `metadata` | `Json?` | Additional node metadata |
-| `nodeTokenHash` | `String?` | Hashed node authentication token (null = revoked) |
-| `enrolledAt` | `DateTime?` | When the node first enrolled |
-| `lastHeartbeat` | `DateTime?` | Timestamp of the last heartbeat |
-| `agentVersion` | `String?` | Reported agent binary version |
-| `vectorVersion` | `String?` | Reported Vector binary version |
-| `os` | `String?` | Operating system and architecture (e.g., `linux/amd64`) |
-| `deploymentMode` | `DeploymentMode` | `STANDALONE`, `DOCKER`, or `UNKNOWN` |
-| `maintenanceMode` | `Boolean` | Whether the node is in maintenance mode (pipelines are stopped) |
-| `maintenanceModeAt` | `DateTime?` | When the node entered maintenance mode (null if not in maintenance) |
-| `pendingAction` | `Json?` | Server-initiated action (e.g., self-update command) |
-| `createdAt` | `DateTime` | Registration timestamp |
-
-### Environment
-
-Logical grouping that contains nodes, pipelines, secrets, and certificates.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `name` | `String` | Display name (e.g., "Production", "Staging") |
-| `isSystem` | `Boolean` | `true` = internal system environment (hidden from UI) |
-| `teamId` | `String?` | FK to Team (null for system environment) |
-| `enrollmentTokenHash` | `String?` | Hashed enrollment token for agent registration |
-| `enrollmentTokenHint` | `String?` | First few characters of the token for display |
-| `secretBackend` | `SecretBackend` | Secret storage: `BUILTIN`, `VAULT`, `AWS_SM`, `EXEC` |
-| `secretBackendConfig` | `Json?` | Configuration for external secret backends |
-| `gitRepoUrl` | `String?` | HTTPS URL of the Git repository for pipeline audit trail |
-| `gitBranch` | `String?` | Git branch to commit pipeline YAML to (default: `main`) |
-| `gitToken` | `String?` | Encrypted access token for Git repository authentication |
-| `createdAt` | `DateTime` | Creation timestamp |
-
-### PipelineVersion
-
-Immutable deployment snapshot. Created each time a pipeline is deployed.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `pipelineId` | `String` | FK to Pipeline |
-| `version` | `Int` | Auto-incrementing version number |
-| `configYaml` | `String` | The generated Vector YAML config |
-| `configToml` | `String?` | Optional TOML representation |
-| `logLevel` | `String?` | Vector log level at deploy time |
-| `globalConfig` | `Json?` | Global config snapshot |
-| `createdById` | `String` | FK to User who deployed |
-| `changelog` | `String?` | User-provided deploy message |
-| `createdAt` | `DateTime` | Deploy timestamp |
-
-### Secret
-
-Encrypted secrets scoped to an environment. Referenced in pipeline configs using `SECRET[name]` syntax.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `name` | `String` | Secret identifier (unique per environment) |
-| `encryptedValue` | `String` | AES-256-GCM encrypted value |
-| `environmentId` | `String` | FK to Environment |
-| `createdAt` | `DateTime` | Creation timestamp |
-| `updatedAt` | `DateTime` | Last update timestamp |
-
-### Certificate
-
-Encrypted TLS certificate files. Referenced in pipeline configs using `CERT[name]` syntax.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `name` | `String` | Certificate identifier (unique per environment) |
-| `filename` | `String` | Original filename (e.g., `ca.pem`) |
-| `fileType` | `String` | Type: `ca`, `cert`, or `key` |
-| `encryptedData` | `String` | AES-256-GCM encrypted PEM content |
-| `environmentId` | `String` | FK to Environment |
-| `createdAt` | `DateTime` | Upload timestamp |
-
-### AuditLog
-
-Immutable audit trail of all significant actions.
-
-| Field | Type | Description |
-|-------|------|-------------|
-| `id` | `String` (CUID) | Primary key |
-| `userId` | `String?` | FK to User (null for system actions) |
-| `action` | `String` | Action identifier (e.g., `pipeline.created`, `deploy.agent`) |
-| `entityType` | `String` | Target entity type (e.g., `Pipeline`, `Environment`) |
-| `entityId` | `String` | ID of the affected entity |
-| `diff` | `Json?` | Before/after field changes |
-| `metadata` | `Json?` | Additional context |
-| `ipAddress` | `String?` | Client IP address |
-| `userEmail` | `String?` | Denormalized email for display |
-| `userName` | `String?` | Denormalized name for display |
-| `teamId` | `String?` | Owning team |
-| `environmentId` | `String?` | Owning environment |
-| `createdAt` | `DateTime` | Timestamp |
-
----
-
-## Enums
-
-| Enum | Values | Description |
-|------|--------|-------------|
-| `Role` | `VIEWER`, `EDITOR`, `ADMIN` | Team membership role |
-| `AuthMethod` | `LOCAL`, `OIDC` | User authentication method |
-| `NodeStatus` | `HEALTHY`, `DEGRADED`, `UNREACHABLE`, `UNKNOWN` | Agent node health |
-| `DeploymentMode` | `STANDALONE`, `DOCKER`, `UNKNOWN` | How the agent is deployed |
-| `ComponentKind` | `SOURCE`, `TRANSFORM`, `SINK` | Pipeline node category |
-| `ProcessStatus` | `RUNNING`, `STARTING`, `STOPPED`, `CRASHED`, `PENDING` | Pipeline process state |
-| `LogLevel` | `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR` | Log severity |
-| `SecretBackend` | `BUILTIN`, `VAULT`, `AWS_SM`, `EXEC` | Secret storage provider |
-| `AlertMetric` | `node_unreachable`, `cpu_usage`, `memory_usage`, `disk_usage`, `error_rate`, `discarded_rate`, `pipeline_crashed` | Metric to evaluate |
-| `AlertCondition` | `gt`, `lt`, `eq` | Comparison operator |
-| `AlertStatus` | `firing`, `resolved` | Alert event state |
-
----
-
-## Encryption at rest
-
-Sensitive fields are encrypted using AES-256-GCM before being stored in the database:
-
-- **Secret values** (`Secret.encryptedValue`) -- pipeline credentials, API keys, passwords
-- **Certificate data** (`Certificate.encryptedData`) -- TLS certificates and private keys
-- **TOTP secrets** (`User.totpSecret`) -- two-factor authentication secrets
-- **TOTP backup codes** (`User.totpBackupCodes`) -- recovery codes
-- **Password hashes** (`User.passwordHash`) -- bcrypt-hashed, not AES-encrypted
-
-The encryption key is derived from the `NEXTAUTH_SECRET` environment variable. Losing this value means encrypted data cannot be recovered.
-
----
-
-## Indexes
-
-Key database indexes for query performance:
-
-| Table | Index | Purpose |
-|-------|-------|---------|
-| `PipelineMetric` | `(pipelineId, timestamp)` | Time-range queries for pipeline charts |
-| `PipelineMetric` | `(timestamp)` | Retention cleanup |
-| `NodeMetric` | `(nodeId, timestamp)` | Time-range queries for node charts |
-| `PipelineLog` | `(pipelineId, timestamp)` | Pipeline log pagination |
-| `PipelineLog` | `(nodeId, timestamp)` | Node log pagination |
-| `AuditLog` | `(entityType, entityId)` | Entity-specific audit queries |
-| `AuditLog` | `(userId)` | User activity queries |
-| `AuditLog` | `(createdAt)` | Time-range audit queries |
-| `AlertRule` | `(environmentId)` | Environment-scoped alert listing |
-| `AlertEvent` | `(alertRuleId)` | Alert event history |
-| `AlertEvent` | `(firedAt)` | Time-range alert queries |
-
----
-
-## Data retention
-
-VectorFlow automatically prunes time-series data based on system settings:
-
-| Data Type | Default Retention | Setting |
-|-----------|------------------|---------|
-| Pipeline metrics | 7 days | `metricsRetentionDays` |
-| Pipeline logs | 3 days | `logsRetentionDays` |
-| Node metrics | 7 days | `metricsRetentionDays` |
-| Audit logs | Indefinite | Not automatically pruned |
-| Alert events | Indefinite | Not automatically pruned |
-
-These values are configured in the `SystemSettings` table via the admin UI.
-
----
-
-## Backup considerations
-
-{% hint style="warning" %}
-The database is the single source of truth for all VectorFlow state. Losing the database without a backup means losing all pipeline definitions, deployment history, secrets, and audit logs.
-{% endhint %}
-
-For backup and restore procedures, see [Backup & Restore](../operations/backup-restore.md).
-
-Key points:
-- Use `pg_dump` for logical backups or continuous archiving for point-in-time recovery
-- The `NEXTAUTH_SECRET` environment variable must match between backup and restore -- it is the encryption key for secrets and certificates
-- VectorFlow has built-in scheduled backup support (configured via `SystemSettings`)
diff --git a/docs/public/reference/pipeline-yaml.md b/docs/public/reference/pipeline-yaml.md
deleted file mode 100644
index 671611cf..00000000
--- a/docs/public/reference/pipeline-yaml.md
+++ /dev/null
@@ -1,282 +0,0 @@
-# Pipeline YAML
-
-{% hint style="info" %}
-Users typically do not write pipeline YAML directly -- the visual editor generates it. This reference is for understanding the generated output, debugging deployment issues, and advanced use cases like importing existing Vector configs.
-{% endhint %}
-
-VectorFlow pipelines are ultimately Vector configuration files. The visual pipeline editor translates your node graph into standard [Vector YAML configuration](https://vector.dev/docs/reference/configuration/) that Vector can execute directly.
-
----
-
-## How YAML is generated
-
-The pipeline YAML generation follows this flow:
-
-```
- Visual Editor Server Agent
- ┌───────────┐ ┌──────────────┐ ┌──────────────┐
- │ Drag nodes│ │ Save graph │ │ Receive YAML │
- │ Connect │───▶│ to database │ │ via config │
- │ edges │ │ │ │ endpoint │
- └───────────┘ │ On deploy: │ │ │
- │ 1. Generate │ poll │ Write to disk│
- │ YAML │────────▶ │ Start Vector │
- │ 2. Validate │ │ process │
- │ 3. Version │ └──────────────┘
- └──────────────┘
-```
-
-1. **Graph saved**: The visual editor saves pipeline nodes (components) and edges (connections) to the database
-2. **YAML generated**: At deploy time, the server converts the graph into Vector YAML
-3. **Validation**: The generated YAML is validated using `vector validate --no-environment`
-4. **Versioning**: A new `PipelineVersion` record stores the YAML and a version number
-5. **Distribution**: Agents poll the server and receive the YAML config for all deployed pipelines
-6. **Execution**: The agent writes the YAML to disk and starts a Vector process with it
-
----
-
-## YAML structure
-
-The generated YAML follows Vector's standard configuration format with three top-level sections:
-
-```yaml
-sources:
- :
- type:
- # ... source-specific fields
-
-transforms:
- :
- type:
- inputs:
- -
- # ... transform-specific fields
-
-sinks:
- :
- type:
- inputs:
- -
- # ... sink-specific fields
-```
-
-### Component keys
-
-Each node in the visual editor has a **component key** -- a unique, auto-generated identifier within the pipeline (e.g., `http_server_k7xMp2nQ`). Component keys are generated when a node is added and never change, even if you rename the component in the editor.
-
-Component keys must:
-- Start with a letter or underscore
-- Contain only letters, numbers, and underscores
-- Be between 1 and 128 characters
-
-These keys become the YAML block names under `sources`, `transforms`, or `sinks`. The human-readable **Name** field in the editor is separate from the component key and does not affect the generated YAML.
-
-### Connections via `inputs`
-
-Vector uses an `inputs` field to define data flow. When you draw an edge from node A to node B in the visual editor, the generated YAML adds A's component key to B's `inputs` array.
-
-Sources never have `inputs` -- they are the entry points. Transforms and sinks always have at least one input.
-
----
-
-## Complete example
-
-A pipeline that receives syslog, parses and enriches the events with VRL, then sends to S3:
-
-**Visual editor graph:**
-```
-[syslog_in] ──▶ [parse_logs] ──▶ [s3_output]
- (source) (transform) (sink)
-```
-
-**Generated YAML:**
-```yaml
-sources:
- syslog_in:
- type: syslog
- address: 0.0.0.0:514
- mode: udp
-
-transforms:
- parse_logs:
- type: remap
- inputs:
- - syslog_in
- source: |
- .environment = "production"
- .processed_at = now()
- del(.source_type)
-
-sinks:
- s3_output:
- type: aws_s3
- inputs:
- - parse_logs
- bucket: my-log-bucket
- region: us-east-1
- key_prefix: "logs/{{ .environment }}/%Y/%m/%d/"
- encoding:
- codec: json
- auth:
- access_key_id: "${VF_SECRET_AWS_ACCESS_KEY}"
- secret_access_key: "${VF_SECRET_AWS_SECRET_KEY}"
-```
-
----
-
-## Global configuration
-
-Pipelines can include global Vector configuration sections beyond sources, transforms, and sinks. These are set via the pipeline's `globalConfig` field and appear at the top level of the generated YAML.
-
-Common global config sections:
-
-- **`api`**: Enable the Vector API and GraphQL playground
-- **`enrichment_tables`**: Define lookup tables for enrichment
-
-The `log_level` key in `globalConfig` is handled specially -- it is **not** included in the generated YAML. Instead, it is passed to the Vector process as the `VECTOR_LOG` environment variable.
-
----
-
-## Disabled nodes
-
-Nodes marked as **disabled** in the visual editor are excluded from the generated YAML entirely. Their edges are also removed. This lets you temporarily disable a component without deleting it from the graph.
-
----
-
-## Secret references
-
-Secrets stored in VectorFlow can be referenced in pipeline component configurations. When you use a secret in a node's config, the reference is resolved at deploy time.
-
-### How it works
-
-1. Create a secret in the environment's secret store (e.g., name: `AWS_ACCESS_KEY`)
-2. Reference it in a component config field using the `SECRET[name]` syntax in the visual editor
-3. At deploy time, the server resolves all secret references
-4. The agent receives the resolved values in the config response and injects them as environment variables
-5. In the generated YAML, secrets appear as `${VF_SECRET_AWS_ACCESS_KEY}` -- standard Vector environment variable interpolation (all secrets use the `VF_SECRET_` prefix)
-
-### Example
-
-A sink configured with secret references:
-
-```yaml
-sinks:
- elasticsearch:
- type: elasticsearch
- inputs:
- - transform_logs
- endpoints:
- - "https://es.example.com:9200"
- auth:
- strategy: basic
- user: "${VF_SECRET_ES_USER}"
- password: "${VF_SECRET_ES_PASSWORD}"
-```
-
-The agent injects environment variables `VF_SECRET_ES_USER` and `VF_SECRET_ES_PASSWORD` with the decrypted values when starting the Vector process.
-
----
-
-## Certificate references
-
-TLS certificates uploaded to VectorFlow are referenced using `CERT[name]` syntax. At deploy time:
-
-1. Certificate data is sent to the agent in the config response (base64-encoded)
-2. The agent writes cert files to `/certs/`
-3. The config YAML references the local file path
-
-### Example
-
-```yaml
-sinks:
- kafka_out:
- type: kafka
- inputs:
- - parse_logs
- bootstrap_servers: "kafka.example.com:9093"
- topic: logs
- tls:
- ca_file: "/var/lib/vf-agent/certs/ca.pem"
- crt_file: "/var/lib/vf-agent/certs/client.crt"
- key_file: "/var/lib/vf-agent/certs/client.key"
-```
-
----
-
-## Validation
-
-Before any deployment, VectorFlow validates the generated YAML using the Vector binary:
-
-```bash
-vector validate --no-environment
-```
-
-The `--no-environment` flag skips environment variable validation (since secrets are resolved at runtime by the agent, not at validation time).
-
-### Validation results
-
-The validation returns:
-- **Valid**: The config is syntactically correct and all component types are recognized
-- **Errors**: Specific error messages, often with the affected component key identified
-- **Warnings**: Deprecation notices or non-fatal issues
-
-If validation fails, the deployment is blocked and errors are displayed in the UI.
-
----
-
-## Metrics sidecar
-
-When the agent starts a pipeline, it automatically appends a **metrics sidecar config** as a second `--config` argument. This sidecar adds instrumentation without modifying the user's pipeline YAML:
-
-```yaml
-# Auto-generated by the VectorFlow agent
-api:
- enabled: true
- address: "127.0.0.1:"
-
-sources:
- vf_internal_metrics:
- type: internal_metrics
- vf_host_metrics:
- type: host_metrics
-
-sinks:
- vf_metrics_exporter:
- type: prometheus_exporter
- inputs:
- - vf_internal_metrics
- - vf_host_metrics
- address: "127.0.0.1:"
-```
-
-Vector merges both config files, so the pipeline's sources/transforms/sinks coexist with the metrics instrumentation. The `vf_` prefix prevents key collisions.
-
----
-
-## Version history
-
-Every deployment creates an immutable `PipelineVersion` record containing:
-
-| Field | Description |
-|-------|-------------|
-| `version` | Auto-incrementing integer (1, 2, 3, ...) |
-| `configYaml` | The exact YAML that was deployed |
-| `logLevel` | The Vector log level at deploy time |
-| `changelog` | User-provided deploy message |
-| `createdById` | Who triggered the deployment |
-| `createdAt` | Deployment timestamp |
-
-You can view previous versions in the pipeline detail page and roll back to any prior version. Rolling back creates a **new** version with the old config, preserving the full history.
-
----
-
-## Importing existing configs
-
-VectorFlow supports importing existing Vector YAML or TOML configurations into the visual editor. The importer:
-
-1. Parses the config file
-2. Creates nodes for each source, transform, and sink
-3. Creates edges based on `inputs` fields
-4. Auto-positions nodes in a left-to-right layout
-
-This is useful for migrating existing Vector deployments to VectorFlow's managed model.
diff --git a/docs/public/screenshots/dashboard.png b/docs/public/screenshots/dashboard.png
deleted file mode 100644
index 719f09fb..00000000
Binary files a/docs/public/screenshots/dashboard.png and /dev/null differ
diff --git a/docs/public/screenshots/environments.png b/docs/public/screenshots/environments.png
deleted file mode 100644
index a0a89a42..00000000
Binary files a/docs/public/screenshots/environments.png and /dev/null differ
diff --git a/docs/public/screenshots/fleet.png b/docs/public/screenshots/fleet.png
deleted file mode 100644
index 4640fced..00000000
Binary files a/docs/public/screenshots/fleet.png and /dev/null differ
diff --git a/docs/public/screenshots/login.png b/docs/public/screenshots/login.png
deleted file mode 100644
index a4e6e87d..00000000
Binary files a/docs/public/screenshots/login.png and /dev/null differ
diff --git a/docs/public/screenshots/node-details.png b/docs/public/screenshots/node-details.png
deleted file mode 100644
index b65297ad..00000000
Binary files a/docs/public/screenshots/node-details.png and /dev/null differ
diff --git a/docs/public/screenshots/pipeline-editor.png b/docs/public/screenshots/pipeline-editor.png
deleted file mode 100644
index 59b6177f..00000000
Binary files a/docs/public/screenshots/pipeline-editor.png and /dev/null differ
diff --git a/docs/public/screenshots/pipelines.png b/docs/public/screenshots/pipelines.png
deleted file mode 100644
index aa73689c..00000000
Binary files a/docs/public/screenshots/pipelines.png and /dev/null differ
diff --git a/docs/public/user-guide/ai-suggestions.md b/docs/public/user-guide/ai-suggestions.md
deleted file mode 100644
index 5c95ac22..00000000
--- a/docs/public/user-guide/ai-suggestions.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# AI Suggestions
-
-VectorFlow includes optional AI-powered assistance for writing VRL code and generating pipeline configurations. When enabled, team members with Editor or Admin roles can use AI features in both the VRL editor and pipeline builder.
-
-## Setup
-
-Team admins can configure AI in **Settings → AI**. The configuration requires:
-
-| Field | Description |
-|-------|-------------|
-| **Provider** | OpenAI, Anthropic, or Custom (any OpenAI-compatible endpoint) |
-| **Base URL** | API endpoint — pre-filled for known providers |
-| **API Key** | Provider API key — encrypted at rest using AES-256 |
-| **Model** | Model identifier (e.g. `gpt-4o`, `claude-sonnet-4-20250514`) |
-
-After saving, use **Test Connection** to verify the configuration works.
-
-{% hint style="info" %}
-VectorFlow uses the OpenAI-compatible chat completions API format (`/chat/completions`). Most providers support this format natively or via a compatibility layer. For Anthropic, use an OpenAI-compatible proxy such as LiteLLM or OpenRouter.
-{% endhint %}
-
-## VRL Assistant
-
-In the VRL editor (opened from any remap, filter, or route transform), click the **AI** button in the tools panel to reveal the AI input.
-
-1. Type a natural language description of what you want the VRL code to do
-2. Click **Generate** — the AI streams VRL code in real time
-3. When complete, choose:
- - **Insert** — append the generated code after your existing code
- - **Replace** — replace all existing code with the generated result
- - **Regenerate** — try again with the same prompt
-
-The AI is aware of your upstream source types and available fields, so you can reference them naturally (e.g., "parse the syslog message and extract the hostname").
-
-## Pipeline Builder
-
-In the pipeline editor toolbar, click the **sparkle icon** to open the AI Pipeline Builder dialog.
-
-### Generate mode
-
-Describe a pipeline in plain language:
-
-> "Collect Kubernetes logs from a file source, drop debug-level events, parse JSON, and send to Elasticsearch and S3"
-
-The AI generates a complete Vector YAML configuration. Click **Apply to Canvas** to add the generated components to your pipeline. If your canvas already has components, the new ones are positioned below the existing layout to avoid overlap.
-
-### Review mode
-
-Ask the AI to analyze your current pipeline configuration:
-
-> "Are there any performance issues with my pipeline?"
-
-The AI returns structured, actionable suggestion cards that you can selectively apply to your canvas.
-
-#### Suggestion cards
-
-Each suggestion appears as an interactive card showing:
-
-- **Title** and **description** explaining why the change helps
-- **Priority badge** (High, Medium, Low)
-- **Type badge** — Config Change, Add Component, Remove Component, or Rewire
-- **Checkbox** for batch selection
-- **Config preview** for configuration changes showing the exact fields that will be modified
-
-#### Applying suggestions
-
-- **Apply All** — applies every actionable suggestion from that AI response
-- **Apply Selected** — applies only the suggestions you have checked
-
-Applied suggestions are marked with a green "Applied" badge and cannot be re-applied. The entire batch is a single undo operation — press **Ctrl+Z** (or **Cmd+Z**) to revert all changes at once.
-
-#### Conflict detection
-
-When you select multiple suggestions that conflict (e.g., two suggestions modifying the same config field, or one removing a component that another references), an amber warning appears on the affected cards explaining the conflict. You can still apply conflicting suggestions, but review the warnings first.
-
-#### Suggestion statuses
-
-| Status | Meaning |
-|--------|---------|
-| **Actionable** | Ready to apply |
-| **Applied** | Already applied to the canvas |
-| **Outdated** | The pipeline changed since this suggestion was made |
-| **Invalid** | References a component that no longer exists on the canvas |
-
-#### Conversations
-
-Review conversations are persistent — they are saved per pipeline and visible to all team members with access. You can:
-
-- **Ask follow-up questions** using the input at the bottom of the dialog
-- **Start a new conversation** by clicking "New Conversation" below the input
-
-## Rate Limits
-
-AI requests are rate-limited to 60 requests per hour per team to prevent excessive API usage. The limit resets on a rolling window.
-
-## Security
-
-- API keys are encrypted at rest using AES-256-GCM
-- Keys are never exposed to the client — the settings page shows only whether a key is saved
-- AI configuration changes are recorded in the audit log with the API key redacted
-- Only team members with Editor or Admin roles can use AI features
diff --git a/docs/public/user-guide/alerts.md b/docs/public/user-guide/alerts.md
deleted file mode 100644
index 463541d5..00000000
--- a/docs/public/user-guide/alerts.md
+++ /dev/null
@@ -1,300 +0,0 @@
-# Alerts
-
-The **Alerts** page lets you configure rules that monitor your pipelines and nodes, receive notifications when something needs attention, and review a history of past alert events. Alerts are scoped to the currently selected environment.
-
-## Overview
-
-The Alerts page is organized into four sections:
-
-- **Alert Rules** -- Define the conditions that trigger alerts.
-- **Notification Channels** -- Configure where alert notifications are delivered (Slack, Email, PagerDuty, or Webhook).
-- **Legacy Webhooks** -- Existing HTTP webhook endpoints (shown only if legacy webhooks exist).
-- **Alert History** -- Browse a chronological log of all alert events.
-
-## Alert rules
-
-An alert rule defines a metric to watch, a condition to evaluate, and how long the condition must persist before the alert fires.
-
-### Creating an alert rule
-
-{% stepper %}
-{% step %}
-### Open the Alerts page
-Select an environment from the header, then navigate to **Alerts** in the sidebar.
-{% endstep %}
-{% step %}
-### Click Add Rule
-Click the **Add Rule** button in the Alert Rules section.
-{% endstep %}
-{% step %}
-### Configure the rule
-Fill in the rule form:
-
-- **Name** -- A descriptive label (e.g., "High CPU on prod nodes").
-- **Pipeline** (optional) -- Scope the rule to a specific pipeline, or leave as "All pipelines" for environment-wide monitoring.
-- **Metric** -- The metric to evaluate (see supported metrics below).
-- **Threshold** -- The numeric value that triggers the alert (not required for binary metrics).
-- **Duration** -- How many seconds the condition must persist before firing. Defaults to 60 seconds.
-- **Notification Channels** (optional) -- Select specific channels for this rule. If none are selected, all enabled channels in the environment receive notifications.
-{% endstep %}
-{% step %}
-### Save
-Click **Create Rule**. The rule is enabled by default and begins evaluating on the next agent heartbeat.
-{% endstep %}
-{% endstepper %}
-
-### Supported metrics
-
-VectorFlow supports three categories of alert metrics: **Infrastructure** metrics that monitor resource utilization with thresholds, **Binary** metrics that fire on detected conditions, and **Event** metrics that fire when specific system events occur.
-
-#### Infrastructure metrics
-
-| Metric | Type | Description |
-|--------|------|-------------|
-| **CPU Usage** | Percentage | CPU utilization derived from cumulative CPU seconds. |
-| **Memory Usage** | Percentage | Memory used as a percentage of total memory. |
-| **Disk Usage** | Percentage | Filesystem used as a percentage of total disk space. |
-| **Error Rate** | Percentage | Errors as a percentage of total events ingested. |
-| **Discarded Rate** | Percentage | Discarded events as a percentage of total events ingested. |
-| **Node Unreachable** | Binary | Fires when a node stops sending heartbeats. |
-| **Pipeline Crashed** | Binary | Fires when a pipeline enters the crashed state. |
-
-Percentage-based metrics use the conditions **>** (greater than), **<** (less than), or **=** (equals) against a threshold value. Binary metrics (Node Unreachable, Pipeline Crashed) fire automatically when the condition is detected -- no threshold is needed.
-
-#### Event metrics
-
-Event metrics fire whenever a specific system event occurs. Unlike infrastructure metrics, they have **no threshold** -- the alert triggers on each occurrence. Event rules are created the same way as infrastructure rules, but you select a metric from the **Events** category in the metric dropdown.
-
-| Metric | Description |
-|--------|-------------|
-| **Deploy Requested** | A deploy request was submitted for approval. |
-| **Deploy Completed** | A pipeline was successfully deployed to agents. |
-| **Deploy Rejected** | A deploy request was rejected by a reviewer. |
-| **Deploy Cancelled** | A deploy request was cancelled. |
-| **New Version Available** | A new VectorFlow server version is available. |
-| **SCIM Sync Failed** | A SCIM provisioning operation failed. |
-| **Backup Failed** | A scheduled database backup failed. |
-| **Certificate Expiring** | A TLS certificate is approaching its expiration date. |
-| **Node Joined** | A new agent node enrolled in the environment. |
-| **Node Left** | An agent node was removed or disconnected from the environment. |
-
-{% hint style="info" %}
-Event alerts use the same notification channels as infrastructure alerts (Slack, Email, PagerDuty, Webhook). You can route event alerts to specific channels by linking channels to the rule, just like any other alert rule.
-{% endhint %}
-
-### Condition evaluation
-
-Alert rules are evaluated during each agent heartbeat cycle. The evaluation logic works as follows:
-
-1. The metric value is read from the latest node data.
-2. If the value meets the condition (e.g., CPU > 80), a timer starts.
-3. If the condition persists for the configured **duration** (in seconds), the alert fires and an event is created.
-4. If the condition clears before the duration elapses, the timer resets.
-5. When a firing alert's condition clears, the alert automatically resolves.
-
-{% hint style="info" %}
-The duration setting prevents transient spikes from triggering alerts. A 60-second duration means the condition must hold for a full minute before an alert fires.
-{% endhint %}
-
-### Managing rules
-
-- **Enable / Disable** -- Toggle the switch in the rules table to enable or disable a rule without deleting it.
-- **Edit** -- Click the pencil icon to update the rule name, threshold, duration, or linked notification channels.
-- **Delete** -- Click the trash icon to permanently remove the rule and stop future evaluations.
-
-## Notification channels
-
-Notification channels define where alert notifications are delivered. VectorFlow supports four channel types: **Slack**, **Email**, **PagerDuty**, and **Webhook**. When an alert fires or resolves, notifications are sent to all enabled channels -- or to specific channels linked to the alert rule.
-
-### Adding a channel
-
-{% stepper %}
-{% step %}
-### Click Add Channel
-In the Notification Channels section, click **Add Channel**.
-{% endstep %}
-{% step %}
-### Choose a type
-Select the channel type from the dropdown: Slack, Email, PagerDuty, or Webhook. Each type has its own configuration form.
-{% endstep %}
-{% step %}
-### Configure the channel
-Fill in the type-specific settings (see setup examples below).
-{% endstep %}
-{% step %}
-### Test the channel
-After creating the channel, click the **send** icon in the channels table to deliver a test payload. VectorFlow reports whether delivery succeeded.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="warning" %}
-Always test your channel after creating it. Misconfigured credentials or URLs will silently drop alert notifications.
-{% endhint %}
-
-### Channel types
-
-{% tabs %}
-{% tab title="Slack" %}
-#### Slack setup
-
-Deliver alerts to a Slack channel using an Incoming Webhook.
-
-**Configuration:**
-
-- **Webhook URL** -- The Slack Incoming Webhook URL (e.g., `https://hooks.slack.com/services/T00.../B00.../xxx`).
-
-**How to get a webhook URL:**
-
-1. Go to [Slack API: Incoming Webhooks](https://api.slack.com/messaging/webhooks).
-2. Create a new app or select an existing one.
-3. Enable Incoming Webhooks and add a new webhook to a channel.
-4. Copy the webhook URL and paste it into VectorFlow.
-
-Alerts are delivered using Slack Block Kit formatting with color-coded status indicators, metric details, and a link to the VectorFlow dashboard.
-{% endtab %}
-
-{% tab title="Email" %}
-#### Email setup
-
-Deliver alerts via SMTP email.
-
-**Configuration:**
-
-- **SMTP Host** -- Your mail server hostname (e.g., `smtp.gmail.com`).
-- **SMTP Port** -- The SMTP port (typically `587` for STARTTLS or `465` for SSL).
-- **SMTP User** (optional) -- Username for SMTP authentication.
-- **SMTP Password** (optional) -- Password for SMTP authentication.
-- **From Address** -- The sender email address.
-- **Recipients** -- Comma-separated list of recipient email addresses.
-
-**Example with Gmail:**
-
-| Setting | Value |
-|---------|-------|
-| SMTP Host | `smtp.gmail.com` |
-| SMTP Port | `587` |
-| SMTP User | `alerts@yourcompany.com` |
-| SMTP Password | App-specific password |
-| From | `alerts@yourcompany.com` |
-| Recipients | `ops@yourcompany.com, oncall@yourcompany.com` |
-
-{% hint style="info" %}
-If your SMTP server does not require authentication, leave the SMTP User and Password fields empty.
-{% endhint %}
-{% endtab %}
-
-{% tab title="PagerDuty" %}
-#### PagerDuty setup
-
-Trigger and resolve PagerDuty incidents using the Events API v2.
-
-**Configuration:**
-
-- **Integration Key** -- The routing key from your PagerDuty service integration.
-
-**How to get an integration key:**
-
-1. In PagerDuty, go to **Services** and select (or create) a service.
-2. Under **Integrations**, add a new integration of type **Events API v2**.
-3. Copy the **Integration Key** and paste it into VectorFlow.
-
-**Behavior:**
-
-- **Firing** alerts create a `trigger` event in PagerDuty.
-- **Resolved** alerts send a `resolve` event, automatically closing the PagerDuty incident.
-- VectorFlow uses the alert event ID as the PagerDuty `dedup_key`, so repeated firings of the same alert update the same incident.
-{% endtab %}
-
-{% tab title="Webhook" %}
-#### Webhook setup
-
-Deliver alerts to any HTTP endpoint via a JSON POST request. This is the most flexible option, suitable for custom integrations, chat platforms, or automation tools.
-
-**Configuration:**
-
-- **URL** -- The HTTPS endpoint that will receive alert payloads.
-- **Headers** (optional) -- A JSON object of custom headers (e.g., `{"Authorization": "Bearer token"}`).
-- **HMAC Secret** (optional) -- If set, each request includes an `X-VectorFlow-Signature` header with a SHA-256 HMAC of the body.
-
-The webhook payload is a JSON object containing all alert details including `alertId`, `status`, `ruleName`, `metric`, `value`, `threshold`, `message`, `timestamp`, and a `dashboardUrl` link.
-{% endtab %}
-{% endtabs %}
-
-### Channel routing
-
-By default, all enabled notification channels in an environment receive every alert. To send specific alerts to specific channels:
-
-1. Edit an alert rule (or create a new one).
-2. In the **Notification Channels** section of the rule form, click the channel badges to select which channels should receive notifications for that rule.
-3. Save the rule.
-
-If no channels are explicitly selected for a rule, all enabled channels are used as a fallback.
-
-### Managing channels
-
-- **Enable / Disable** -- Toggle the switch to pause or resume deliveries without deleting the channel.
-- **Edit** -- Click the pencil icon to update the channel name or configuration.
-- **Test** -- Click the send icon to deliver a test payload.
-- **Delete** -- Click the trash icon to permanently remove the channel.
-
-## Legacy webhooks
-
-If you previously configured webhooks before notification channels were introduced, they continue to work. The **Legacy Webhooks** section appears only when legacy webhooks exist.
-
-{% hint style="info" %}
-Consider migrating legacy webhooks to Notification Channels for a unified configuration experience. Create a new **Webhook** type notification channel with the same URL, headers, and HMAC secret, then delete the legacy webhook.
-{% endhint %}
-
-### Webhook payload
-
-Each webhook delivery (both legacy and Webhook-type notification channels) sends a JSON POST body with the following fields:
-
-```json
-{
- "alertId": "evt_abc123",
- "status": "firing",
- "ruleName": "High CPU Usage",
- "severity": "warning",
- "environment": "Production",
- "team": "Platform",
- "node": "node-01.example.com",
- "metric": "cpu_usage",
- "value": 85.5,
- "threshold": 80,
- "message": "CPU usage is 85.50 (threshold: > 80)",
- "timestamp": "2026-03-06T12:00:00.000Z",
- "dashboardUrl": "https://vectorflow.example.com/alerts",
- "content": "**Alert FIRING: High CPU Usage**\n> CPU usage is 85.50 ..."
-}
-```
-
-The `content` field contains a pre-formatted, human-readable summary suitable for chat platforms like Slack or Discord. Generic consumers can ignore it and use the structured fields instead.
-
-### Webhook security
-
-- **HMAC signing** -- When an HMAC secret is configured, VectorFlow computes `sha256=` over the raw JSON body and includes it in the `X-VectorFlow-Signature` header. Verify this on your server to ensure payload authenticity.
-- **SSRF protection** -- VectorFlow validates that webhook and Slack URLs resolve to public IP addresses. Private and reserved IP ranges are blocked.
-- **Timeout** -- All notification channel deliveries time out after 10 seconds.
-
-## Alert history
-
-The **Alert History** section shows a chronological list of all alert events in the current environment. Each row displays:
-
-| Column | Description |
-|--------|-------------|
-| **Timestamp** | When the alert fired. |
-| **Rule Name** | The alert rule that triggered. |
-| **Node** | The node where the condition was detected. |
-| **Pipeline** | The pipeline associated with the rule (or "-" for environment-wide rules). |
-| **Status** | **Firing** (red) or **Resolved** (green). |
-| **Value** | The metric value at the time the alert was evaluated. |
-| **Message** | A human-readable summary of the condition. |
-
-Click **Load more** at the bottom of the table to fetch older events. Events are ordered newest-first.
-
-## Alert states
-
-An alert event transitions through two states:
-
-- **Firing** -- The rule's condition has been met for the required duration. The alert is active and notifications have been sent to all configured channels.
-- **Resolved** -- The condition is no longer met. The alert closes automatically and a resolution notification is sent to all configured channels.
diff --git a/docs/public/user-guide/anomaly-detection.md b/docs/public/user-guide/anomaly-detection.md
deleted file mode 100644
index 36e5eabd..00000000
--- a/docs/public/user-guide/anomaly-detection.md
+++ /dev/null
@@ -1,106 +0,0 @@
-# Anomaly Detection
-
-VectorFlow continuously monitors your pipeline metrics and automatically detects statistical anomalies -- unusual spikes or drops that may indicate problems with your data pipelines.
-
-## What anomalies are detected
-
-The anomaly detector monitors three core metrics for each deployed pipeline:
-
-| Metric | Spike anomaly | Drop anomaly |
-|--------|---------------|--------------|
-| **Events In** (throughput) | Throughput spike | Throughput drop |
-| **Errors Total** | Error rate spike | -- (drops are expected) |
-| **Latency Mean (ms)** | Latency spike | -- (drops are expected) |
-
-Error and latency drops are not flagged because decreasing errors or latency is a positive signal, not an anomaly.
-
-## Sigma-based detection methodology
-
-Anomaly detection uses a statistical sigma (standard deviation) approach:
-
-1. **Baseline computation** -- VectorFlow computes the mean and standard deviation of each metric over a rolling historical window (default: 7 days).
-2. **Current comparison** -- The most recent metric value is compared against the baseline.
-3. **Deviation factor** -- The number of standard deviations the current value is from the mean is calculated: `deviation = |current - mean| / stddev`.
-4. **Threshold check** -- If the deviation factor exceeds the configured sigma threshold, an anomaly is raised.
-
-A minimum standard deviation floor (default: 5% of the mean) prevents false positives on metrics that are nearly constant. For example, if throughput has been steady at 1000 events/interval with near-zero variance, a fluctuation of 50 events would not be flagged because it falls within the 5% floor.
-
-{% hint style="info" %}
-At least 24 data points are required to compute a reliable baseline. New pipelines will not generate anomalies until enough historical data has been collected.
-{% endhint %}
-
-## Sensitivity presets
-
-The sigma threshold controls how sensitive the detector is. Lower values catch more anomalies but may produce more false positives:
-
-| Preset | Sigma threshold | Description |
-|--------|----------------|-------------|
-| **Sensitive** | 2.0 sigma | Catches subtle changes. Higher false positive rate. |
-| **Moderate** | 2.5 sigma | Balanced sensitivity for most environments. |
-| **Balanced** | 3.0 sigma (default) | Standard statistical significance. Good for stable pipelines. |
-| **Relaxed** | 4.0 sigma | Only flags extreme outliers. Minimal false positives. |
-
-The sigma threshold and other parameters can be configured in **Settings > System** by a super admin. Changes are picked up within 60 seconds.
-
-### Additional configuration options
-
-| Setting | Default | Description |
-|---------|---------|-------------|
-| Baseline window | 7 days | How much historical data is used for computing the baseline. |
-| Sigma threshold | 3.0 | Number of standard deviations to trigger an anomaly. |
-| Min stddev floor | 5% | Minimum standard deviation as a percentage of the mean. |
-| Dedup window | 4 hours | Cooldown before creating a duplicate anomaly for the same pipeline and type. |
-| Enabled metrics | `eventsIn`, `errorsTotal`, `latencyMeanMs` | Which metrics to monitor. |
-
-## Severity levels
-
-Each detected anomaly is assigned a severity based on how far the metric has deviated:
-
-| Severity | Condition |
-|----------|-----------|
-| **Warning** | Deviation is between the sigma threshold and threshold + 1 |
-| **Critical** | Deviation exceeds sigma threshold + 1 |
-
-For example, with a 3-sigma threshold, a deviation of 3.5 sigma is a **warning** and a deviation of 4.2 sigma is **critical**.
-
-## Viewing anomalies
-
-Anomalies appear in the **Anomalies** section of the environment dashboard. The list shows:
-
-- **Pipeline name** -- Which pipeline the anomaly was detected on
-- **Type** -- The anomaly type (throughput drop, throughput spike, error rate spike, latency spike)
-- **Severity** -- Warning or critical
-- **Message** -- A human-readable description including the current value, baseline mean, standard deviation, and sigma factor
-- **Detected at** -- When the anomaly was first detected
-
-Open anomaly counts are also shown as badges on pipeline cards throughout the UI.
-
-## Acknowledging and dismissing anomalies
-
-Anomalies have three statuses:
-
-- **Open** -- Newly detected, awaiting review
-- **Acknowledged** -- A team member has reviewed the anomaly and is investigating
-- **Dismissed** -- The anomaly has been resolved or determined to be a false positive
-
-From the anomaly list:
-
-- Click **Acknowledge** to mark an anomaly as under investigation
-- Click **Dismiss** to close the anomaly
-
-{% hint style="info" %}
-Acknowledging or dismissing anomalies requires the **Editor** role or above.
-{% endhint %}
-
-## Deduplication
-
-To avoid alert fatigue, the detector will not create a new anomaly if an open or acknowledged anomaly already exists for the same pipeline and anomaly type within the deduplication window (default: 4 hours). This means you will see at most one active anomaly per pipeline per type at any given time.
-
-## Detection schedule
-
-The anomaly detector runs as a background job on the leader server instance every 5 minutes. It evaluates all deployed (non-draft) pipelines using two optimized SQL queries:
-
-1. A batch query to fetch the latest metric values for all pipelines
-2. Per-pipeline baseline queries (cached for 15 minutes) to compute mean and standard deviation
-
-This design ensures detection scales efficiently even with hundreds of pipelines.
diff --git a/docs/public/user-guide/cost-optimization.md b/docs/public/user-guide/cost-optimization.md
deleted file mode 100644
index f21e39db..00000000
--- a/docs/public/user-guide/cost-optimization.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Cost Optimization
-
-VectorFlow includes a cost analytics dashboard that helps you understand, attribute, and optimize the cost of your observability pipelines. Cost is modeled as a function of data volume (bytes processed) and a configurable per-GB rate.
-
-## Cost dashboard overview
-
-The cost dashboard is available from the environment sidebar under **Analytics > Costs**. It provides:
-
-- **Summary cards** -- Total ingested bytes, egressed bytes, and estimated cost for the selected time range, with a comparison to the previous equivalent period showing the percentage change.
-- **Per-pipeline breakdown** -- A table showing each pipeline's bytes in, bytes out, data reduction percentage, and attributed cost. This helps identify which pipelines are the most expensive.
-- **Per-team breakdown** -- Aggregated cost by team, useful for chargeback and cost allocation.
-- **Per-environment breakdown** -- Cost comparison across environments, each with its own per-GB rate.
-- **Time series chart** -- A line chart showing cost trends over time.
-
-You can select a time range of 1 hour, 6 hours, 1 day, 7 days, or 30 days to adjust the reporting window.
-
-## Cost-per-GB configuration
-
-Each environment has a **Cost per GB** setting (in cents) that determines how data volume is converted to a dollar cost. This is configured in **Settings > Environments**.
-
-The default value is 0 (cost tracking disabled). Set it to your actual per-GB cost to enable cost attribution. For example, if your observability platform charges $2.50 per GB ingested, set the value to 250 (cents).
-
-{% hint style="info" %}
-The cost-per-GB rate is applied uniformly to all pipelines in the environment. If different pipelines route to destinations with different pricing, consider using separate environments.
-{% endhint %}
-
-## AI cost recommendations
-
-When AI is enabled for your team, VectorFlow can analyze your pipeline configurations and metrics to generate cost optimization recommendations. Recommendations are generated by a background analysis job and appear in the **Recommendations** tab of the cost dashboard.
-
-Each recommendation includes:
-
-- **Title** -- A short description of the suggested optimization
-- **Impact** -- The estimated cost savings if the recommendation is applied
-- **Details** -- An explanation of what to change and why
-- **Pipeline** -- Which pipeline the recommendation applies to
-
-### Managing recommendations
-
-Recommendations have three statuses:
-
-- **Pending** -- New recommendations awaiting review
-- **Applied** -- Recommendations you have acted on
-- **Dismissed** -- Recommendations you have reviewed and decided not to pursue
-
-From the recommendations list, you can:
-
-- **View details** -- Click a recommendation to see the full explanation and the affected pipeline's current configuration
-- **Mark as applied** -- After implementing the suggestion, mark it as applied to track your optimizations
-- **Dismiss** -- If a recommendation is not applicable, dismiss it to remove it from the pending list
-
-{% hint style="info" %}
-Dismissing or applying a recommendation requires the **Editor** role or above.
-{% endhint %}
-
-### Triggering a fresh analysis
-
-Admins can trigger a manual cost analysis run from the recommendations page. This re-evaluates all pipelines in the environment and may generate new recommendations based on current metrics.
-
-## Cost export API
-
-The cost data can be exported via the REST API for integration with external billing or reporting systems. Send a `GET` request to:
-
-```
-GET /api/v1/analytics/costs/export?environmentId=&range=
-```
-
-Parameters:
-
-| Parameter | Required | Description |
-|-----------|----------|-------------|
-| `environmentId` | Yes | The environment to export costs for |
-| `range` | No | Time range: `1h`, `6h`, `1d`, `7d`, or `30d` (default: `30d`) |
-
-The response is a CSV file with per-pipeline cost data. Authentication requires a service account API key with the `read` permission.
diff --git a/docs/public/user-guide/dashboard.md b/docs/public/user-guide/dashboard.md
deleted file mode 100644
index 0dae6cb3..00000000
--- a/docs/public/user-guide/dashboard.md
+++ /dev/null
@@ -1,166 +0,0 @@
-# Dashboard
-
-The dashboard is the landing page you see after logging in. It gives you a high-level view of your observability pipeline health across the selected environment, with real-time metrics, fleet status, and interactive charts.
-
-
-
-## KPI summary cards
-
-The top of the dashboard displays five summary cards that provide an at-a-glance overview of your environment.
-
-| Card | What it shows |
-|------|--------------|
-| **Total Nodes** | The number of Vector agent nodes registered in the current environment. |
-| **Node Health** | A breakdown of node statuses -- **Healthy**, **Degraded**, or **Unreachable** -- so you can spot issues quickly. |
-| **Pipelines** | The total number of deployed (non-draft) pipelines in the environment. |
-| **Pipeline Status** | Counts of pipelines by runtime state: **Running**, **Stopped**, or **Crashed**. |
-| **Log Reduction** | The percentage of data volume reduced by your transforms (see below). |
-
-## Log reduction percentage
-
-The **Log Reduction** card shows how effectively your pipelines are filtering and transforming data before it reaches your sinks. The formula is:
-
-```
-reduction % = (1 - eventsOut / eventsIn) * 100
-```
-
-- A **higher percentage** means more data is being filtered, sampled, or deduplicated by your transforms before reaching downstream destinations.
-- The value is **clamped to 0% minimum** -- if your pipeline produces more events than it receives (e.g. through event splitting), the card shows 0% rather than a negative number.
-- The card also displays the raw events-per-second rates for both input and output so you can see absolute throughput.
-- Color coding provides quick visual feedback: green for reductions above 50%, amber for 10--50%, and neutral for lower values.
-
-{% hint style="info" %}
-Reduction metrics are calculated from the last hour of aggregated pipeline data. If no traffic has flowed recently the card will display a dash.
-{% endhint %}
-
-## Metrics filter bar
-
-Below the summary cards, a filter bar lets you narrow down the charts displayed on the dashboard.
-
-- **Time range** -- Choose from **1 hour**, **6 hours**, **1 day**, or **7 days**. The selected window controls both the data shown in the charts and the automatic refresh interval.
-- **Pipeline filter** -- Select one or more pipelines to focus on. When no pipelines are selected, all deployed pipelines in the environment are shown.
-- **Node filter** -- Select specific agent nodes. Filtering by node automatically restricts the pipeline list to pipelines running on those nodes, and vice versa.
-- **Group by** -- Choose how chart series are broken down:
- - **Pipeline** -- one series per pipeline (default)
- - **Node** -- one series per agent node
- - **Aggregate** -- a single "Total" series combining all pipelines and nodes
-
-## Pipeline metrics charts
-
-The **Pipeline Metrics** section includes three charts:
-
-- **Events In/Out per Second** -- Shows the rate of events entering your sources and leaving your sinks. Comparing the two lines reveals how much data your transforms are reducing.
-- **Bytes In/Out per Second** -- The same comparison in bytes, useful for understanding bandwidth and storage impact.
-- **Errors & Discarded** -- An area chart showing error rates and discarded event rates. A spike here may indicate a misconfigured transform or an unreachable sink.
-
-## System metrics charts
-
-The **System Metrics** section shows resource utilization for the agent nodes in your environment:
-
-- **CPU Usage** -- Percentage of CPU consumed by each Vector agent. Capped at 100%.
-- **Memory Usage** -- Percentage of available memory used by each agent.
-- **Disk I/O** -- Read and write throughput in bytes per second.
-- **Network I/O** -- Receive (Rx) and transmit (Tx) throughput in bytes per second.
-
-When **Group by** is set to **Aggregate**, CPU and memory are averaged across nodes while disk and network rates are summed.
-
-{% hint style="warning" %}
-System metrics require the VectorFlow agent to be reporting node-level telemetry. If a node is unreachable, its metrics will stop updating until connectivity is restored.
-{% endhint %}
-
-## Auto-refresh
-
-Dashboard data refreshes automatically based on the selected time range:
-
-| Time range | Refresh interval |
-|-----------|-----------------|
-| 1 hour | 15 seconds |
-| 6 hours | 60 seconds |
-| 1 day | 60 seconds |
-| 7 days | 5 minutes |
-
-Pipeline status cards also poll every 15 seconds regardless of the selected time range, so you will see status changes (Running, Stopped, Crashed) promptly.
-
-## Analytics
-
-The **Analytics** page (accessible from the sidebar) provides a deeper breakdown of pipeline volume metrics compared to the dashboard summary.
-
-### KPI cards
-
-The analytics page displays four KPI cards with trend indicators comparing the current period to the previous period:
-
-| Card | What it shows |
-|------|--------------|
-| **Bytes In** | Total bytes ingested by all pipeline sources. |
-| **Bytes Out** | Total bytes emitted by all pipeline sinks. |
-| **Events Reduced** | Percentage of events reduced by transforms, calculated as `(1 - eventsOut / eventsIn) × 100`, clamped at 0%. |
-| **Bytes Saved** | Percentage of bytes saved through pipeline processing, calculated as `(1 - bytesOut / bytesIn) × 100`. Hover for a tooltip showing the absolute bytes saved. |
-
-Each card shows a trend arrow indicating whether the metric increased or decreased compared to the previous period. For reduction metrics, an upward trend (more reduction) is shown in green.
-
-### Per-pipeline table
-
-Below the KPI cards, a sortable table shows per-pipeline volume breakdown:
-
-| Column | Description |
-|--------|-------------|
-| **Pipeline** | Pipeline name |
-| **Bytes In / Out** | Total bytes ingested and emitted, formatted as human-readable sizes |
-| **Events Reduced** | Percentage of events reduced, shown with a color-coded progress bar (green > 50%, amber > 20%, red below) |
-| **Bytes Saved** | Percentage of bytes saved, shown with a color-coded progress bar |
-
-Click any column header to sort the table. The time range filter at the top controls the aggregation window.
-
-## Custom dashboard views
-
-You can create personalized dashboard views that display only the panels you care about. Custom views are saved per-user and persist across sessions.
-
-### Creating a view
-
-{% stepper %}
-{% step %}
-### Open the view builder
-Click the **+ New View** button in the tab bar at the top of the dashboard.
-{% endstep %}
-{% step %}
-### Name your view
-Enter a short, descriptive name (up to 50 characters).
-{% endstep %}
-{% step %}
-### Select panels
-Check the panels you want to include. Panels are grouped into three categories:
-
-| Category | Available panels |
-|----------|-----------------|
-| **Pipeline** | Events In/Out, Bytes In/Out, Error Rate, Data Reduction % |
-| **System** | CPU Usage, Memory Usage, Disk I/O, Network I/O |
-| **Summary** | Node Health Summary, Pipeline Health Summary |
-{% endstep %}
-{% step %}
-### Save
-Click **Create**. Your new view will appear as a tab in the tab bar.
-{% endstep %}
-{% endstepper %}
-
-### Arranging panels
-
-Custom view panels support drag-and-drop reordering and resizing.
-
-1. Click the **Editing Layout** button (lock icon) in the top-right corner of the view to unlock the layout.
-2. **Drag** any panel by its header to reposition it within the grid.
-3. **Resize** a panel by dragging its bottom-right corner handle.
-4. Click **Layout Locked** to lock the layout and prevent accidental changes.
-
-Layout changes are saved automatically. Each user's layout is independent — rearranging panels does not affect other users.
-
-### Switching views
-
-The tab bar at the top of the dashboard shows all your custom views alongside the **Default** view. Click any tab to switch to that view. Each custom view has its own time range picker and filter bar for any chart panels it includes.
-
-### Editing and deleting views
-
-Hover over a custom view tab to reveal the edit and delete icons. Click the pencil icon to update the view name or panel selection, or click the trash icon to remove the view.
-
-{% hint style="info" %}
-Custom views are scoped to your user account. Other team members will not see your custom views, and you will not see theirs.
-{% endhint %}
diff --git a/docs/public/user-guide/environments.md b/docs/public/user-guide/environments.md
deleted file mode 100644
index b187705a..00000000
--- a/docs/public/user-guide/environments.md
+++ /dev/null
@@ -1,220 +0,0 @@
-# Environments
-
-Environments are isolated deployment contexts that let you separate your pipelines, agents, and secrets across lifecycle stages such as **development**, **staging**, and **production**. Each environment maintains its own independent set of resources, so changes in one environment never affect another.
-
-
-
-## Why use environments?
-
-- **Isolation** -- Keep experimental pipeline changes out of production.
-- **Separate secrets** -- The same secret name (e.g., `API_KEY`) can hold different values in each environment.
-- **Independent fleets** -- Agent nodes enroll into a specific environment and only run pipelines assigned to that environment.
-- **Promotion workflow** -- Build and test a pipeline in dev, then promote it to staging and production when ready.
-
-## Environment selector
-
-The environment selector is the dropdown in the header bar. Switching it changes the global context for the entire application -- the pipeline list, fleet view, alerts, and all other pages update to show only resources belonging to the selected environment.
-
-{% hint style="info" %}
-When you switch environments, the pipeline list, fleet view, and alerts page update to show only resources for that environment. Your selection is persisted across sessions.
-{% endhint %}
-
-### Default environment
-
-You can set a **default environment** so VectorFlow automatically selects it when you log in or switch teams.
-
-**User default (per-user):** Click the star icon next to any environment in the environment selector dropdown. The starred environment becomes your personal default for that team. Click the star again to clear it.
-
-**Admin default (per-team):** Team admins can set a team-wide default environment from **Settings > Team**. This applies to all team members who have not set their own personal default.
-
-The fallback chain when loading the app is:
-
-1. **User default** -- your personally starred environment (if set)
-2. **Admin team default** -- the team-level default environment (if configured by an admin)
-3. **First in list** -- the first environment alphabetically
-
-{% hint style="info" %}
-The team selector in the header also supports starring. Click the star next to a team to set it as your default team on login.
-{% endhint %}
-
-## Creating an environment
-
-{% stepper %}
-{% step %}
-### Open the Environments page
-Navigate to **Environments** in the sidebar and click **New Environment**.
-{% endstep %}
-{% step %}
-### Enter a name
-Give the environment a descriptive name (e.g., "Production", "Staging", "Dev"). Names can be up to 100 characters.
-{% endstep %}
-{% step %}
-### Create
-Click **Create Environment**. You are redirected to the environments list where your new environment appears.
-{% endstep %}
-{% endstepper %}
-
-## Environment detail page
-
-Click any environment name in the list to open its detail page. The detail view shows:
-
-- **Overview cards** -- At-a-glance counts for agent nodes and pipelines assigned to this environment.
-- **Vector Nodes table** -- All nodes registered in this environment with their name, host address, status, and last-seen timestamp. Click a node name to jump to its fleet detail page.
-- **Agent Enrollment** -- Generate or revoke the enrollment token that agents use to connect to this environment.
-- **Secret Backend** -- Configure how pipelines resolve secret references (see below).
-- **Secrets & Certificates** -- Manage the secrets and TLS certificates available to pipelines in this environment.
-
-You can edit the environment name or delete the environment from the detail page header.
-
-{% hint style="danger" %}
-Deleting an environment permanently removes all of its pipelines, nodes, and secrets. This action cannot be undone.
-{% endhint %}
-
-## Agent enrollment tokens
-
-Before an agent can connect to an environment, you must generate an **enrollment token** on the environment detail page. The token is displayed once -- copy it immediately.
-
-### Linux (binary)
-
-The install script downloads the agent binary, verifies its checksum, installs [Vector](https://vector.dev) if it is not already present, and creates a systemd service:
-
-```bash
-curl -sSfL https://raw.githubusercontent.com/TerrifiedBug/vectorflow/main/agent/install.sh | \
- sudo bash -s -- --url https://your-vectorflow-instance --token
-```
-
-### Docker
-
-```bash
-docker run -d --name vf-agent --restart unless-stopped \
- -e VF_URL=https://your-vectorflow-instance \
- -e VF_TOKEN= \
- -v /var/lib/vf-agent:/var/lib/vf-agent \
- ghcr.io/terrifiedbug/vectorflow-agent:latest
-```
-
-You can regenerate or revoke the token at any time. Revoking a token prevents new agents from enrolling, but already-connected agents continue operating until their individual node tokens are revoked from the Fleet page.
-
-## Secret backends
-
-Each environment can use a different backend for resolving secret references in pipeline configurations:
-
-| Backend | Description |
-|---------|-------------|
-| **Built-in** | VectorFlow stores secrets internally and delivers them to agents as environment variables. This is the default. |
-| **HashiCorp Vault** | Secrets are fetched from a Vault instance. Configure the Vault address, auth method (token, AppRole, or Kubernetes), and mount path. |
-| **AWS Secrets Manager** | Secrets are resolved from AWS Secrets Manager at deploy time. |
-| **Exec** | A custom script on the agent host is executed to retrieve secrets. |
-
-## Secrets per environment
-
-Secrets are scoped to individual environments. The same secret name can hold different values in each environment. For example, you might have an `ELASTICSEARCH_API_KEY` secret that points to a test cluster in your dev environment and a production cluster in your production environment.
-
-Manage secrets from the **Secrets & Certificates** section on the environment detail page.
-
-## Pipeline promotion
-
-You can copy a pipeline from one environment to another using the **Promote to...** action on the Pipelines page. This is the recommended workflow for moving validated configurations through your lifecycle stages (e.g., dev to staging to production).
-
-{% hint style="warning" %}
-Secrets and certificates are stripped during promotion. After promoting a pipeline, configure the appropriate secrets in the target environment before deploying.
-{% endhint %}
-
-## Deploy approval
-
-Environments can require **approval** before pipelines are deployed. This is useful for production environments where you want a second pair of eyes on every configuration change. Approval and deployment are **separate actions** -- a reviewer approves the request, and then anyone with deploy access can execute the deployment.
-
-### Enabling approval
-
-{% stepper %}
-{% step %}
-### Open environment settings
-Navigate to **Environments**, click the environment name, and click **Edit**.
-{% endstep %}
-{% step %}
-### Enable the toggle
-Turn on **Require approval for deploys**.
-{% endstep %}
-{% step %}
-### Save
-Click **Save** to apply the change.
-{% endstep %}
-{% endstepper %}
-
-When enabled:
-- Users with the **Editor** role will see a **Request Deploy** button instead of **Publish to Agents** in the deploy dialog. Their deploy requests are queued for review.
-- Any team member with deploy access (editor or admin) can **approve** a pending request. Approval does not automatically deploy -- it marks the request as ready.
-- Once approved, any team member with deploy access can **deploy** the approved request. The deploy dialog shows a **Deploy** button for approved requests.
-- Approved requests can also be **cancelled** by anyone with deploy access if the deployment is no longer needed.
-- Users with the **Admin** role can deploy directly (no approval needed), bypassing the approval flow entirely.
-- A **Pending Approval** or **Approved** badge appears on the pipeline list and in the pipeline editor toolbar to indicate the request status.
-
-{% hint style="info" %}
-The person who submitted a deploy request cannot approve their own request. This enforces a four-eyes principle -- a second team member must always review the deployment.
-{% endhint %}
-
-### Deploy tracking
-
-VectorFlow tracks who actually deployed a pipeline separately from who approved it. The deploy history records:
-
-- **Requested by** -- the user who submitted the deploy request
-- **Approved by** -- the user who approved the request
-- **Deployed by** -- the user who executed the deployment
-- **Status** -- the request lifecycle: `PENDING` → `APPROVED` → `DEPLOYED` (or `REJECTED` / `CANCELLED`)
-
-This separation provides a clear audit trail for compliance, especially in regulated environments where you need to know exactly who authorized and executed each deployment.
-
-For more details on how the approval workflow operates, see [Pipelines -- Deploy approval workflows](pipelines.md#deploy-approval-workflows).
-
-## Editing and deleting environments
-
-- **Edit** -- Click the **Edit** button on the environment detail page to rename the environment or change its secret backend configuration.
-- **Delete** -- Click the **Delete** button to permanently remove the environment. You must have the Admin role on the team to delete an environment.
-
-## Git Integration
-
-VectorFlow can automatically commit pipeline YAML files to a Git repository whenever a pipeline is deployed or deleted. This provides an audit trail, version history, and hook points for external CI/CD workflows.
-
-### Setup
-
-{% stepper %}
-{% step %}
-### Navigate to Environment Settings
-Go to **Environments** and click on the environment you want to configure.
-{% endstep %}
-{% step %}
-### Configure Git Integration
-In the **Git Integration** card, enter:
-- **Repository URL**: The HTTPS URL of your Git repo (e.g., `https://github.com/org/pipeline-configs.git`)
-- **Branch**: The branch to commit to (default: `main`)
-- **Access Token**: A personal access token with write access to the repo
-{% endstep %}
-{% step %}
-### Test Connection
-Click **Test Connection** to verify VectorFlow can reach the repository.
-{% endstep %}
-{% endstepper %}
-
-### How It Works
-
-When you deploy a pipeline, VectorFlow commits the generated YAML to `{environment-name}/{pipeline-name}.yaml` in the configured repository. When you delete a pipeline, the file is removed with a commit.
-
-{% hint style="info" %}
-Git sync is a post-deploy side effect. If the Git push fails, the pipeline deploy still succeeds -- you will see a warning toast in the UI.
-{% endhint %}
-
-### GitOps Mode
-
-Each environment has a **GitOps Mode** setting that controls the direction of Git synchronization:
-
-| Mode | Description |
-|------|-------------|
-| **Off** | Git integration is disabled (default). |
-| **Push Only** | Pipeline YAML is committed to the repo on deploy. Changes in git are not pulled back. |
-| **Bi-directional** | Pipeline YAML is committed on deploy AND pushes to the repo trigger pipeline imports via webhook. |
-
-When **Bi-directional** mode is enabled, a webhook URL and secret are generated. Configure these in your GitHub repository webhook settings to enable automatic pipeline imports on push. See the [GitOps guide](../operations/gitops.md) for detailed setup instructions.
-
-{% hint style="warning" %}
-In bi-directional mode the Git repository is the source of truth. Manual edits in the VectorFlow UI may be overwritten on the next push. The pipeline editor displays a banner to remind users of this.
-{% endhint %}
diff --git a/docs/public/user-guide/fleet.md b/docs/public/user-guide/fleet.md
deleted file mode 100644
index 77b60146..00000000
--- a/docs/public/user-guide/fleet.md
+++ /dev/null
@@ -1,316 +0,0 @@
-# Fleet Management
-
-The **Fleet** page gives you a centralized view of every agent node enrolled in the current environment. From here you can monitor node health, inspect system resources, view pipeline metrics, trigger agent updates, and stream live logs.
-
-
-
-## Node list
-
-All enrolled agent nodes are displayed in a table with the following columns:
-
-| Column | Description |
-|--------|-------------|
-| **Name** | The node name. Click it to open the node detail page. You can rename nodes from the detail view. |
-| **Host:Port** | The hostname or IP address and API port the agent is listening on. |
-| **Environment** | The environment the node is enrolled in. |
-| **Labels** | Key-value labels assigned to the node, shown as `key=value` badges. See [Node Labels](#node-labels) below. |
-| **Version** | The Vector version running on the node. |
-| **Agent Version** | The VectorFlow agent version, plus deployment mode (Docker or Binary). An **Update available** badge appears when a newer version exists. |
-| **Status** | Current health status (see statuses below). |
-| **Last Seen** | How recently the agent last communicated with the server. |
-
-If no agents have enrolled yet, the page shows a prompt directing you to generate an enrollment token in the environment settings.
-
-## Node health statuses
-
-Agent nodes report their health through periodic heartbeats. VectorFlow derives the following statuses:
-
-- **Online** -- The agent is sending heartbeats within the expected interval. The node is healthy and processing pipelines.
-- **Unreachable** -- The agent has missed heartbeats beyond the configured threshold (default: 3 missed intervals). This typically means the agent process has stopped, the host is down, or there is a network issue.
-
-The heartbeat threshold is calculated as `fleetPollIntervalMs * fleetUnhealthyThreshold`. With the default settings of a 15-second poll interval and a threshold of 3, a node is marked unreachable after approximately 45 seconds of silence.
-
-{% hint style="info" %}
-You can adjust the heartbeat interval and unhealthy threshold in the system settings.
-{% endhint %}
-
-## Node detail page
-
-Click a node name to open its detail page, which provides deep visibility into that specific agent.
-
-
-
-### Node details card
-
-A summary card shows key information at a glance:
-
-- **Status** -- Current health status
-- **Environment** -- Which environment the node belongs to
-- **Agent Version** -- The installed VectorFlow agent version
-- **Vector Version** -- The Vector binary version
-- **Last Heartbeat** -- Timestamp of the most recent heartbeat
-- **Enrolled** -- When the agent first enrolled
-- **Host / API Port** -- Network address details
-- **Last Seen / Created** -- Timestamps for tracking node lifecycle
-
-### System resources
-
-Charts display real-time and historical metrics for the node's host machine:
-
-- **CPU usage** -- Derived from cumulative CPU seconds
-- **Memory usage** -- Used vs. total memory
-- **Disk usage** -- Filesystem used vs. total bytes
-- **Load averages** -- 1, 5, and 15-minute load averages
-- **Network I/O** -- Bytes received and transmitted
-- **Disk I/O** -- Bytes read and written
-
-You can adjust the time window (up to 168 hours / 7 days) to view historical trends.
-
-### Pipeline metrics
-
-A table shows every pipeline deployed to the node along with live throughput data:
-
-| Column | Description |
-|--------|-------------|
-| **Pipeline** | Pipeline name |
-| **Status** | Running, Stopped, Starting, or Crashed |
-| **Events In / Out** | Total event counts with live per-second rates |
-| **Errors** | Total error count with live error rate (highlighted in red if non-zero) |
-| **Bytes In / Out** | Total bytes processed with live byte rates |
-| **Uptime** | How long the pipeline has been running on this node |
-
-### Logs
-
-A live log stream from the agent, with filtering options:
-
-- **Log level** -- Filter by severity (DEBUG, INFO, WARN, ERROR)
-- **Pipeline** -- Scope logs to a specific pipeline running on the node
-
-Logs are paginated and load on demand.
-
-## Agent updates
-
-When a newer agent version is available, an **Update available** badge appears in the node list. The update mechanism depends on the deployment mode:
-
-{% tabs %}
-{% tab title="Binary (Standalone)" %}
-Click the **Update** button in the node list to trigger a self-update. VectorFlow instructs the agent to download the new binary, verify its checksum, and restart. The node shows an **Update pending...** badge while the update is in progress.
-{% endtab %}
-{% tab title="Docker" %}
-Docker-based agents are updated by pulling the latest image. The **Update** button is disabled for Docker nodes -- update them by redeploying the container with the new image tag.
-{% endtab %}
-{% endtabs %}
-
-## Update errors
-
-When a binary agent update fails (for example, due to a download error or checksum mismatch), the fleet page shows an **Update failed** badge on the affected node. Hover over the badge to see the error details.
-
-Common causes:
-
-| Error | Resolution |
-|-------|------------|
-| **404 / download failed** | The release asset may not exist. Check that the release tag and binary name are correct on GitHub. |
-| **checksum mismatch** | The downloaded binary does not match the expected hash. This may indicate a corrupted download or a release that was re-published. Retry the update. |
-| **permission denied** | The agent process does not have write access to its own binary. Check file permissions on the agent host. |
-
-The error badge clears automatically after the next successful update.
-
-## Pipeline deployment matrix
-
-Below the node list, the **Pipeline Deployment Matrix** shows a grid of all deployed pipelines across all nodes in the environment. This lets you see at a glance which pipelines are running on which nodes and their current status.
-
-## Node labels
-
-Labels are key-value pairs you can attach to nodes for organization and selective deployment. Common uses include tagging nodes by region, role, tier, or any custom dimension relevant to your infrastructure.
-
-### Viewing labels
-
-In the fleet table, nodes with labels show a compact **"N label(s)"** button in the Labels column. Click the button to open a popover displaying all labels as `key=value` badges. Nodes with no labels show an empty column.
-
-### Adding and editing labels
-
-{% stepper %}
-{% step %}
-### Open the node detail page
-Click a node name in the fleet table to open its detail page.
-{% endstep %}
-{% step %}
-### Edit labels
-In the **Labels** card, click the **Edit** button.
-{% endstep %}
-{% step %}
-### Add or modify labels
-Use the key-value input pairs to add, modify, or remove labels. Click **Add Label** to add a new pair, or click the **X** button to remove a row.
-{% endstep %}
-{% step %}
-### Save
-Click **Save Labels** to persist the changes.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="info" %}
-Editing labels requires the **Editor** role or above on the team.
-{% endhint %}
-
-### Agent-reported labels
-
-Agents can also report labels in their heartbeat payload. When a label is reported by the agent and also set via the UI, the **UI value takes precedence**. This lets you override agent-reported labels without them being overwritten on the next heartbeat.
-
-### Selective deployment with labels
-
-When deploying a pipeline, you can optionally restrict deployment to nodes matching specific labels. In the deploy dialog, the **Target Nodes** selector lets you pick from all labels in the environment. Selected labels are combined with AND logic -- a node must have all selected labels to receive the pipeline.
-
-The deploy dialog shows a live count of matching nodes (e.g., "3 of 5 nodes match") so you can verify your selection before deploying. When no labels are selected, the pipeline deploys to all nodes in the environment (backward compatible).
-
-{% hint style="warning" %}
-Changing a pipeline's node selector on a subsequent deploy updates the targeting. Nodes that no longer match will stop the pipeline on their next poll.
-{% endhint %}
-
-## Node groups
-
-Node groups let administrators segment their fleet into logical clusters based on node labels -- for example by datacenter, role, or region. Groups are managed from **Settings > Fleet**.
-
-Each node group has:
-
-| Field | Description |
-|-------|-------------|
-| **Name** | A unique display name for the group within the environment. |
-| **Criteria** | A label selector (key-value pairs) that determines which enrolling nodes match the group. An empty criteria matches all nodes. |
-| **Label template** | Key-value labels that are automatically merged into a node's labels when it enrolls and matches the group's criteria. |
-| **Required labels** | Label keys that every node should have. Nodes missing any required label are flagged as non-compliant in the fleet list. |
-
-{% hint style="info" %}
-Label templates are applied once at enrollment time. Changing a group's template does not retroactively update existing nodes.
-{% endhint %}
-
-## Label compliance
-
-When node groups define **required labels**, the fleet list displays a **Non-compliant** badge next to any node that is missing one or more of those labels. This is a warn-only indicator -- non-compliant nodes continue to receive heartbeats and deployments normally.
-
-To resolve a non-compliant node, add the missing labels via the node detail page or ensure the node enrolls with matching labels so that group templates apply automatically.
-
-## Fleet health dashboard
-
-The Health tab on the Fleet page provides an aggregated view of fleet status organized by node group. This is especially useful for large fleets where you want to see health at a glance before drilling into individual nodes.
-
-### Group summary cards
-
-Each node group is represented as a collapsible card showing three metrics:
-
-| Metric | Description |
-|--------|-------------|
-| **Online** | Count of HEALTHY nodes out of the group total (e.g. `4/5`). Shown in amber when any nodes are offline. |
-| **Alerts** | Count of nodes with at least one firing alert rule. Shown in red when greater than zero. |
-| **Compliance** | Percentage of nodes that have all required labels defined by the group. Shown in amber when below 100%. |
-
-### Drill-down
-
-Click any group card to expand it and see a per-node detail table with:
-
-- **Name** — the node name, linked to its detail page
-- **Status** — current health status badge (Healthy, Degraded, Unreachable, Unknown)
-- **CPU Load** — the 1-minute load average from the latest heartbeat, or `--` if no metrics are available
-- **Last Seen** — how long ago the node last sent a heartbeat
-- **Compliance** — whether the node has all required labels for the group
-
-Nodes are sorted by health status with the least healthy nodes shown first, then alphabetically by name.
-
-### Filtering
-
-The toolbar above the group cards supports three filter types:
-
-- **Group** — show only a specific group card
-- **Labels** — filter by label key/value pairs (applied to the per-node detail table inside expanded cards)
-- **Compliance** — toggle between All, Compliant (100% compliance rate), or Non-compliant (below 100%)
-
-{% hint style="info" %}
-Filter state is stored in the URL as query parameters, so you can copy and share the URL with filters applied.
-{% endhint %}
-
-### Ungrouped nodes
-
-Nodes that do not match the criteria of any defined group appear under an **Ungrouped** card. This card behaves the same as any other group card — you can expand it to see the per-node table.
-
-{% hint style="info" %}
-The Ungrouped card only appears when at least one node exists outside all group criteria. If all nodes belong to a group, no Ungrouped card is shown.
-{% endhint %}
-
-## Maintenance mode
-
-Maintenance mode lets you temporarily stop all pipelines on a node without removing it from the fleet. This is useful for host upgrades, kernel patches, disk maintenance, or any situation where you need the node idle but still connected.
-
-### Entering maintenance mode
-
-You can toggle maintenance mode from two places:
-
-- **Fleet list** -- Click the **Maintenance** button in the node's row.
-- **Node detail page** -- Click the **Enter Maintenance** button in the header, or the **Exit Maintenance** button in the orange banner.
-
-Both locations show a confirmation dialog before entering maintenance mode. The dialog warns that all running pipelines on the node will be stopped.
-
-### What happens in maintenance mode
-
-When maintenance mode is enabled on a node:
-
-1. The node's status badge changes to an orange **Maintenance** indicator with a wrench icon.
-2. On the next poll, the agent receives an empty pipeline list from the server, causing all running pipelines to stop gracefully.
-3. The agent continues sending heartbeats, so the node remains visible and manageable in the fleet UI.
-
-{% hint style="info" %}
-Maintenance mode is per-node. Other nodes in the same environment continue running their pipelines normally.
-{% endhint %}
-
-### Exiting maintenance mode
-
-Click **Exit Maintenance** from the fleet list or the node detail page. No confirmation is required. On the next poll cycle, the agent receives its full pipeline configuration again and automatically restarts all pipelines.
-
-{% hint style="warning" %}
-Toggling maintenance mode requires the **Admin** role on the team.
-{% endhint %}
-
-## Drift Detection
-
-Drift detection monitors your fleet for discrepancies between the expected state (what the server says should be running) and the actual state (what agents report). VectorFlow tracks two types of drift:
-
-### Version drift
-
-Version drift occurs when one or more nodes are running a different pipeline version than the latest deployed version. For example, if Pipeline A is at version 5 on the server but a node is still running version 4, that pipeline has version drift on that node.
-
-Version drift is evaluated fleet-wide across all deployed pipelines in an environment. The system compares each node's reported pipeline version (from its heartbeat) against the latest deployed version in the database.
-
-### Config drift
-
-Config drift occurs when a node's running configuration checksum does not match the expected checksum computed by the server. This can happen when:
-
-- A deploy was initiated but the agent has not yet picked up the new configuration
-- The agent's local config file was modified outside of VectorFlow
-- A network issue prevented the agent from receiving the latest config
-
-Config drift is evaluated per-node during heartbeat processing by comparing the agent-reported `configChecksum` against the server-side expected checksum.
-
-{% hint style="info" %}
-Older agents that do not report a config checksum are excluded from config drift detection -- they do not count as drifted.
-{% endhint %}
-
-### Drift alerts
-
-Drift metrics feed into the fleet alerting system. When version drift is detected, alerts can be triggered based on your configured alert rules. The alert message includes details about which pipelines are drifted and what versions each node is running.
-
-### Responding to drift
-
-When drift is detected:
-
-1. **Version drift** -- Check if the affected nodes are reachable and polling. Most version drift resolves automatically on the next agent poll cycle. If it persists, verify the agent is running and can reach the server.
-2. **Config drift** -- Verify that the agent is receiving the latest configuration. Re-deploying the pipeline can force a fresh config push. If the agent's local config was manually modified, the next poll cycle will overwrite it with the server's version.
-
-## Node management
-
-From the node detail page you can:
-
-- **Rename** -- Click the node name in the header to edit it inline.
-- **Revoke Token** -- Revokes the node's authentication token, preventing it from communicating with the server. The node is marked as unreachable.
-- **Delete Node** -- Permanently removes the node record from VectorFlow. This does not stop the agent process on the remote host.
-
-{% hint style="warning" %}
-Revoking a node token immediately prevents the agent from sending heartbeats or receiving pipeline updates. The agent process continues running on the host but operates in isolation until re-enrolled.
-{% endhint %}
diff --git a/docs/public/user-guide/migration-toolkit.md b/docs/public/user-guide/migration-toolkit.md
deleted file mode 100644
index f5453aa9..00000000
--- a/docs/public/user-guide/migration-toolkit.md
+++ /dev/null
@@ -1,214 +0,0 @@
-# Migration Toolkit
-
-The Migration Toolkit helps you migrate existing log pipelines from other platforms to VectorFlow. Upload your existing configuration, let VectorFlow parse and translate it, then generate a ready-to-deploy pipeline.
-
-## Supported platforms
-
-| Platform | Status |
-|----------|--------|
-| **FluentD** | Supported |
-| Logstash | Coming soon |
-| Filebeat | Coming soon |
-| Telegraf | Coming soon |
-
-## Migration workflow
-
-The migration process follows a five-step workflow:
-
-{% stepper %}
-{% step %}
-### Create a migration project
-Navigate to **Settings > Migration** and click **New Migration**. Give the project a name, select the source platform (FluentD), and paste or upload your existing configuration file.
-
-The configuration file can be up to 500 KB in size.
-{% endstep %}
-{% step %}
-### Parse the configuration
-Click **Parse** to analyze the uploaded configuration. VectorFlow parses the config into structured blocks representing sources, filters, and outputs. Each block is identified with its plugin type and parameters.
-
-After parsing, a **readiness report** shows how well the configuration maps to Vector components, including a readiness score (0-100%).
-{% endstep %}
-{% step %}
-### Translate to Vector
-Click **Translate** to convert the parsed blocks into Vector configuration. This step uses AI to translate each block from the source platform's format to Vector's TOML/YAML configuration.
-
-{% hint style="info" %}
-AI translation requires an AI provider to be configured for your team. Go to **Settings > AI** to set up an API key.
-{% endhint %}
-
-Each translated block includes a confidence score indicating how reliable the translation is. Low-confidence blocks may need manual review.
-{% endstep %}
-{% step %}
-### Validate
-Click **Validate** to run the generated Vector configuration through Vector's built-in validator. This catches syntax errors, invalid field names, and configuration conflicts before you deploy.
-
-If validation fails, the error messages are displayed so you can fix the translated configuration. You can manually edit individual block configs and re-validate.
-{% endstep %}
-{% step %}
-### Generate pipeline
-Click **Generate Pipeline** to create a VectorFlow pipeline from the translated configuration. Select the target environment and give the pipeline a name. VectorFlow creates the pipeline with all nodes, edges, and configuration pre-populated from the migration.
-
-The generated pipeline starts as a draft. Review it in the pipeline editor, make any final adjustments, then deploy when ready.
-{% endstep %}
-{% endstepper %}
-
-## Built-in templates
-
-VectorFlow includes 10 built-in migration templates that cover common FluentD patterns. When the parser detects a config that closely matches a template, the template's pre-translated Vector blocks are used instead of AI translation, resulting in higher accuracy and faster processing.
-
-| Template | Description |
-|----------|-------------|
-| Tail to Elasticsearch | File tailing with Elasticsearch output |
-| Tail to Kafka | File tailing with Kafka output |
-| Tail to S3 | File tailing with S3 output |
-| Syslog to Elasticsearch | Syslog input with Elasticsearch output |
-| Forward Bridge | FluentD forward protocol bridging |
-| HTTP to Datadog | HTTP input with Datadog output |
-| Kubernetes to Loki | Kubernetes log collection with Loki output |
-| Multi-output Fanout | Single input routing to multiple outputs |
-| Log Parsing and Enrichment | Complex parsing with field enrichment |
-| Grep Routing | Content-based log routing using grep filters |
-
-## AI translation
-
-For configurations that do not match a built-in template, VectorFlow uses AI to translate each block. The AI translator:
-
-- Receives the parsed block structure along with the source platform context
-- Generates the equivalent Vector component configuration
-- Assigns a confidence score based on the complexity and clarity of the mapping
-- Flags any warnings about features that may not have a direct Vector equivalent
-
-You can re-translate individual blocks if the initial result is not satisfactory. Click the **Re-translate** button next to any block to trigger a fresh AI translation for that specific block.
-
-### Manual block editing
-
-After translation, you can manually edit any block's configuration. Click a block in the translation results to open its config editor. Changes are saved to the migration project and reflected when you regenerate the Vector YAML.
-
-## Readiness score
-
-The readiness score (0-100%) is computed during the parse step and indicates how smoothly the migration is likely to go. It is based on:
-
-- **Plugin coverage** -- What percentage of the source config's plugins have known Vector equivalents
-- **Configuration complexity** -- How many advanced or non-standard features are used
-- **Template match** -- Whether the config matches a built-in template
-
-A score above 80% generally indicates a straightforward migration. Scores below 50% suggest significant manual work may be needed.
-
-The readiness report also includes a **plugin inventory** listing every plugin found in the source config and its mapping status (mapped, partially mapped, or unmapped).
-
-## Project management
-
-Migration projects are scoped to a team. From the migration list page you can:
-
-- View all migration projects with their status, readiness score, and creation date
-- Open a project to continue the workflow from where you left off
-- Delete a project you no longer need
-
-Project statuses track the workflow progress:
-
-| Status | Description |
-|--------|-------------|
-| **Draft** | Project created, configuration uploaded |
-| **Parsing** | Configuration is being parsed |
-| **Translating** | Blocks are being translated via AI |
-| **Validating** | Generated config is being validated |
-| **Ready** | Translation complete, ready to generate pipeline |
-| **Generating** | Pipeline is being created |
-| **Completed** | Pipeline generated successfully |
-| **Failed** | An error occurred (see error message for details) |
-
----
-
-## Vector Config Import
-
-The Vector Config Import tool lets you quickly import existing Vector YAML or TOML configurations directly into VectorFlow pipelines. Unlike the Migration Toolkit (which translates from other platforms like FluentD), this tool works with Vector configs you already have—whether exported from another Vector instance, version-controlled, or generated by a script.
-
-### Supported formats
-
-- **YAML** (.yaml, .yml)
-- **TOML** (.toml)
-
-### What happens during import
-
-When you import a Vector config, VectorFlow:
-
-1. Parses the configuration file into its component structure (sources, transforms, sinks)
-2. **Automatically detects independent data paths** — each logical pipeline is identified and can be split into separate VectorFlow pipelines if needed
-3. Extracts global configuration (api, enrichment_tables, etc.) that applies across all pipelines
-4. Creates nodes, edges, and layout automatically using intelligent graph positioning
-5. Normalizes deprecated field names and auth headers for compatibility
-
-### Quick start: Import in the editor
-
-The fastest way to import is via the pipeline editor:
-
-1. Open the pipeline editor
-2. Press **Cmd+I** (or Ctrl+I on Windows/Linux) to open the import dialog
-3. Select your Vector config file (YAML or TOML)
-4. Review the imported nodes and connections
-5. Save and deploy
-
-The imported config is merged into the current pipeline. All nodes and edges are positioned automatically.
-
-{% hint style="info" %}
-This method imports into a single pipeline. If your config contains multiple independent pipelines, use the REST API to import each separately with different names.
-{% endhint %}
-
-### Advanced: Import via REST API
-
-For bulk imports or integration with external tools, use the REST API:
-
-```bash
-curl -X POST https://your-vectorflow-instance/api/v1/pipelines/import \
- -H "Authorization: Bearer vf_" \
- -H "Content-Type: application/json" \
- -d '{
- "name": "imported-pipeline",
- "description": "Imported from production Vector config",
- "yaml": ""
- }'
-```
-
-Response includes the created pipeline ID and node/edge count:
-
-```json
-{
- "pipeline": {
- "id": "pipeline_123",
- "name": "imported-pipeline",
- "nodeCount": 5,
- "edgeCount": 4
- }
-}
-```
-
-{% hint style="warning" %}
-The API requires the `pipelines.write` permission. Use a service account with appropriate scopes.
-{% endhint %}
-
-### Subgraph detection
-
-When your Vector config contains multiple independent data paths, VectorFlow detects them automatically. For example, a config with:
-
-- Source A → Transform 1 → Sink A
-- Source B → Transform 2 → Sink B
-
-...can be split into two separate pipelines. During import, you can:
-
-- Import all into one pipeline (fully connected via shared transforms)
-- Rename and select which subgraphs to include
-- Import each subgraph separately using the REST API with appropriate filtering
-
-### Global configuration
-
-Vector global settings like `api`, `enrichment_tables`, and `log` schemas are extracted during import and stored as pipeline-wide configuration. These settings apply to all components in the imported pipeline.
-
-You can edit global config from the **Pipeline Settings** panel in the editor.
-
-### Known limitations and tips
-
-- **Field normalization**: Deprecated field names (e.g., `fingerprinting` → `fingerprint`) are automatically updated for compatibility
-- **Auth headers**: Request Authorization headers are converted to Vector's canonical `auth` structure with strategy and token/credentials
-- **Type fallback**: If a component type is not found in VectorFlow's catalog, it's added as an unresolved type—you can manually edit its config but validation may be limited
-- **Empty sections**: Sources/transforms/sinks sections that are empty are skipped
-- **Comments and formatting**: YAML/TOML comments and formatting are not preserved (configs are reparsed and normalized)
diff --git a/docs/public/user-guide/pipeline-dependencies.md b/docs/public/user-guide/pipeline-dependencies.md
deleted file mode 100644
index 18cc20ce..00000000
--- a/docs/public/user-guide/pipeline-dependencies.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Pipeline Dependencies
-
-Pipeline dependencies let you model upstream/downstream relationships between pipelines within the same environment. VectorFlow uses these relationships to warn you at deploy time, visualize data flow, and prevent configuration mistakes.
-
-## What are dependencies?
-
-A dependency means "Pipeline B depends on Pipeline A." In this relationship:
-
-- **Pipeline A** is the **upstream** -- it produces data that Pipeline B consumes.
-- **Pipeline B** is the **downstream** -- it relies on Pipeline A being deployed and running.
-
-Dependencies are always within a single environment. You cannot create cross-environment dependencies.
-
-## Creating a dependency
-
-{% stepper %}
-{% step %}
-### Open the pipeline editor
-Navigate to the pipeline you want to add a dependency to and open it in the editor.
-{% endstep %}
-{% step %}
-### Open the Dependencies tab
-In the pipeline detail panel, click the **Dependencies** tab to see existing upstream dependencies.
-{% endstep %}
-{% step %}
-### Add a dependency
-Click **Add Dependency** and select the upstream pipeline from the dropdown. Only pipelines in the same environment are shown as candidates.
-{% endstep %}
-{% step %}
-### Save
-The dependency is saved immediately. You can optionally add a description to document why the relationship exists.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="info" %}
-Adding or removing dependencies requires the **Editor** role or above on the team.
-{% endhint %}
-
-## Removing a dependency
-
-From the Dependencies tab, click the remove button next to any upstream dependency. The relationship is removed immediately.
-
-## DAG visualization
-
-The **Dependency Graph** page (accessible from the environment sidebar) renders all pipelines and their dependencies as a directed acyclic graph (DAG). Each node in the graph represents a pipeline, and edges show the upstream-to-downstream flow.
-
-The graph helps you:
-
-- Understand the overall data flow across your environment
-- Identify pipelines with many dependents (high fan-out)
-- Spot isolated pipelines with no relationships
-
-Pipeline nodes are color-coded by status: deployed pipelines appear in their normal color, while draft (undeployed) pipelines are visually distinct.
-
-## Deployment order implications
-
-VectorFlow uses dependencies to surface warnings during deployment:
-
-- **Deploying a downstream pipeline** -- If any of its upstream dependencies are not yet deployed (still in draft), VectorFlow shows a warning listing the undeployed upstreams. You can still proceed, but the warning helps you avoid deploying a consumer before its producer is ready.
-- **Undeploying an upstream pipeline** -- If any downstream pipelines are currently deployed and depend on the pipeline you are undeploying, VectorFlow warns you that those pipelines may lose their data source.
-
-These are advisory warnings only -- they do not block deployment. The goal is to keep you informed so you can coordinate rollouts across related pipelines.
-
-## Cycle prevention
-
-VectorFlow enforces that the dependency graph must be a DAG (directed acyclic graph). When you attempt to add a dependency that would create a circular loop, the operation is rejected with an error:
-
-> Cannot add: would create a circular dependency
-
-For example, if Pipeline A depends on Pipeline B, and Pipeline B depends on Pipeline C, you cannot make Pipeline C depend on Pipeline A because that would form a cycle (A -> B -> C -> A).
-
-The cycle detection algorithm runs automatically whenever you add a new dependency. It traverses the existing graph to verify that no loop would be created before persisting the relationship.
-
-{% hint style="warning" %}
-Self-references are also prevented -- a pipeline cannot depend on itself.
-{% endhint %}
diff --git a/docs/public/user-guide/pipeline-editor.md b/docs/public/user-guide/pipeline-editor.md
deleted file mode 100644
index 7f126491..00000000
--- a/docs/public/user-guide/pipeline-editor.md
+++ /dev/null
@@ -1,301 +0,0 @@
-# Pipeline Editor
-
-The pipeline editor is a visual canvas where you design data pipelines by connecting sources, transforms, and sinks. It provides a drag-and-drop interface powered by a flow-graph layout, so you can build and modify pipelines without writing configuration files by hand.
-
-
-
-## Editor layout
-
-The editor is divided into four main areas:
-
-| Area | Location | Purpose |
-|------|----------|---------|
-| **Component Palette** | Left sidebar | Lists all available Vector component types, organized by kind and category. |
-| **Canvas** | Center | The main workspace where you arrange and connect nodes. |
-| **Detail Panel** | Right sidebar | Configuration form for the currently selected node. |
-| **Toolbar** | Top bar | Actions for saving, validating, deploying, and managing the pipeline. |
-
-## Component palette
-
-The left sidebar has two tabs:
-
-- **Catalog** -- Lists every available Vector component, grouped by kind (Sources, Transforms, Sinks). Each section can be collapsed and is further organized by category (e.g. "Cloud Platform", "Aggregating", "Messaging").
-- **Shared** -- Lists [shared components](shared-components.md) available in the current environment. Filter by kind using the All / Source / Transform / Sink buttons.
-
-Use the **search bar** at the top of the palette to filter components by name, type, description, or category. The search applies to whichever tab is active.
-
-### Adding a component
-
-Drag a component from the palette and drop it onto the canvas. A new node appears at the drop position, pre-configured with sensible defaults. You can also right-click the canvas to paste previously copied nodes.
-
-Dragging a shared component from the **Shared** tab creates a **linked node** -- the node's configuration is pre-filled from the shared component and pinned to its current version. See [Shared Components](shared-components.md) for details on linked node behavior.
-
-## Canvas
-
-The canvas is where your pipeline takes visual shape. Each component is represented as a **node**, and data flow between components is represented as **edges** (connections).
-
-### Interacting with the canvas
-
-- **Pan** -- Click and drag on empty canvas space to move around.
-- **Zoom** -- Use the scroll wheel or the zoom controls in the bottom-left corner.
-- **Select a node** -- Click a node to select it. Its configuration appears in the detail panel on the right.
-- **Multi-select** -- Hold Shift and click additional nodes to select multiple components at once. The detail panel shows bulk actions (Copy All, Delete All).
-- **Reposition** -- Drag a node to move it to a new position on the canvas.
-- **Connect nodes** -- Drag from a node's output port (right side) to another node's input port (left side) to create a connection. The editor enforces data-type compatibility and prevents self-connections.
-- **Fit to view** -- The canvas automatically fits all nodes into view when first loaded. Use the zoom controls to reset the view.
-
-### Context menus
-
-- **Right-click a node** -- Opens a menu with Copy, Paste, Duplicate, Delete, and **Save as Shared Component** actions. The shared component option extracts the node's configuration into a reusable [shared component](shared-components.md) and links the node to it.
-- **Right-click an edge** -- Opens a menu with a Delete connection action.
-
-## Node types
-
-### Sources
-
-Sources are where data **enters** the pipeline. They have an output port on the right side and are color-coded green. Examples include:
-
-- `syslog` -- Receive syslog messages over TCP or UDP
-- `file` -- Tail log files from disk
-- `kafka` -- Consume from Kafka topics
-- `http_server` -- Accept events over HTTP
-- `demo_logs` -- Generate synthetic log events for testing
-- `datadog_agent`, `splunk_hec` -- Receive data from vendor agents
-
-### Transforms
-
-Transforms **process and modify** data as it flows through the pipeline. They have both an input port (left) and an output port (right), and are color-coded blue. Key types include:
-
-- `remap` -- Apply VRL (Vector Remap Language) expressions to reshape events
-- `filter` -- Drop events that do not match a VRL condition
-- `sample` -- Randomly sample a percentage of events
-- `route` -- Split events into multiple outputs based on VRL conditions
-- `dedupe` -- Remove duplicate events based on field values
-- `reduce` -- Aggregate multiple events into one
-- `log_to_metric` -- Convert log events into metric data
-
-### Sinks
-
-Sinks are where data **exits** the pipeline to downstream destinations. They have an input port on the left side and are color-coded orange. Examples include:
-
-- `elasticsearch` -- Send to Elasticsearch / OpenSearch
-- `aws_s3` -- Write to Amazon S3 buckets
-- `http` -- Forward over HTTP to any endpoint
-- `console` -- Print to standard output (useful for debugging)
-- `datadog_logs`, `splunk_hec_logs` -- Send to vendor platforms
-- `kafka` -- Produce to Kafka topics
-- `loki` -- Send to Grafana Loki
-
-### Live metrics on nodes
-
-When a pipeline is deployed, each node displays live throughput metrics directly on the canvas:
-
-- **Events per second** and **bytes per second** in a compact readout
-- A **status dot** indicating health (green for healthy, amber for degraded)
-- A **sparkline** showing recent throughput trends
-
-
-
-## Detail panel
-
-Click any node on the canvas to open its configuration in the detail panel on the right.
-
-The panel has two tabs:
-
-- **Config** -- The component configuration form (always visible).
-- **Live Tail** -- Sample live events flowing through the component (visible only when the pipeline is deployed). See [Live Tail](#live-tail) below.
-
-The **Config** tab shows:
-
-- **Component name and kind** -- The display name, a badge indicating source/transform/sink, and a delete button.
-- **Name** -- A human-readable label for the component (e.g. "Traefik Logs"). Changing the name requires saving, but does not require a redeploy.
-- **Component ID** -- An auto-generated unique identifier used in the backend configuration (read-only). This key is set when the node is created and never changes.
-- **Enabled toggle** -- Disable a component to exclude it from the generated configuration without removing it from the canvas.
-- **Type** -- The Vector component type (read-only).
-- **Configuration form** -- Auto-generated form fields based on the component's configuration schema. Required fields are marked, and each field has contextual help.
-- **Secret picker** -- Sensitive fields (passwords, API keys, tokens) display a secret picker instead of a text input. You must select an existing secret or create a new one inline -- plaintext values cannot be entered directly into sensitive fields. See [Security](../operations/security.md#sensitive-fields) for details.
-
-### Linked shared components
-
-When a node is linked to a [shared component](shared-components.md), the detail panel shows additional elements:
-
-- A **purple banner** with the shared component name and an "Open in Library" link.
-- **Read-only configuration** -- All config fields are disabled. Edits must be made on the shared component's Library page.
-- An **Unlink** button to convert the node back to a regular editable component (the current config snapshot is preserved).
-- When the shared component has been updated since the node was last synced, an **amber update banner** appears with an "Accept update" button to pull in the latest configuration.
-
-### VRL editor for transforms
-
-For **remap**, **filter**, and **route** transforms, the detail panel includes an integrated VRL editor (powered by Monaco) instead of a plain text field. The VRL editor provides:
-
-- Syntax highlighting for VRL code
-- Autocomplete for VRL functions
-- An inline snippet drawer to insert common VRL patterns (see [VRL Snippets](vrl-snippets.md))
-- A fields panel showing the schema of upstream source events
-- A test runner that lets you execute your VRL code against sample JSON events and see the output
-
-## Toolbar
-
-The toolbar runs along the top of the editor and provides the following actions (left to right):
-
-| Action | Shortcut | Description |
-|--------|----------|-------------|
-| **Save** | `Cmd+S` | Save the current pipeline state. A dot indicator appears when there are unsaved changes. |
-| **Validate** | -- | Run server-side validation on the generated Vector configuration and report any errors. |
-| **Undo** | `Cmd+Z` | Undo the last change. |
-| **Redo** | `Cmd+Shift+Z` | Redo a previously undone change. |
-| **Delete** | `Delete` | Delete the currently selected node or edge. |
-| **Import** | `Cmd+I` | Import a Vector configuration file (YAML or TOML) to populate the canvas. |
-| **Export** | `Cmd+E` | Export the pipeline as a YAML or TOML file. |
-| **Save as Template** | -- | Save the current pipeline layout as a reusable template. |
-| **Version History** | -- | View all deployed versions, compare diffs, and rollback to a previous version. |
-| **Discard Changes** | -- | Revert the pipeline to its last deployed configuration. Only visible when the pipeline is deployed and has unsaved changes. |
-| **Metrics** | -- | Toggle the metrics chart panel at the bottom of the editor. |
-| **Logs** | -- | Toggle the live logs panel. A red dot appears when recent errors have been detected. |
-| **Settings** | -- | Open pipeline-level settings (log level, global configuration). A blue dot indicates active global config. |
-
-### Pipeline settings
-
-Click the **Settings** button in the toolbar to open the pipeline settings panel. Available options include:
-
-- **Log Level** -- Sets the Vector log verbosity for this pipeline (`trace`, `debug`, `info`, `warn`, `error`).
-- **Metadata Enrichment** -- When enabled, VectorFlow automatically injects a `vectorflow_metadata_enrich` remap transform before every sink at deploy time. This adds two fields to every event:
- - `.vectorflow.environment` -- the name of the environment the pipeline is deployed in.
- - `.vectorflow.pipeline_version` -- the version number of this deployment.
-- **Compliance Tags** -- Assign compliance labels (e.g. PII, PCI-DSS) to the pipeline.
-- **Global Configuration (JSON)** -- Advanced Vector configuration such as enrichment tables.
-- **Health SLIs** -- Define service-level indicators to monitor pipeline health.
-
-{% hint style="info" %}
-The metadata enrichment transform is invisible in the editor canvas. It is only injected into the generated Vector config at deploy time, so you will see it in the deploy preview diff but not as a node on the canvas.
-{% endhint %}
-
-### Deploy and undeploy
-
-The right side of the toolbar shows the current deployment state:
-
-- **Deploy** button -- Appears when the pipeline has never been deployed, or when changes have been made since the last deployment. Clicking Deploy first auto-saves, then opens the deploy dialog.
-- **Deployed** indicator -- A green checkmark shown when the deployed configuration matches the saved state.
-- **Undeploy** button -- Stops the pipeline on all agents and reverts it to draft status. You can redeploy at any time.
-- **Process status** -- A colored dot and label showing the runtime status: Running (green), Starting (yellow), Stopped (gray), or Crashed (red).
-
-## Pipeline logs panel
-
-Toggle the logs panel from the toolbar to view real-time logs from the running pipeline. The panel supports:
-
-- **Level filtering** -- Toggle visibility of ERROR, WARN, INFO, DEBUG, and TRACE log levels using the badges in the toolbar.
-- **Search** -- Type in the search box to filter loaded log lines by keyword. Matching text is highlighted in yellow. The line counter updates to show how many lines match (e.g., "42/318 lines").
-- **Node filtering** -- When viewing logs for a pipeline running on multiple nodes, filter by specific agent node.
-- **Pagination** -- Scroll to the top to automatically load older log entries, or click **Load older** to fetch the next page.
-
-{% hint style="info" %}
-Search filters the logs that are already loaded in the browser. It does not query the server — to find older log entries, load more pages first, then search.
-{% endhint %}
-
-{% hint style="info" %}
-The logs panel only shows data for deployed pipelines. Draft pipelines have no running processes to produce logs.
-{% endhint %}
-
-## Live Tail
-
-Live Tail lets you sample real events flowing through any component in a deployed pipeline, directly from the detail panel. This is useful for verifying that data is being ingested, transformed, and routed as expected.
-
-### How to use Live Tail
-
-{% stepper %}
-
-{% step %}
-### Deploy the pipeline
-Live Tail requires a running pipeline. If the pipeline is still a draft, deploy it first using the **Deploy** button in the toolbar.
-{% endstep %}
-
-{% step %}
-### Select a component
-Click any node on the canvas to open the detail panel.
-{% endstep %}
-
-{% step %}
-### Switch to the Live Tail tab
-In the detail panel, click the **Live Tail** tab. This tab only appears for deployed pipelines.
-{% endstep %}
-
-{% step %}
-### Sample events
-Click **Sample 10 Events** to request a batch of live events from the selected component. The panel polls for results and displays them as they arrive.
-{% endstep %}
-
-{% endstepper %}
-
-Each sampled event appears as a collapsed row showing a JSON preview. Click any row to expand it and view the full event payload with formatted JSON.
-
-- Events are shown newest-first, and the panel retains up to 50 events across multiple sampling requests.
-- Each sampling request has a two-minute expiry. If no events are captured within that window, the request expires silently.
-- You can sample from any component type -- sources, transforms, or sinks.
-
-{% hint style="info" %}
-Live Tail uses the agent's event sampling infrastructure. The agent captures a snapshot of events passing through the selected component and returns them on its next heartbeat. There is no persistent stream -- each click of "Sample 10 Events" triggers a new one-shot capture.
-{% endhint %}
-
-## Pipeline rename
-
-Click the pipeline name in the top-left corner of the editor to rename it inline. Press Enter to confirm or Escape to cancel.
-
-## Keyboard shortcuts
-
-| Shortcut | Action |
-|----------|--------|
-| `Cmd+S` | Save pipeline |
-| `Cmd+Z` | Undo |
-| `Cmd+Shift+Z` | Redo |
-| `Cmd+C` | Copy selected node(s) |
-| `Cmd+V` | Paste copied node(s) |
-| `Cmd+D` | Duplicate selected node |
-| `Cmd+E` | Export configuration |
-| `Cmd+I` | Import configuration |
-| `Delete` / `Backspace` | Delete selected node or edge |
-
-{% hint style="info" %}
-On Windows and Linux, use `Ctrl` instead of `Cmd` for all keyboard shortcuts.
-{% endhint %}
-
-## Cross-Environment Promotion
-
-Promote a pipeline from one environment to another (e.g., dev to staging, staging to production) with built-in validation and approval workflow.
-
-### Promoting a Pipeline
-
-1. From the pipeline list, click the **...** menu on any pipeline and select **Promote to...**
-2. Select the **target environment** and optionally rename the pipeline
-3. VectorFlow validates that all secret references in the pipeline exist in the target environment
-4. Review the **substitution diff** showing what will change between environments
-5. Click **Confirm Promotion** to submit
-
-### Approval Workflow
-
-If the target environment has **Require Deploy Approval** enabled, the promotion creates a request that must be approved by an administrator before the pipeline appears in the target environment.
-
-If approval is not required, the pipeline is promoted immediately.
-
-### Secret Pre-flight Validation
-
-Before promotion proceeds, VectorFlow checks that every `SECRET[name]` reference in the source pipeline has a corresponding secret defined in the target environment. If any secrets are missing, promotion is blocked with a clear list of which secrets need to be created.
-
-### Promotion History
-
-Each pipeline's detail page shows a promotion history log with source environment, target environment, who promoted, and the current status.
-
-## AI-Powered Suggestions
-
-When AI is configured for your team (Settings → AI), two AI features become available:
-
-### VRL Assistant
-In the VRL editor, click the **AI** button in the tools panel. Type a natural language description of what you want the VRL code to do, and the AI will generate VRL code. You can **Insert** (append) or **Replace** the current code.
-
-### Pipeline Builder
-In the pipeline editor toolbar, click the **sparkle icon** to open the AI Pipeline Builder. Two modes are available:
-
-- **Generate**: Describe a pipeline in plain language (e.g., "Collect K8s logs, drop debug, send to Datadog"). The AI generates Vector YAML config that is applied directly to your canvas.
-- **Review**: Ask the AI to review your current pipeline configuration for performance, correctness, and best practices.
-
-### Configuration
-Team admins can configure AI in **Settings → AI**. VectorFlow supports any OpenAI-compatible API (OpenAI, Anthropic, Ollama, Groq, Together, etc.).
diff --git a/docs/public/user-guide/pipelines.md b/docs/public/user-guide/pipelines.md
deleted file mode 100644
index 25ee94c4..00000000
--- a/docs/public/user-guide/pipelines.md
+++ /dev/null
@@ -1,227 +0,0 @@
-# Pipelines
-
-The **Pipelines** page lists all pipelines in the currently selected environment. From here you can create, clone, promote, and delete pipelines, as well as monitor their live throughput at a glance.
-
-
-
-## Pipeline list
-
-Pipelines are displayed in a table with the following columns:
-
-| Column | Description |
-|--------|------------|
-| **Name** | The pipeline name. Click it to open the pipeline in the editor. |
-| **Status** | Current lifecycle state (see statuses below). |
-| **Health** | SLI health badge -- green **Healthy**, yellow **Degraded**, or gray **No SLIs** (see [Pipeline Health SLIs](#pipeline-health-slis) below). |
-| **Events/sec In** | Live event ingestion rate polled from the agent fleet. |
-| **Bytes/sec In** | Live byte ingestion rate. |
-| **Reduction** | Percentage of events reduced by transforms, color-coded green (>50%), amber (>10%), or neutral. |
-| **Created** | Date and avatar of the user who created the pipeline. |
-| **Last Updated** | Date and avatar of the user who last modified the pipeline. |
-
-## Pipeline statuses
-
-A pipeline moves through several states during its lifecycle:
-
-- **Draft** -- The pipeline has been created but never deployed. It exists only as a saved configuration.
-- **Running** -- The pipeline is deployed and actively processing events on at least one agent node.
-- **Starting** -- The pipeline was recently deployed or restarted and agents are bringing it online.
-- **Stopped** -- The pipeline is deployed but all agent nodes have stopped processing it.
-- **Crashed** -- One or more agent nodes report that the pipeline has crashed. Check the pipeline logs for details.
-- **Pending deploy** -- Shown as an additional badge when the saved configuration differs from what is currently deployed. Deploy the pipeline to push the latest changes.
-- **Updates available** -- Shown as an amber badge when the pipeline contains nodes linked to [shared components](shared-components.md) that have been updated. Hover over the badge to see which shared components have pending updates. Open the pipeline editor to review and accept the changes.
-- **Pending Approval** -- Shown when an editor has submitted a deploy request that is waiting for admin approval. See [Deploy approval workflows](#deploy-approval-workflows).
-
-## Creating a pipeline
-
-{% stepper %}
-{% step %}
-### Click New Pipeline
-Click the **New Pipeline** button in the top-right corner. This navigates you to a new, empty pipeline in the editor.
-{% endstep %}
-{% step %}
-### Name your pipeline
-Give the pipeline a descriptive name. Names must start with a letter or number and can contain letters, numbers, spaces, hyphens, and underscores (up to 100 characters).
-{% endstep %}
-{% step %}
-### Build and save
-Add sources, transforms, and sinks in the pipeline editor. Save your work -- the pipeline starts as a **Draft** until you deploy it.
-{% endstep %}
-{% endstepper %}
-
-## Pipeline actions
-
-Each pipeline row has an actions menu (the three-dot icon on the right) with the following options:
-
-- **Metrics** -- Opens the dedicated metrics page for the pipeline, showing detailed throughput and error charts.
-- **Clone** -- Creates a copy of the pipeline (with " (Copy)" appended to the name) in the same environment and opens it in the editor.
-- **Promote to...** -- Copies the pipeline to a different environment within the same team. This is useful for promoting a pipeline from development to staging or production. Secrets and certificates are stripped during promotion and must be re-configured in the target environment.
-- **Delete** -- Permanently deletes the pipeline and all of its versions.
-
-{% hint style="danger" %}
-Deleting a deployed pipeline will automatically **undeploy** it from all agents before deletion. This means running agents will stop processing the pipeline on their next configuration poll. This action cannot be undone.
-{% endhint %}
-
-## Versioning
-
-Every time you deploy a pipeline, a new **version** is created that captures the full configuration YAML and a changelog entry. Versions let you:
-
-- **View history** -- See a list of all previously deployed versions with timestamps and the user who deployed them.
-- **Compare changes** -- View diffs between any two versions to understand what changed.
-- **Rollback** -- Restore a previous version if a deployment causes issues.
-
-The pipeline list shows a **Pending deploy** badge when the saved configuration differs from the most recently deployed version, so you always know if there are undeployed changes.
-
-## Pipeline Health SLIs
-
-Service Level Indicators (SLIs) let you define health thresholds for your deployed pipelines. When SLIs are configured, VectorFlow continuously evaluates pipeline metrics against your thresholds and displays the result as a health badge in the pipeline list and pipeline editor toolbar.
-
-### Health badges
-
-| Badge | Meaning |
-|-------|---------|
-| **Healthy** (green) | All configured SLIs are within their thresholds. |
-| **Degraded** (yellow) | One or more SLIs have breached their threshold. |
-| **No SLIs** (gray) | No SLI definitions have been configured for this pipeline. |
-
-Draft pipelines do not show a health badge since they are not deployed and have no metrics.
-
-### Available metrics
-
-| Metric | Description | Typical condition |
-|--------|-------------|-------------------|
-| **Error Rate** | Ratio of errors to total events ingested (`errorsTotal / eventsIn`). | `< 0.01` (less than 1% errors) |
-| **Discard Rate** | Ratio of discarded events to total events ingested (`eventsDiscarded / eventsIn`). | `< 0.05` (less than 5% discards) |
-| **Throughput Floor** | Events per second averaged over the evaluation window (`eventsIn / windowSeconds`). | `> 100` (at least 100 events/sec) |
-
-### Configuring SLIs
-
-{% stepper %}
-{% step %}
-### Open pipeline settings
-In the pipeline editor, click the **Settings** gear icon in the toolbar to open the settings popover.
-{% endstep %}
-{% step %}
-### Expand Health SLIs
-Click the **Health SLIs** collapsible section at the bottom of the settings panel.
-{% endstep %}
-{% step %}
-### Add an SLI
-Select a **Metric** (Error Rate, Throughput Floor, or Discard Rate), choose a **Condition** (less than or greater than), set a **Threshold** value, and configure the evaluation **Window** in minutes (1--1440). Click **Add SLI** to save.
-{% endstep %}
-{% step %}
-### Review and remove
-Existing SLIs are listed above the form. Click the trash icon to remove an SLI. Changes take effect immediately -- the pipeline list and toolbar health indicators update on the next evaluation cycle.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="info" %}
-Each metric can only have one SLI per pipeline. Adding an SLI for a metric that already has one will update the existing definition.
-{% endhint %}
-
-{% hint style="warning" %}
-If no metric data is available for the evaluation window (for example, the pipeline was recently deployed or has no traffic), the SLI is treated as **breached** and the pipeline health will show as **Degraded**.
-{% endhint %}
-
-## Compliance tags
-
-Compliance tags let you label pipelines with compliance or sensitivity markers such as **PII**, **PHI**, **PCI-DSS**, **Internal**, or **Public**. Tags appear as color-coded badges next to pipeline names in the list and help teams quickly identify which data-handling rules apply to each pipeline.
-
-### Defining available tags (Admin)
-
-Tags are defined at the **team** level. Only team admins can manage the list of available tags.
-
-{% stepper %}
-{% step %}
-### Open team settings
-Navigate to **Settings** and select the **Team** tab.
-{% endstep %}
-{% step %}
-### Add tags
-Scroll to the **Compliance Tags** card. Type a tag name (up to 30 characters) and click **Add**. Repeat for each tag you want to make available.
-{% endstep %}
-{% step %}
-### Remove tags
-Click the **X** button on any existing tag to remove it from the available list. Removing a tag from the team does not automatically remove it from pipelines that already have it applied.
-{% endstep %}
-{% endstepper %}
-
-### Applying tags to pipelines
-
-{% stepper %}
-{% step %}
-### Open pipeline settings
-Open a pipeline in the editor, then click the **gear icon** in the toolbar to open Pipeline Settings.
-{% endstep %}
-{% step %}
-### Select tags
-In the **Compliance Tags** section, use the dropdown to add tags from the team's available list. Tags are saved immediately.
-{% endstep %}
-{% step %}
-### Remove tags
-Click the **X** on any applied tag badge to remove it from the pipeline.
-{% endstep %}
-{% endstepper %}
-
-### Tag color coding
-
-Tags are color-coded in the pipeline list for quick visual identification:
-
-| Tag | Color |
-|-----|-------|
-| PII | Red |
-| PHI | Orange |
-| PCI-DSS | Purple |
-| Internal | Blue |
-| Public | Green |
-| Other | Gray |
-
-{% hint style="info" %}
-Tags are metadata labels only -- they do not enforce any access controls or data-handling policies. Use them as a visual aid for compliance awareness and pipeline organization.
-{% endhint %}
-
-
-## Deploy approval workflows
-
-Environments can optionally require **deploy approval** before a pipeline goes live. When enabled, editors who click **Deploy** will submit a deploy request instead of deploying directly. Approval and deployment are **separate actions** -- a reviewer approves the request, and then anyone with deploy access can execute the deployment.
-
-### How it works
-
-1. An admin enables **Require approval for deploys** on the environment settings page (see [Environments](environments.md#deploy-approval)).
-2. When an editor clicks **Deploy** in the pipeline editor, the deploy dialog shows a **Request Deploy** button instead of **Publish to Agents**.
-3. The editor submits a deploy request with a changelog entry. The pipeline list and pipeline editor toolbar show a **Pending Approval** badge.
-4. Another team member (editor or admin) opens the deploy dialog for the pipeline and sees the request in **review mode** -- displaying the requester, changelog, and a config diff.
-5. The reviewer clicks **Approve** to mark the request as approved. This does **not** deploy the pipeline.
-6. Once approved, any team member with deploy access can click **Deploy** on the approved request to push the configuration to agents.
-
-### Request lifecycle
-
-A deploy request moves through these statuses:
-
-| Status | Description |
-|--------|-------------|
-| **Pending** | The request is waiting for review. |
-| **Approved** | A reviewer has approved the request. It is ready to be deployed. |
-| **Deployed** | The approved request has been deployed to agents. |
-| **Rejected** | A reviewer has rejected the request (with an optional note). |
-| **Cancelled** | The request was cancelled before deployment. |
-
-{% hint style="warning" %}
-**Self-approval is blocked.** The person who submitted a deploy request cannot approve their own request. This enforces a four-eyes principle -- a second team member must always review the deployment.
-{% endhint %}
-
-{% hint style="info" %}
-Admins can always deploy directly, even when approval is required. When an admin deploys without going through the approval flow, a warning banner is shown to indicate the approval gate was bypassed. The approval gate only applies to users with the Editor role.
-{% endhint %}
-
-### Cancelling a request
-
-Anyone with deploy access can cancel a pending or approved deploy request. For pending requests, click the **X** button next to the **Pending Approval** badge in the pipeline editor toolbar. For approved requests, click **Cancel** in the deploy dialog.
-
-### Pipeline list indicators
-
-Pipelines with pending or approved deploy requests show a status badge (**Pending Approval** or **Approved**) in the status column on the Pipelines page, so team members can quickly identify which pipelines need attention.
-
-## Filtering by environment
-
-Pipelines are scoped to the currently selected **environment** (shown in the sidebar). Switch environments to view pipelines in a different environment. Each environment maintains its own independent set of pipelines, agent nodes, and secrets.
diff --git a/docs/public/user-guide/shared-components.md b/docs/public/user-guide/shared-components.md
deleted file mode 100644
index fe30ccd6..00000000
--- a/docs/public/user-guide/shared-components.md
+++ /dev/null
@@ -1,165 +0,0 @@
-# Shared Components
-
-Shared components are reusable, environment-scoped pipeline component configurations that can be linked into multiple pipelines. Edit a shared component in one place and every linked pipeline sees the update -- no more hunting through dozens of pipelines to change an endpoint.
-
-## How shared components work
-
-A shared component stores a canonical configuration for a specific component type (e.g., an Elasticsearch sink with your production cluster settings). When you link a shared component into a pipeline, the pipeline node receives a snapshot of that configuration and pins to a specific version.
-
-When someone edits the shared component, its version number increments. Linked pipelines don't change automatically -- instead, they surface an **Update available** indicator at three levels:
-
-- **Pipeline list** -- An amber "Updates available" badge on the pipeline row.
-- **Canvas** -- The linked node's footer changes to "Update available" with an amber dot.
-- **Detail panel** -- A banner with an "Accept update" button.
-
-Pipeline owners review and explicitly accept updates before redeploying. This review-first model prevents surprise breakage across production pipelines.
-
-{% hint style="info" %}
-Shared components are scoped to a single environment. A production Kafka config is separate from a staging one. If you need the same component in multiple environments, create a shared component in each.
-{% endhint %}
-
-## Finding shared components
-
-Shared components are managed in the **Library** page, accessible from the sidebar. The Library has two sections:
-
-- **Templates** -- Reusable pipeline blueprints (see [Templates](templates.md)).
-- **Shared Components** -- Reusable component configurations (this page).
-
-### Shared components list
-
-The list shows all shared components in the currently selected environment:
-
-| Column | Description |
-|--------|-------------|
-| **Name** | The shared component name. Click to open the detail page. |
-| **Type** | The Vector component type (e.g., `elasticsearch`, `kafka`). |
-| **Kind** | A badge indicating Source, Transform, or Sink. |
-| **Linked Pipelines** | Number of distinct pipelines using this shared component. |
-| **Version** | Current version number (increments on each config change). |
-| **Last Updated** | When the configuration was last modified. |
-
-## Creating a shared component
-
-There are two ways to create a shared component.
-
-### From the Library page
-
-{% stepper %}
-{% step %}
-### Open the Library
-Navigate to **Library > Shared Components** in the sidebar and click **New Shared Component**.
-{% endstep %}
-{% step %}
-### Choose a component type
-Select the component type from the catalog grid (e.g., Elasticsearch, Kafka, S3).
-{% endstep %}
-{% step %}
-### Configure
-Enter a **Name** and optional **Description**, then fill in the component configuration using the form editor. Click **Create** to save.
-{% endstep %}
-{% endstepper %}
-
-### From the pipeline editor
-
-{% stepper %}
-{% step %}
-### Right-click a node
-In the pipeline editor, right-click any configured node on the canvas.
-{% endstep %}
-{% step %}
-### Select Save as Shared Component
-Choose **Save as Shared Component** from the context menu. A dialog opens.
-{% endstep %}
-{% step %}
-### Name and save
-Enter a **Name** and optional **Description**, then click **Save**. The node's current configuration becomes the shared component, and the node is automatically linked to it.
-{% endstep %}
-{% endstepper %}
-
-## Using shared components in pipelines
-
-### Adding from the component palette
-
-The component palette (left sidebar in the pipeline editor) has two tabs:
-
-- **Catalog** -- The standard list of all Vector component types.
-- **Shared** -- Shared components available in the current environment.
-
-The Shared tab includes kind filter buttons (All, Source, Transform, Sink) and supports search by name or component type. Drag a shared component from the list onto the canvas to create a linked node with the shared configuration pre-filled.
-
-### Linked node appearance
-
-Linked nodes are visually distinct on the canvas:
-
-- **Purple accent border** -- A purple border and subtle glow distinguish linked nodes from regular ones.
-- **Footer** -- Shows "Shared" with a link icon.
-- **Stale indicator** -- When an update is available, the footer changes to "Update available" in amber, and a small amber dot appears on the node corner.
-
-### Detail panel for linked nodes
-
-When you select a linked node, the detail panel shows:
-
-- A **purple banner** with the shared component name and an "Open in Library" link.
-- **Read-only configuration** -- Config fields are disabled because the configuration is managed centrally. To edit, go to the Library page.
-- An **Unlink** button in the header to convert back to a regular editable node.
-
-{% hint style="warning" %}
-Unlinking a node preserves its current configuration but breaks the connection to the shared component. Future updates to the shared component will not reach this node.
-{% endhint %}
-
-## Editing a shared component
-
-{% stepper %}
-{% step %}
-### Open the detail page
-Navigate to **Library > Shared Components** and click the shared component you want to edit.
-{% endstep %}
-{% step %}
-### Modify the configuration
-Update the configuration using the form editor. You can also edit the name and description.
-{% endstep %}
-{% step %}
-### Save
-Click **Save**. The version number increments automatically, and all linked pipelines will see an "Update available" indicator.
-{% endstep %}
-{% endstepper %}
-
-The detail page also shows a **Linked Pipelines** section listing every pipeline that uses this shared component, with a badge indicating whether each is up to date or has a pending update.
-
-## Accepting updates
-
-When a shared component is updated, linked pipeline nodes become stale. Pipeline owners decide when to accept the new configuration.
-
-### Single node
-
-Select the stale node in the pipeline editor. The detail panel shows an amber banner with an **Accept update** button. Click it to pull in the latest configuration and update the pinned version.
-
-### All nodes in a pipeline
-
-If a pipeline has multiple stale linked nodes, you can accept all updates at once from the pipeline's toolbar or through the API.
-
-{% hint style="info" %}
-Accepting an update modifies the pipeline's saved configuration but does not automatically redeploy. You still need to deploy the pipeline to push the changes to agents.
-{% endhint %}
-
-## Deleting a shared component
-
-On the shared component detail page, scroll to the **Danger Zone** section and click **Delete**. A confirmation dialog explains what will happen:
-
-- All linked pipeline nodes are **unlinked** -- their configurations remain intact, but they become regular standalone nodes.
-- The shared component is permanently removed from the Library.
-
-{% hint style="danger" %}
-Deleting a shared component cannot be undone. Linked nodes keep their last-synced configuration but lose the ability to receive future updates.
-{% endhint %}
-
-## Edge cases
-
-| Scenario | Behavior |
-|----------|----------|
-| **Cloning a pipeline** | Linked nodes in the clone remain linked to the same shared components. |
-| **Promoting a pipeline** | Shared component links are stripped during cross-environment promotion. Nodes retain their config snapshots as regular standalone nodes. |
-| **Saving as template** | Templates are portable across environments, so shared component links are stripped. Template nodes store config snapshots only. |
-| **Discarding changes** | Reverting to a previous version restores the shared component links as they were at that version. |
-| **Copy/paste nodes** | Pasted nodes retain their link to the same shared component. |
-| **GitOps / YAML export** | Generated YAML uses the resolved config snapshot. Importing from YAML creates regular unlinked nodes. |
diff --git a/docs/public/user-guide/templates.md b/docs/public/user-guide/templates.md
deleted file mode 100644
index 49b72e4d..00000000
--- a/docs/public/user-guide/templates.md
+++ /dev/null
@@ -1,99 +0,0 @@
-# Templates
-
-Templates are reusable pipeline blueprints that capture a complete pipeline graph -- sources, transforms, sinks, their configurations, and how they connect. Save common patterns as templates so your team can spin up new pipelines in seconds without rebuilding from scratch.
-
-## Template library
-
-Templates are found in the **Library** page (accessible from the sidebar), under the **Templates** tab. The page displays all templates available to your team as a card grid. Each card shows:
-
-- **Name** -- The template name.
-- **Category** -- A label such as Logging, Metrics, Archival, Streaming, or a custom category you define.
-- **Description** -- A short summary of what the template does.
-- **Node and edge count** -- How many pipeline components and connections the template contains.
-
-{% hint style="info" %}
-Templates are shared across all environments within a team. Any team member with the Editor role or above can create and use templates.
-{% endhint %}
-
-## Saving a pipeline as a template
-
-You can save any pipeline from the pipeline editor as a reusable template.
-
-{% stepper %}
-{% step %}
-### Open a pipeline
-Navigate to the pipeline editor for the pipeline you want to save as a template.
-{% endstep %}
-{% step %}
-### Click Save as Template
-In the editor toolbar, click the **Save as Template** button. A dialog opens.
-{% endstep %}
-{% step %}
-### Fill in the details
-- **Name** -- A descriptive name (e.g., "Kafka to Elasticsearch").
-- **Description** -- Explain what the template does and when to use it.
-- **Category** -- Choose an existing category or type a custom one (e.g., "Logging", "Metrics", "Security").
-{% endstep %}
-{% step %}
-### Save
-Click **Save Template**. The template now appears in the template library for all team members.
-{% endstep %}
-{% endstepper %}
-
-## Creating a pipeline from a template
-
-{% stepper %}
-{% step %}
-### Open the Library
-Navigate to **Library > Templates** in the sidebar. Make sure you have an environment selected in the header.
-{% endstep %}
-{% step %}
-### Choose a template
-Browse the template cards and click **Use Template** on the one you want.
-{% endstep %}
-{% step %}
-### Customize
-VectorFlow creates a new pipeline in the current environment with the template's graph pre-loaded. The pipeline opens in the editor where you can rename it, adjust configurations, and wire in environment-specific settings like secrets.
-{% endstep %}
-{% endstepper %}
-
-## What templates include
-
-Templates save the complete pipeline graph:
-
-- **Nodes** -- Every source, transform, and sink component, including its component type, component key, display name, and configuration.
-- **Edges** -- All connections between components, preserving the data flow topology.
-- **Layout** -- The X/Y positions of nodes on the canvas, so the visual layout is preserved.
-
-## What templates do not include
-
-Templates are designed to be portable across environments, so they intentionally exclude:
-
-- **Secrets** -- Secret values (API keys, passwords, tokens) are never stored in templates. You must configure secrets in the target environment after creating a pipeline from a template.
-- **Certificates** -- TLS certificates are environment-specific and are not included.
-- **Environment bindings** -- Templates are not tied to any specific environment. They can be used in any environment within the team.
-- **Shared component links** -- Nodes that are linked to [shared components](shared-components.md) have their links stripped when saved as a template. The node's configuration snapshot is preserved, but the template node becomes a standalone component.
-- **Deployment state** -- Pipelines created from templates start as drafts. You deploy them when ready.
-
-{% hint style="warning" %}
-After creating a pipeline from a template, review the configuration and add any required secrets or environment-specific values before deploying.
-{% endhint %}
-
-## Managing templates
-
-- **Delete** -- Click the trash icon on a template card to permanently remove it. This does not affect pipelines that were already created from the template.
-- Templates cannot be edited after creation. To update a template, save the revised pipeline as a new template and delete the old one.
-
-## Template categories
-
-Templates are organized by category. Built-in categories include:
-
-| Category | Description |
-|----------|-------------|
-| **Getting Started** | Simple example pipelines for learning VectorFlow. |
-| **Logging** | Log collection, parsing, and routing patterns. |
-| **Archival** | Long-term storage and compliance pipelines. |
-| **Streaming** | Real-time event streaming configurations. |
-| **Metrics** | Metric collection and aggregation patterns. |
-
-You can also create custom categories by typing any name in the Category field when saving a template.
diff --git a/docs/public/user-guide/vrl-snippets.md b/docs/public/user-guide/vrl-snippets.md
deleted file mode 100644
index 48f84785..00000000
--- a/docs/public/user-guide/vrl-snippets.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# VRL Snippets
-
-## What is VRL?
-
-**VRL (Vector Remap Language)** is a purpose-built expression language for transforming observability data. It is the primary way to reshape, filter, and enrich events inside Vector pipelines. VRL is used in `remap`, `filter`, and `route` transforms.
-
-{% hint style="info" %}
-VRL compiles to native Rust and runs inside the Vector process, so transforms execute with minimal overhead -- typically microseconds per event. For the full language reference, see the [official VRL documentation](https://vector.dev/docs/reference/vrl/).
-{% endhint %}
-
-## Snippet library
-
-VectorFlow ships with a built-in library of reusable VRL snippets that cover common transformation patterns. You can also create your own **custom snippets** that are shared with your team.
-
-Snippets are accessible from the VRL editor inside the pipeline editor. When you are editing a `remap`, `filter`, or `route` transform, a snippet drawer is available that lets you search, browse, and insert snippets directly into your code.
-
-### Built-in categories
-
-The built-in snippet library is organized into the following categories:
-
-| Category | Examples |
-|----------|---------|
-| **Parsing** | `parse_json`, `parse_syslog`, `parse_csv`, `parse_key_value`, `parse_regex`, `parse_grok`, `parse_apache_log`, `parse_nginx_log` |
-| **Filtering** | `del(.field)`, keep fields, `if/else` conditions, `abort` (drop event), `assert`, `compact` |
-| **Enrichment** | Set fields, rename fields, merge objects, add tags, set timestamp, `uuid_v4()` |
-| **Type Coercion** | `to_int`, `to_float`, `to_bool`, `to_string`, `to_timestamp` |
-| **Encoding** | `encode_json`, `encode_logfmt`, `encode_base64`, `decode_base64` |
-| **String** | `downcase`, `upcase`, `strip_whitespace`, `replace`, `contains`, `starts_with`, `split`, `join` |
-| **Timestamp** | `now()`, `format_timestamp`, `parse_timestamp`, `to_unix_timestamp` |
-| **Networking** | `ip_cidr_contains`, `parse_url`, `ip_to_ipv6`, `community_id` |
-
-### Inserting a snippet
-
-{% stepper %}
-{% step %}
-### Open the snippet drawer
-In the VRL editor, click the **snippets** toggle to open the snippet drawer below the code editor.
-{% endstep %}
-{% step %}
-### Browse or search
-Scroll through the categories or type in the search box to filter snippets by name, description, or code content.
-{% endstep %}
-{% step %}
-### Click to insert
-Click any snippet to insert its VRL code at the current cursor position in the editor. Adjust placeholder values (like field names) to match your data.
-{% endstep %}
-{% endstepper %}
-
-## Creating custom snippets
-
-Your team can create custom snippets that appear alongside the built-in library, tagged with a **Custom** badge.
-
-{% stepper %}
-{% step %}
-### Open the snippet form
-Click the **+** button in the snippet drawer header to open the creation form.
-{% endstep %}
-{% step %}
-### Fill in the details
-- **Name** -- A short, descriptive name (required, up to 100 characters).
-- **Description** -- An optional explanation of what the snippet does (up to 500 characters).
-- **Category** -- Choose from the built-in categories or select "Custom".
-- **Code** -- The VRL code to insert (required).
-{% endstep %}
-{% step %}
-### Save
-Click **Create** to save the snippet. It will immediately appear in the drawer for all team members.
-{% endstep %}
-{% endstepper %}
-
-Custom snippets can be **edited** or **deleted** by hovering over them in the drawer and clicking the pencil or trash icon. Only team members with Editor access or higher can create, edit, or delete custom snippets.
-
-## Testing VRL
-
-The VRL editor includes a built-in **test runner** that lets you execute your VRL code against sample data without deploying the pipeline.
-
-{% stepper %}
-{% step %}
-### Write your VRL code
-Enter your transform logic in the VRL editor.
-{% endstep %}
-{% step %}
-### Provide a sample event
-Enter a JSON object in the **input** panel. If you leave it blank, a default test event is used:
-```json
-{
- "message": "test event",
- "timestamp": "2026-01-01T00:00:00Z",
- "host": "localhost"
-}
-```
-{% endstep %}
-{% step %}
-### Run the test
-Click the **Run** button. VectorFlow executes your VRL program against the input using the `vector vrl` CLI and displays the transformed output or any error messages.
-{% endstep %}
-{% endstepper %}
-
-{% hint style="warning" %}
-VRL testing requires the `vector` binary to be installed on the VectorFlow server. If it is not available, the test runner will show an error with installation instructions.
-{% endhint %}
-
-### Live event sampling
-
-For deployed pipelines, you can also sample **live events** flowing through upstream sources and use them as test input. The VRL editor provides:
-
-- A **Sample** button that requests recent events from the running pipeline
-- Navigation controls to step through multiple sampled events
-- A **Fields** panel that shows the inferred schema of upstream source events, so you know which fields are available for transformation
-
-## Example snippets
-
-Here are a few commonly used VRL patterns:
-
-### Parse syslog messages
-
-```coffee
-. = merge!(., parse_syslog!(.message))
-```
-
-Parses a syslog-formatted `.message` field and merges the structured fields (timestamp, hostname, severity, etc.) into the top-level event.
-
-### Extract and rename fields
-
-```coffee
-.host = del(.hostname)
-.severity = del(.level)
-.service = "my-app"
-```
-
-Renames fields by moving values to new keys and sets a static field.
-
-### Redact sensitive data
-
-```coffee
-.message = redact(.message, filters: [r'\d{3}-\d{2}-\d{4}', r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'])
-```
-
-Replaces patterns matching US Social Security numbers and email addresses with `[REDACTED]`.
-
-### Drop debug-level events
-
-```coffee
-if .level == "debug" {
- abort
-}
-```
-
-Used in a `remap` transform with `drop_on_abort` enabled, this drops all events where the level is "debug", reducing volume before data reaches your sinks.
-
-### Conditional enrichment
-
-```coffee
-if starts_with(to_string(.path), "/api") {
- .is_api_request = true
- .team = "backend"
-} else {
- .is_api_request = false
- .team = "frontend"
-}
-```
-
-Adds metadata fields based on the request path, which can be used for routing or filtering downstream.
diff --git a/src/app/(dashboard)/layout.tsx b/src/app/(dashboard)/layout.tsx
index 941ef040..6e8201a6 100644
--- a/src/app/(dashboard)/layout.tsx
+++ b/src/app/(dashboard)/layout.tsx
@@ -111,7 +111,7 @@ export default function DashboardLayout({