# Custom Tagging

Custom tags allow you to attach metadata to captured connections based on process environment variables, container labels, or Kubernetes labels and annotations. This is especially useful for:

* **Multi-tenant environments**: Tag traffic by team, customer, or tenant
* **Environment identification**: Distinguish between production, staging, and development traffic
* **Cost allocation**: Track usage by department, project, or business unit
* **Service organization**: Identify traffic by service name, version, or component
* **Qplane filtering**: Filter and organize captured traffic in the Qplane UI by custom dimensions

Custom tags appear in the `tags` object of **Connection events** (event store) alongside default tags like `bin`, `host`, `ip`, `protocol`, and `strategy`. Tags are connection-level metadata and do not appear in HTTP transaction objects (object store).

## Configuration

Custom tags are defined in the `tags` section of your qtap configuration:

```yaml
tags:
  - key: team
    source: env
    location: TEAM
  - key: service
    source: container.label
    location: service
  - key: environment
    source: k8s.label
    location: env
```

### Field Reference

| Field      | Type   | Required | Description                                                                     |
| ---------- | ------ | -------- | ------------------------------------------------------------------------------- |
| `key`      | string | Yes      | The name of the tag as it will appear in the `tags` object                      |
| `source`   | string | Yes      | Where to extract the tag value from (see [Tag Sources](#tag-sources))           |
| `location` | string | Yes      | The specific environment variable name, label key, or annotation key to extract |

### Tag Sources

Qtap supports extracting tag values from multiple sources:

| Source            | Description                                     | Example                                                                       |
| ----------------- | ----------------------------------------------- | ----------------------------------------------------------------------------- |
| `env`             | Environment variable from the monitored process | <p><code>source: env</code><br><code>location: APP\_NAME</code></p>           |
| `container.label` | Docker container label                          | <p><code>source: container.label</code><br><code>location: service</code></p> |
| `k8s.label`       | Kubernetes pod label                            | <p><code>source: k8s.label</code><br><code>location: team</code></p>          |
| `k8s.annotation`  | Kubernetes pod annotation                       | <p><code>source: k8s.annotation</code><br><code>location: version</code></p>  |

## Examples

### Environment Variables

Extract tags from environment variables set on your applications:

```yaml
version: 2

services:
  event_stores:
    - type: stdout

tags:
  - key: team
    source: env
    location: TEAM
  - key: app
    source: env
    location: APP_NAME
  - key: environment
    source: env
    location: ENVIRONMENT

stacks:
  default_stack:
    plugins:
      - type: http_capture
        config:
          level: summary
          format: json

tap:
  direction: egress
  ignore_loopback: true
  http:
    stack: default_stack
```

When you run your application with environment variables:

```bash
TEAM=platform APP_NAME=api-gateway ENVIRONMENT=production python ./app.py
```

The captured connections will include:

```json
{
  "tags": {
    "app": ["api-gateway"],
    "bin": ["python"],
    "environment": ["production"],
    "host": ["qpoint"],
    "ip": ["192.168.1.100"],
    "protocol": ["http2"],
    "strategy": ["observe"],
    "team": ["platform"]
  }
}
```

### Container Labels

Extract tags from Docker container labels:

```yaml
tags:
  - key: service
    source: container.label
    location: service
  - key: version
    source: container.label
    location: version
```

Run containers with labels:

```bash
docker run --label service=web-frontend --label version=v2.1.0 myapp:latest
```

Tags in captured connections:

```json
{
  "tags": {
    "service": ["web-frontend"],
    "version": ["v2.1.0"],
    "bin": ["node"],
    "host": ["qpoint"]
  }
}
```

### Kubernetes Labels

Extract tags from Kubernetes pod labels:

```yaml
tags:
  - key: team
    source: k8s.label
    location: team
  - key: app
    source: k8s.label
    location: app
```

Pod manifest with labels:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: api-server
    team: backend
spec:
  containers:
    - name: api
      image: myapp:latest
```

Tags in captured connections:

```json
{
  "tags": {
    "app": ["api-server"],
    "team": ["backend"],
    "bin": ["node"],
    "host": ["k8s-node-01"]
  }
}
```

### Kubernetes Annotations

Extract tags from Kubernetes pod annotations:

```yaml
tags:
  - key: version
    source: k8s.annotation
    location: app.version
  - key: owner
    source: k8s.annotation
    location: owner
```

Pod manifest with annotations:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-server
  annotations:
    app.version: "2.1.0"
    owner: "platform-team"
spec:
  containers:
    - name: api
      image: myapp:latest
```

### Combining Tag Sources

You can combine tags from multiple sources:

```yaml
tags:
  # From environment variables
  - key: environment
    source: env
    location: ENVIRONMENT

  # From container labels
  - key: service
    source: container.label
    location: service

  # From Kubernetes labels
  - key: team
    source: k8s.label
    location: team

  # From Kubernetes annotations
  - key: version
    source: k8s.annotation
    location: version
```

All tags from different sources are merged together in the Connection event.

## Static Tags (CLI Flag)

In addition to dynamic tags extracted from the YAML configuration, you can add static tags to all connections using the `--tags` CLI flag:

```bash
qtap --config=/path/to/qtap.yaml \
  --tags='region:us-west,deployment:production,cluster:main'
```

{% hint style="info" %}
**Format:** The `--tags` flag uses `key:value` pairs separated by commas (not `key=value`).
{% endhint %}

Static tags are useful for:

* Identifying the qtap instance or node
* Tagging by region, datacenter, or availability zone
* Marking deployment environment at the infrastructure level

Static tags and dynamic tags (from YAML) are merged together:

```json
{
  "tags": {
    "app": ["api-gateway"],          // from env var (YAML)
    "team": ["platform"],             // from env var (YAML)
    "region": ["us-west"],            // from CLI --tags
    "deployment": ["production"],     // from CLI --tags
    "cluster": ["main"],              // from CLI --tags
    "bin": ["python"],                // default
    "host": ["qpoint"],               // default
    "protocol": ["http2"],            // default
    "strategy": ["observe"]           // default
  }
}
```

## Tag Behavior

### Missing Values

If a tag's source value doesn't exist, the tag is simply omitted from the output (no errors are logged):

```yaml
tags:
  - key: team
    source: env
    location: TEAM  # If TEAM env var not set, tag won't appear
```

If the `TEAM` environment variable is not set on the process, the `team` tag won't appear in the Connection event.

### Tag Merging

When the same tag key is defined in multiple sources, **all values are merged into an array**:

**Example with environment variable and CLI flag:**

```yaml
tags:
  - key: team
    source: env
    location: TEAM
```

```bash
TEAM=platform-yaml ./my-app
```

```bash
qtap --tags='team:platform-cli' ...
```

**Result:**

```json
{
  "tags": {
    "team": ["platform-yaml", "platform-cli"]
  }
}
```

This applies to all tag sources:

* YAML-defined tags (env, container.label, k8s.label, k8s.annotation)
* CLI `--tags` flag
* Default qtap tags (bin, host, ip, protocol, strategy)

Even default tags can be extended:

```bash
qtap --tags='bin:my-custom-bin,host:my-custom-host' ...
```

**Result:**

```json
{
  "tags": {
    "bin": ["curl", "my-custom-bin"],
    "host": ["qpoint", "my-custom-host"]
  }
}
```

All tag values from all sources are preserved in the array.

## Use Cases

### Multi-Tenant SaaS Platform

Tag traffic by tenant and tier for cost allocation:

```yaml
tags:
  - key: tenant_id
    source: env
    location: TENANT_ID
  - key: tier
    source: env
    location: SERVICE_TIER
```

Run application:

```bash
TENANT_ID=customer-123 SERVICE_TIER=premium python ./app.py
```

### Microservices Environment

Identify traffic by service, version, and team:

```yaml
tags:
  - key: service
    source: k8s.label
    location: app
  - key: version
    source: k8s.annotation
    location: version
  - key: team
    source: k8s.label
    location: team
```

### Cost Tracking by Department

Tag traffic for cost allocation across departments:

```yaml
tags:
  - key: department
    source: env
    location: DEPARTMENT
  - key: cost_center
    source: env
    location: COST_CENTER
  - key: project
    source: env
    location: PROJECT_ID
```

Run application:

```bash
DEPARTMENT=engineering COST_CENTER=R001 PROJECT_ID=api-v2 ./my-app
```

### Environment-Specific Tagging

Distinguish between production, staging, and development:

```yaml
tags:
  - key: environment
    source: env
    location: ENV
  - key: region
    source: env
    location: AWS_REGION
```

Run in production:

```bash
ENV=production AWS_REGION=us-west-2 ./my-app
```

Run in staging:

```bash
ENV=staging AWS_REGION=us-east-1 ./my-app
```

## Viewing Tags in Qplane

When qtap is connected to Qplane, custom tags appear in the Qplane UI and can be used for:

* **Filtering connections**: Filter by team, environment, service, etc.
* **Grouping traffic**: Group connections by custom dimensions
* **Creating dashboards**: Visualize traffic patterns by custom tags
* **Setting alerts**: Alert on traffic patterns for specific tags

Custom tags make it easy to organize and filter large volumes of captured traffic across multiple teams, services, and environments.

## Best Practices

### Security Considerations

{% hint style="warning" %}
**Avoid tagging sensitive data**: Don't extract environment variables or labels that contain secrets, credentials, or personally identifiable information (PII).
{% endhint %}

```yaml
# AVOID - these could contain sensitive data
tags:
  - key: api_key
    source: env
    location: API_KEY        # ❌ Never tag secrets

  - key: user_email
    source: env
    location: USER_EMAIL     # ❌ Avoid PII

# Good - safe metadata
tags:
  - key: service
    source: env
    location: SERVICE_NAME   # ✅ Safe

  - key: team
    source: env
    location: TEAM           # ✅ Safe
```

### Use Static Tags for Infrastructure

Use the `--tags` CLI flag for infrastructure-level tags that don't change per process:

```bash
# Tag by region and cluster
qtap --tags='region:us-west-2,cluster:production,zone:a'

# Tag by datacenter and rack
qtap --tags='datacenter:dc1,rack:r42'
```

Use YAML tags for application-level tags that vary per process:

```yaml
# Tag by application attributes
tags:
  - key: service
    source: env
    location: SERVICE_NAME
  - key: version
    source: env
    location: APP_VERSION
```

## Complete Example

Here's a complete configuration combining multiple tag sources:

```yaml
version: 2

services:
  event_stores:
    - type: s3
      config:
        bucket: my-qtap-events
        region: us-west-2
  object_stores:
    - type: s3
      config:
        bucket: my-qtap-objects
        region: us-west-2

tags:
  # Application-level tags from environment
  - key: service
    source: env
    location: SERVICE_NAME
  - key: version
    source: env
    location: APP_VERSION
  - key: environment
    source: env
    location: ENVIRONMENT

  # Team ownership from Kubernetes
  - key: team
    source: k8s.label
    location: team
  - key: cost_center
    source: k8s.annotation
    location: cost-center

stacks:
  default_stack:
    plugins:
      - type: http_capture
        config:
          level: summary
          format: json

tap:
  direction: egress
  ignore_loopback: true
  audit_include_dns: false
  http:
    stack: default_stack
```

Start qtap with static infrastructure tags:

```bash
qtap --config=/etc/qtap/config.yaml \
  --tags='region:us-west-2,cluster:production'
```

Run your application with environment variables:

```bash
SERVICE_NAME=api-gateway \
APP_VERSION=2.1.0 \
ENVIRONMENT=production \
python ./app.py
```

The resulting Connection events will include all tags:

```json
{
  "meta": {
    "connectionId": "conn-123",
    "endpointId": "api.example.com"
  },
  "tags": {
    "service": ["api-gateway"],
    "version": ["2.1.0"],
    "environment": ["production"],
    "team": ["platform"],
    "cost_center": ["engineering-001"],
    "region": ["us-west-2"],
    "cluster": ["production"],
    "bin": ["python"],
    "host": ["k8s-node-01"],
    "ip": ["10.0.1.50"],
    "protocol": ["http2"],
    "strategy": ["observe"]
  },
  "timestamp": "2025-11-03T18:30:00Z",
  "direction": "egress-external",
  "source": {
    "exe": "/usr/bin/python3",
    "address": {
      "ip": "10.0.1.50",
      "port": 45678
    }
  },
  "destination": {
    "address": {
      "ip": "52.1.2.3",
      "port": 443
    }
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.qpoint.io/getting-started/qtap/configuration/custom-tagging.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
