This is the full developer documentation for Kopai
# Observability, Refactored
> Observability has been locked behind expensive vendors and specialized teams. With coding agents closing the loop, it's time to refactor the workflow.
For years, observability was a specialization in the domain of SREs and DevOps in big IT organizations. It required a special skillset that your common developer lacked. The position was reserved for the most experienced engineers in the company. It was considered unreasonable to expect developers to learn how to instrument their code manually while staying productive in their domains. Instead, the approach taken was to make it someone else’s problem, for a price.
The observability industry made sure that the code was auto-instrumented with magical agents that the observability engineers would set up. The community created OpenTelemetry as an open standard to reduce vendor lock-in. Vendors adopted it, but the instrumentation process remained complex. This made the process of instrumenting systems prohibitively expensive for most. To use their services you have to wait until your company is big enough to afford the observability software and hire the experts.
If you are small, you have to make do with whatever your cloud provider gave you. This usually meant sifting through your logs manually and becoming really good at eyeballing for problems in the logs.
There is a better way, and this problem is not insurmountable, we just need to do a bit of refactoring and remove the bloat.
## Code instrumentation with AI agents
[Section titled “Code instrumentation with AI agents”](#code-instrumentation-with-ai-agents)
With the advent of AI coding agents (Claude Code, Codex etc.), producing code for common tasks is commoditized. Doing a task that someone already did publicly on the internet can be performed easily by our autocomplete genies. For that to work for observability instrumentation, we need to close the agentic loop with a verification step.
```plaintext
Gather Context → Take Action → Verify Results
▲ │
└──────────────────────────────┘
For coding: read code write code run tests ✓
For observability: read code instrument ???
```
The missing piece is a local observability backend and a tool to query it.
Once the loop is closed, the stage is set for the instrumentation magic to shift from observability vendors’ proprietary code to AI-generated code you own. The magic act is done only once in your codebase, not at runtime. As a code-reviewer you are in complete control of what runs in production.
```plaintext
Before: After:
runtime agent ──┐ AI agent [o_o] ──┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ your code │ │ your code │
│ │ │ │
│ ┌────────────────┐ │ │ import { trace } │
│ │ /\_/\ │ │ │ const span = │
│ │ ( o.o ) magic │ │ │ trace.getTracer() │
│ │ > ~ < │ │ │ │
│ └────────────────┘ │ │ │
└──────────────────────┘ └──────────────────────┘
opaque, per-deploy visible, committed once
```
## Agents, not dashboards
[Section titled “Agents, not dashboards”](#agents-not-dashboards)
Once code is instrumented, the next problem is finding out what to look for and what to monitor. The observability experts have a common set of things to look at and this is usually the part where they come to developers for some insights about metrics to follow.
As a developer the things you want to inspect are unknown unknowns because that’s where your bugs live. So the usual answer is “let me get back to you on this”.
Organizations end up with a generic list of metrics which have less to do with the application code and more with the underlying OS, runtime, and networking stack: HTTP request time, CPU, memory etc. Observability team sets up alerts and dashboards on the metrics in vendor-provided dashboard. Unfortunately, there is additional price to this convenient division of labour. As always, the problem is communication and responsibility and the desired result of achieving observability ends up falling through the cracks.
The observability expert is probably not a part of your usual dev team so what you can expect is a generic dashboard that will satisfy the management, not the people who are supposed to use it to maintain the systems.
If the dashboard is not useful to the devs, they resort back to troubleshooting their systems with logs on their cloud provider. The devs started going around this problem and asking their AI coding agents to help with root cause analysis. Observability vendors noticed, so they started creating their own agents as part of their observability platforms.
```plaintext
┌─────────────────────────────┐
│ Observability Platform │
│ │
│ data ──► dashboard │
│ │ │
│ vendor agent │
│ │ │
│ response │
└──────────────┼──────────────┘
▼
Developer
```
Their agents work great with observability data, but cannot easily connect it with any extra context outside of their domain. Your workflow is limited by the way your observability vendor imagined.
For an optimal agentic workflow it would be much more convenient to shift the things around. It makes more sense for the end-user to use their own agent of choice to query the observability data, using a vendor-provided tool. Then the agent is able to join this data with other relevant sources such as source-control or billing systems to get the full context. That context can then immediately be used to fix the problem within the same session.
```plaintext
┌─────────────────┐
│ source control │
└────────┬────────┘
│
┌─────────────────────────┐ ▼
│ Observability Backend │ Agent ──► Developer
│ │ ▲
│ data ──► vendor tool ──┼──────┘
│ │ ▲
└─────────────────────────┘ │
┌────────┴────────┐
│ billing │
└─────────────────┘
```
Instead of being forced to work within the confines of observability vendor’s platform, you should be able to use whatever works for you.
## Kopai
[Section titled “Kopai”](#kopai)
In 2026, developers are capable of taking on a wider range of tasks which previously required specific expertise. By using AI coding agents, well known problems that have well known and documented solutions became much easier to tackle. All we need is a way for the agents to get the immediate feedback about the outcome of their actions. Observability is no different.
This was the motivation for creating Kopai (*to dig, with AI*).
Kopai provides the missing pieces for this workflow: [@kopai/app](https://www.npmjs.com/package/@kopai/app) (the observability backend) and [@kopai/cli](https://www.npmjs.com/package/@kopai/cli) (the AI-friendly CLI). They are **free and [open source](https://github.com/kopai-app/kopai-mono)**, based on [OpenTelemetry](https://opentelemetry.io/).
### Refactored workflows
[Section titled “Refactored workflows”](#refactored-workflows)
#### Application instrumentation
[Section titled “Application instrumentation”](#application-instrumentation)
1. Ask the agent to instrument your code using OpenTelemetry SDK. Use our skill or point the agent to kopai [documentation](/docs/manual-setup/#step-2-instrument-your-application) on instrumenting apps in your programming language
2. agent instruments the code and runs the app. App sends telemetry to `@kopai/app` running locally
3. agent uses `@kopai/cli` to query `@kopai/app` and validate the telemetry that was received from the instrumented code and contains the desired level of information
4. agent presents the results to the developer who uses `@kopai/app` dashboard to review the telemetry
#### Root cause analysis:
[Section titled “Root cause analysis:”](#root-cause-analysis)
1. Describe the problem to your coding agent and instruct it to use `@kopai/cli` to look for relevant observability data
2. agent gathers relevant context by using the kopai cli to query logs, traces, and metrics to inspect (supported by our root-cause-analysis skill)
3. agent presents the facts to the developer and generates a root cause analysis
4. developer uses `@kopai/app` dashboard to check the facts and review if the analysis makes sense
In both of these workflows the developer is always in control and is making the final call.
Observability is no longer someone else’s problem. With these tools and the ones we’ll release soon, you are empowered to own it. If you are a dev team which needs every bit of edge your AI-native approach can give to stay ahead, you will dig Kopai.
[Star on GitHub](https://github.com/kopai-app/kopai-mono) · [Read the Quickstart](/docs/quickstart/)
# Authentication
> Set up API tokens for Kopai
Learn how to authenticate your applications with Kopai.
## API Tokens
[Section titled “API Tokens”](#api-tokens)
Kopai uses bearer tokens for API authentication. All OTLP requests must include your token in the Authorization header.
## Creating a Token
[Section titled “Creating a Token”](#creating-a-token)
1. Navigate to **Settings** → **Access Tokens**
2. Click **Generate Token** and choose a token type
3. Give your token a descriptive name
4. Copy the token immediately (it won’t be shown again)
## Using Your Token
[Section titled “Using Your Token”](#using-your-token)
Include your token in the `Authorization` header:
```bash
Authorization: Bearer YOUR_TOKEN
```
Or set via environment variable:
```bash
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
```
## Token Best Practices
[Section titled “Token Best Practices”](#token-best-practices)
* Use separate tokens for different environments (dev, staging, prod)
* Rotate tokens regularly
* Never commit tokens to source control
* Revoke unused tokens promptly
## CLI Authentication
[Section titled “CLI Authentication”](#cli-authentication)
To query traces, logs, and metrics from the terminal, create a read-only CLI token.
### Create a CLI Token
[Section titled “Create a CLI Token”](#create-a-cli-token)
1. Navigate to **Settings** → **Access Tokens**
2. Click **Generate Token** and choose **CLI Token**
3. Give your token a descriptive name
4. Copy the token immediately (it won’t be shown again)
Note
CLI tokens are read-only. They can query traces, logs, and metrics but cannot write telemetry data. They expire after 90 days by default.
### Login
[Section titled “Login”](#login)
* npm
```bash
npx @kopai/cli login
```
* pnpm
```bash
pnpm dlx @kopai/cli login
```
* yarn
```bash
yarn dlx @kopai/cli login
```
Paste your CLI token at the prompt. The token is saved to `.kopairc` in the current directory.
| Flag | Description |
| ----------------- | ---------------------------------------------------------------------------------- |
| `--url ` | Set the Kopai server URL saved to `.kopairc` (default: `https://api.kopai.app/v2`) |
| `--global` | Save to `~/.kopairc` instead of `./.kopairc` |
| `--config ` | Use a custom config file path |
If `.kopairc` is not in your `.gitignore`, the CLI will warn you.
### Logout
[Section titled “Logout”](#logout)
* npm
```bash
npx @kopai/cli logout
```
* pnpm
```bash
pnpm dlx @kopai/cli logout
```
* yarn
```bash
yarn dlx @kopai/cli logout
```
Removes the token from `.kopairc`. Other config fields (like `url`) are preserved.
| Flag | Description |
| ----------------- | ------------------------------ |
| `--global` | Remove token from `~/.kopairc` |
| `--config ` | Use a custom config file path |
### Verify Authentication
[Section titled “Verify Authentication”](#verify-authentication)
* npm
```bash
npx @kopai/cli whoami
```
* pnpm
```bash
pnpm dlx @kopai/cli whoami
```
* yarn
```bash
yarn dlx @kopai/cli whoami
```
Displays your token prefix and server URL. Validates the token against the server if reachable.
### Config File
[Section titled “Config File”](#config-file)
The CLI stores credentials in `.kopairc`:
```json
{
"token": "kpi_...",
"url": "https://api.kopai.app/v2"
}
```
Resolution order: `--config` flag → `./.kopairc` → `~/.kopairc`
### Token Expiry
[Section titled “Token Expiry”](#token-expiry)
CLI tokens expire after 90 days. When your token expires:
1. Create a new CLI token in the dashboard (**Settings** → **Access Tokens**)
2. Run `npx @kopai/cli login` and paste the new token
# Integrations
> Connect any stack to Kopai with OpenTelemetry. Guides for 11 languages, 3 frameworks and 2 log sources, or let your agent set it up with the Kopai skills.
The fastest way to connect your app is to install the [Kopai skills](/docs/skills/) with `npx skills add kopai-app/kopai-mono` and ask your coding agent to add observability. It instruments your code with OpenTelemetry and confirms the data reaches Kopai, as the [Quickstart](/docs/quickstart/) shows. The guides below are the same setup by hand, and they also cover what the skills do not handle yet: browser JavaScript, Mastra, LangChain and infrastructure logs.
## Languages
[Section titled “Languages”](#languages)
[Node.js](/docs/integration/languages/node/)Node.js apps report traces and logs to Kopai through OpenTelemetry auto-instrumentation. Install the Kopai skills and your coding agent sets it up.
[Browser / React](/docs/integration/languages/browser/)Trace React, Vue, Angular or plain JavaScript in the browser with OpenTelemetry, using a Kopai frontend token that is safe to ship in your bundle.
[Python](/docs/integration/languages/python/)Instrument a Python service with OpenTelemetry and watch its traces and metrics arrive in Kopai. Your agent can do the setup through the Kopai skills.
[Go](/docs/integration/languages/go/)Send Go traces to Kopai with the OpenTelemetry SDK and its OTLP HTTP exporter. Let your coding agent wire it up for you, or follow the manual steps.
[Java](/docs/integration/languages/java/)Trace Java apps in Kopai with zero code changes using OpenTelemetry's auto-instrumentation jar, or let your coding agent set it up with the Kopai skills.
[.NET](/docs/integration/languages/dotnet/)Add OpenTelemetry to ASP.NET Core and export .NET traces, metrics and logs to Kopai. Wire it by hand, or leave it to your agent and the Kopai skills.
[Ruby](/docs/integration/languages/ruby/)Ruby apps ship traces to Kopai with the OpenTelemetry SDK gems and optional auto-instrumentation. With the Kopai skills, your coding agent adds them.
[PHP](/docs/integration/languages/php/)Connect a PHP app to Kopai with the OpenTelemetry SDK and OTLP exporter from Composer. Your coding agent sets it up once you add the Kopai skills.
[Rust](/docs/integration/languages/rust/)Export Rust traces to Kopai using the opentelemetry, opentelemetry\_sdk and opentelemetry-otlp crates. Ask your agent to do it, or follow this guide.
[C++](/docs/integration/languages/cpp/)Link OpenTelemetry into C++ apps with CMake or vcpkg and export spans to Kopai over OTLP HTTP. Your agent can handle the build setup via the Kopai skills.
[Erlang/Elixir](/docs/integration/languages/erlang/)Erlang and Elixir services send traces to Kopai through the OpenTelemetry SDK and OTLP exporter. The Kopai skills let your agent do the wiring.
## Frameworks
[Section titled “Frameworks”](#frameworks)
[Next.js](/docs/integration/frameworks/nextjs/)Get Next.js routes and server actions into Kopai with @vercel/otel or the full OpenTelemetry SDK. Or hand the job to your agent with the Kopai skills.
[Mastra](/docs/integration/frameworks/mastra/)See Mastra agent runs, LLM calls, tool executions and workflow steps in Kopai by adding the @mastra/otel-exporter package and pointing it at your endpoint.
[LangChain](/docs/integration/frameworks/langchain/)LangChain traces reach Kopai through LangSmith's OpenTelemetry export, with no SDK code and no LangSmith cloud. See chain runs, LLM calls and tools.
## Infrastructure logs
[Section titled “Infrastructure logs”](#infrastructure-logs)
[journald](/docs/integration/infrastructure-logs/journald/)Ship systemd journal logs to Kopai with the OpenTelemetry Collector's journald receiver. No code changes, no file tailing, no duplicates across restarts.
[rsyslog](/docs/integration/infrastructure-logs/rsyslog/)Forward syslog from legacy Linux services to Kopai through rsyslog and the OpenTelemetry Collector, without touching a line of the applications' code.
# LangChain
> LangChain traces reach Kopai through LangSmith's OpenTelemetry export, with no SDK code and no LangSmith cloud. See chain runs, LLM calls and tools.
Add observability to your [LangChain](https://www.langchain.com) application and see chain runs, LLM calls, and tool executions in Kopai. LangChain routes traces through [LangSmith’s OpenTelemetry integration](https://docs.langchain.com/langsmith/trace-with-opentelemetry) — the tracing layer built into LangSmith can export directly to any OTLP endpoint, so you do not need to instrument LangChain with the OpenTelemetry SDK yourself or run LangSmith’s cloud backend.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Python 3.11+
* API key from a model provider (optional — this example falls back to a fake chat model)
* Kopai running locally:
- npm
```bash
npx @kopai/app start
```
- pnpm
```bash
pnpm dlx @kopai/app start
```
- yarn
```bash
yarn dlx @kopai/app start
```
## Install dependencies
[Section titled “Install dependencies”](#install-dependencies)
Install LangChain, the `langsmith[otel]` extra, and the Python OpenTelemetry SDK:
```bash
pip install \
"langchain" \
"langchain-community" \
"langchain-openai" \
"langsmith[otel]" \
"opentelemetry-api" \
"opentelemetry-sdk" \
"opentelemetry-exporter-otlp-proto-http"
```
The `langsmith[otel]` extra pulls in the OpenTelemetry bridge that converts LangSmith runs into OTel spans.
## Configure LangSmith and OpenTelemetry
[Section titled “Configure LangSmith and OpenTelemetry”](#configure-langsmith-and-opentelemetry)
Create `app.py`. Before importing any LangChain module, set the LangSmith environment variables and initialize the OTel SDK so the exporter is ready when the first span is created:
```python
import os
# Route LangChain traces through LangSmith's OpenTelemetry bridge,
# bypassing the LangSmith cloud backend entirely.
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_OTEL_ENABLED"] = "true"
os.environ["LANGSMITH_OTEL_ONLY"] = "true"
os.environ.setdefault("LANGSMITH_API_KEY", "dummy")
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
resource = Resource.create({"service.name": "my-langchain-app"})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
SimpleSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces"))
)
trace.set_tracer_provider(provider)
```
The three `LANGSMITH_OTEL_*` variables are the non-obvious part:
* `LANGSMITH_TRACING=true` turns LangSmith tracing on
* `LANGSMITH_OTEL_ENABLED=true` enables the OTel bridge
* `LANGSMITH_OTEL_ONLY=true` skips the LangSmith cloud backend — traces go only to your OTel exporter
* `LANGSMITH_API_KEY` must be set to any non-empty string (LangSmith validates its presence even in OTel-only mode)
## Run a chain
[Section titled “Run a chain”](#run-a-chain)
Add a minimal chat loop at the bottom of `app.py`. This uses a fake chat model so you don’t need an API key, but you can swap it for `ChatOpenAI` whenever you want:
```python
from langchain_community.chat_models.fake import FakeListChatModel
from langchain_core.messages import HumanMessage, SystemMessage
llm = FakeListChatModel(responses=[
"Hello! How can I help you today?",
"That's an interesting question.",
])
messages = [
SystemMessage(content="You are a helpful assistant."),
HumanMessage(content="Tell me about OpenTelemetry."),
]
response = llm.invoke(messages)
print(response.content)
# LangSmith batches runs on its own background thread before handing
# them to the OTel exporter. Give it a moment to drain before shutdown,
# otherwise runs get dropped when the exporter closes.
import time
time.sleep(2)
provider.force_flush()
provider.shutdown()
```
To use a real model, set your provider key and swap the model:
```python
# export OPENAI_API_KEY=sk-...
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
```
## Run and verify locally
[Section titled “Run and verify locally”](#run-and-verify-locally)
The exporter in `app.py` points at `http://localhost:4318` — the OTLP endpoint exposed by the local Kopai instance you started with `npx @kopai/app start`. Run the script:
```bash
python app.py
```
LangSmith captures the chain run, converts it to OTel spans, and the SDK flushes them to your local Kopai on shutdown. Verify they arrived using the Kopai CLI:
* npm
```bash
# List recent traces from your app
npx @kopai/cli traces search --service my-langchain-app --json
# Inspect a specific trace (copy a traceId from above)
npx @kopai/cli traces get --json
```
* pnpm
```bash
# List recent traces from your app
pnpm dlx @kopai/cli traces search --service my-langchain-app --json
# Inspect a specific trace (copy a traceId from above)
pnpm dlx @kopai/cli traces get --json
```
* yarn
```bash
# List recent traces from your app
yarn dlx @kopai/cli traces search --service my-langchain-app --json
# Inspect a specific trace (copy a traceId from above)
yarn dlx @kopai/cli traces get --json
```
Each trace contains one span per LLM invocation, with the prompt and completion captured under the `gen_ai.prompt` and `gen_ai.completion` attributes. Wrap your calls in an LCEL chain (e.g. `prompt | llm | parser`) to produce multi-span traces with chain and tool spans as children.
## Sending to Kopai.app in the cloud
[Section titled “Sending to Kopai.app in the cloud”](#sending-to-kopaiapp-in-the-cloud)
Once it works locally, sending to Kopai Cloud is a one-line change. Go back to the `OTLPSpanExporter` you created in `app.py` and swap the local endpoint for the cloud endpoint plus an `Authorization` header:
```python
# Replace the local exporter with this:
OTLPSpanExporter(
endpoint="https://otlp-http.kopai.app/v1/traces",
headers={"Authorization": "Bearer YOUR_BACKEND_TOKEN"},
)
```
Nothing else changes — the LangSmith env vars, the `TracerProvider`, the chain code, and the verification commands all stay the same. Re-run `python app.py` and your traces flow to the cloud instead of localhost.
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example with an interactive chat REPL:
**[LangChain Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/langchain)**
# Mastra
> See Mastra agent runs, LLM calls, tool executions and workflow steps in Kopai by adding the @mastra/otel-exporter package and pointing it at your endpoint.
Add observability to your [Mastra](https://mastra.ai) AI agents using [Mastra’s OpenTelemetry exporter](https://mastra.ai/docs/observability/tracing/exporters/otel). Capture the full lifecycle of agent runs — from LLM calls and tool executions to workflow steps — and inspect them in Kopai.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Node.js 20+
* API key from a model provider (e.g. Anthropic, OpenAI)
* Kopai running locally:
- npm
```bash
npx @kopai/app start
```
- pnpm
```bash
pnpm dlx @kopai/app start
```
- yarn
```bash
yarn dlx @kopai/app start
```
## Create a Mastra Project
[Section titled “Create a Mastra Project”](#create-a-mastra-project)
Scaffold a new project with the Mastra CLI:
* npm
```bash
npx create-mastra@latest
```
* pnpm
```bash
pnpm create mastra@latest
```
* yarn
```bash
yarn create mastra
```
Set your model provider key in `.env`:
```bash
ANTHROPIC_API_KEY=
```
## Add an Agent
[Section titled “Add an Agent”](#add-an-agent)
Define an agent in `src/mastra/agents/travelAgent.ts`:
```typescript
import { anthropic } from "@ai-sdk/anthropic";
import { Agent } from "@mastra/core/agent";
export const travelAgent = new Agent({
name: "travel-agent",
instructions:
"You are a friendly travel assistant. " +
"Help users plan trips, suggest destinations, and answer questions about travel logistics.",
model: anthropic("claude-haiku-4-5-20251001"),
});
```
## Enable Observability
[Section titled “Enable Observability”](#enable-observability)
Install the OTel exporter and observability packages:
* npm
```bash
npm install @mastra/observability @mastra/otel-exporter @opentelemetry/exporter-trace-otlp-http
```
* pnpm
```bash
pnpm add @mastra/observability @mastra/otel-exporter @opentelemetry/exporter-trace-otlp-http
```
* yarn
```bash
yarn add @mastra/observability @mastra/otel-exporter @opentelemetry/exporter-trace-otlp-http
```
Register the agent and wire up telemetry in `src/mastra/index.ts`:
```typescript
import { Mastra } from "@mastra/core";
import { Observability } from "@mastra/observability";
import { OtelExporter } from "@mastra/otel-exporter";
import { travelAgent } from "./agents/travelAgent";
export const mastra = new Mastra({
agents: { travelAgent },
observability: new Observability({
configs: {
otel: {
serviceName: "my-mastra-app",
exporters: [
new OtelExporter({
provider: {
custom: {
endpoint: "http://localhost:4318/v1/traces",
},
},
}),
],
},
},
}),
});
```
This sends all agent and tool traces to Kopai’s local OTLP endpoint on port 4318.
## Run and Verify
[Section titled “Run and Verify”](#run-and-verify)
Start the Mastra dev server:
* npm
```bash
npm run dev
```
* pnpm
```bash
pnpm dev
```
* yarn
```bash
yarn dev
```
Open the playground at `http://localhost:4111` and send a few messages to your agent.
**Note:** Traces are sent to Kopai, not Mastra Studio’s built-in trace viewer. Use the Kopai CLI or dashboard at `http://localhost:8000` to inspect them.
Verify that traces arrived using the Kopai CLI:
* npm
```bash
# List recent traces from your agent
npx @kopai/cli traces search --service my-mastra-app --json
# Inspect a specific trace (copy a traceId from above)
npx @kopai/cli traces get --json
```
* pnpm
```bash
# List recent traces from your agent
pnpm dlx @kopai/cli traces search --service my-mastra-app --json
# Inspect a specific trace (copy a traceId from above)
pnpm dlx @kopai/cli traces get --json
```
* yarn
```bash
# List recent traces from your agent
yarn dlx @kopai/cli traces search --service my-mastra-app --json
# Inspect a specific trace (copy a traceId from above)
yarn dlx @kopai/cli traces get --json
```
Each trace contains spans for the full agent interaction — LLM calls, tool executions, and decision steps.
## Sending to Kopai.app in the cloud
[Section titled “Sending to Kopai.app in the cloud”](#sending-to-kopaiapp-in-the-cloud)
Swap the `OtelExporter` config in `src/mastra/index.ts` to point at `https://otlp-http.kopai.app` with an auth header:
```typescript
new OtelExporter({
provider: {
custom: {
endpoint: "https://otlp-http.kopai.app",
headers: {
authorization: "Bearer YOUR_BACKEND_TOKEN",
},
},
},
});
```
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example with traces, logs, and metrics:
**[Mastra Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/mastra)**
# Next.js
> Get Next.js routes and server actions into Kopai with @vercel/otel or the full OpenTelemetry SDK. Or hand the job to your agent with the Kopai skills.
Add observability to your [Next.js](https://nextjs.org) App Router application and see HTTP routes, server actions, and (optionally) browser page loads in Kopai. This guide follows the [official Next.js OpenTelemetry guide](https://nextjs.org/docs/app/guides/open-telemetry) and offers two paths:
* **[`@vercel/otel`](#option-1-vercelotel)** — the recommended default. Four lines of code. Covers server-side HTTP spans.
* **[Manual OpenTelemetry SDK](#option-2-manual-opentelemetry-sdk)** — when you need browser traces, end-to-end distributed tracing from client to server, or control over processors, exporters, and sampling.
Both approaches read the OTLP endpoint from the standard `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Node.js 22+
* A Next.js 15 App Router project (`npx create-next-app@latest`)
* Kopai running locally:
- npm
```bash
npx @kopai/app start
```
- pnpm
```bash
pnpm dlx @kopai/app start
```
- yarn
```bash
yarn dlx @kopai/app start
```
## Option 1: @vercel/otel
[Section titled “Option 1: @vercel/otel”](#option-1-vercelotel)
Install the package:
* npm
```bash
npm install @vercel/otel
```
* pnpm
```bash
pnpm add @vercel/otel
```
* yarn
```bash
yarn add @vercel/otel
```
Create `src/instrumentation.ts` at the root of your `src/` directory:
```typescript
import { registerOTel } from "@vercel/otel";
export function register() {
registerOTel({ serviceName: "my-nextjs-app" });
}
```
That’s it. Next.js automatically detects the `register()` hook and initializes OTel before serving the first request. You’ll get HTTP spans for every route and API handler.
Set the OTLP endpoint and start the dev server:
* npm
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
npm run dev
```
* pnpm
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
pnpm dev
```
* yarn
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
yarn dev
```
## Option 2: Manual OpenTelemetry SDK
[Section titled “Option 2: Manual OpenTelemetry SDK”](#option-2-manual-opentelemetry-sdk)
Use this when you need:
* Browser spans (document load, fetch calls)
* End-to-end distributed tracing — browser spans as parents of the server spans they trigger
* Control over span processors, exporters, or sampling
### Server-side
[Section titled “Server-side”](#server-side)
Install the Node SDK:
* npm
```bash
npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources @opentelemetry/sdk-trace-node @opentelemetry/semantic-conventions
```
* pnpm
```bash
pnpm add @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources @opentelemetry/sdk-trace-node @opentelemetry/semantic-conventions
```
* yarn
```bash
yarn add @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http \
@opentelemetry/resources @opentelemetry/sdk-trace-node @opentelemetry/semantic-conventions
```
Create `src/instrumentation.ts` with a runtime guard so the Node SDK is never imported into the Edge runtime:
```typescript
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./instrumentation.node");
}
}
```
Create `src/instrumentation.node.ts`:
```typescript
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-node";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: "my-nextjs-app-server",
}),
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
});
sdk.start();
process.on("SIGTERM", () => {
sdk.shutdown().catch(() => process.exit(0));
});
```
### Browser-side
[Section titled “Browser-side”](#browser-side)
Install the browser packages:
* npm
```bash
npm install @opentelemetry/sdk-trace-web @opentelemetry/sdk-trace-base \
@opentelemetry/context-zone @opentelemetry/instrumentation \
@opentelemetry/instrumentation-document-load @opentelemetry/instrumentation-fetch
```
* pnpm
```bash
pnpm add @opentelemetry/sdk-trace-web @opentelemetry/sdk-trace-base \
@opentelemetry/context-zone @opentelemetry/instrumentation \
@opentelemetry/instrumentation-document-load @opentelemetry/instrumentation-fetch
```
* yarn
```bash
yarn add @opentelemetry/sdk-trace-web @opentelemetry/sdk-trace-base \
@opentelemetry/context-zone @opentelemetry/instrumentation \
@opentelemetry/instrumentation-document-load @opentelemetry/instrumentation-fetch
```
The browser can’t POST directly to `http://localhost:4318` due to CORS. Add a same-origin proxy at `src/app/api/otel/route.ts`:
```typescript
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const endpoint =
process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
const body = await request.arrayBuffer();
const res = await fetch(`${endpoint}/v1/traces`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
return new NextResponse(null, { status: res.status });
}
```
Initialize the browser tracer in a client component at `src/app/otel-provider.tsx`:
```typescript
"use client";
import { useEffect, useRef } from "react";
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { ZoneContextManager } from "@opentelemetry/context-zone";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
export default function OtelProvider({
children,
}: {
children: React.ReactNode;
}) {
const initialized = useRef(false);
useEffect(() => {
if (initialized.current) return;
initialized.current = true;
const provider = new WebTracerProvider({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: "my-nextjs-app-browser",
}),
spanProcessors: [
new BatchSpanProcessor(new OTLPTraceExporter({ url: "/api/otel" })),
],
});
provider.register({ contextManager: new ZoneContextManager() });
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [/.*/],
ignoreUrls: [/\/api\/otel/],
}),
],
});
}, []);
return <>{children}>;
}
```
Wrap your root layout with it in `src/app/layout.tsx`:
```typescript
import OtelProvider from "./otel-provider";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
`FetchInstrumentation` injects `traceparent` headers into every outgoing fetch call, so the browser spans become parents of the server spans your API routes create — giving you a single trace spanning client and server.
## Verify
[Section titled “Verify”](#verify)
Start the app and generate traffic:
* npm
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
npm run dev
```
* pnpm
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
pnpm dev
```
* yarn
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
yarn dev
```
Open and click around. Then query the Kopai CLI:
* npm
```bash
# List recent traces
npx @kopai/cli traces search --json
# Inspect a specific trace (copy a traceId from above)
npx @kopai/cli traces get --json
```
* pnpm
```bash
# List recent traces
pnpm dlx @kopai/cli traces search --json
# Inspect a specific trace (copy a traceId from above)
pnpm dlx @kopai/cli traces get --json
```
* yarn
```bash
# List recent traces
yarn dlx @kopai/cli traces search --json
# Inspect a specific trace (copy a traceId from above)
yarn dlx @kopai/cli traces get --json
```
With the manual SDK setup, a single trace should contain both `my-nextjs-app-browser` and `my-nextjs-app-server` spans in a parent-child relationship.
## Sending to Kopai.app in the cloud
[Section titled “Sending to Kopai.app in the cloud”](#sending-to-kopaiapp-in-the-cloud)
Change the environment variables to point at the cloud endpoint with your API key:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_BACKEND_TOKEN"
```
No code changes needed — both approaches read these variables automatically. For the manual SDK setup, the `/api/otel` proxy route forwards requests from the browser to whatever endpoint it reads at runtime.
## Working Examples
[Section titled “Working Examples”](#working-examples)
Two complete runnable examples, one per approach:
* **[Next.js + @vercel/otel](https://github.com/kopai-app/kopai-integration-examples/tree/main/next-js/vercel-otel)** — minimal server-side setup
* **[Next.js + Manual OTel SDK](https://github.com/kopai-app/kopai-integration-examples/tree/main/next-js/manual-sdk)** — server + browser + distributed tracing
# journald
> Ship systemd journal logs to Kopai with the OpenTelemetry Collector's journald receiver. No code changes, no file tailing, no duplicates across restarts.
Ship logs from any [systemd](https://systemd.io) service on Linux to Kopai using the OpenTelemetry Collector’s [native journald receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/journaldreceiver). The collector reads binary journal entries directly — no file tailing, no parsing, no log rotation to manage. It picks up all journald metadata (`_SYSTEMD_UNIT`, `_BOOT_ID`, `_MACHINE_ID`, and dozens more) automatically, and cursor-based checkpointing guarantees no duplicates or loss across collector restarts.
Your application needs **no code changes**. If it already logs to stdout under a systemd unit, journald is already capturing it.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Linux with systemd
* [`otelcol-contrib`](https://github.com/open-telemetry/opentelemetry-collector-releases) (the contrib distribution includes the journald receiver)
* Kopai running locally:
- npm
```bash
npx @kopai/app start
```
- pnpm
```bash
pnpm dlx @kopai/app start
```
- yarn
```bash
yarn dlx @kopai/app start
```
## Journal access
[Section titled “Journal access”](#journal-access)
The `journald` receiver runs `journalctl` under the hood, so the process running the collector must be able to read `/var/log/journal`. If it can’t, the collector will start, log `Everything is ready`, and then silently ingest no records — there is no error message.
Pick one of the following before starting the collector:
* **Run the collector as root** (simplest for a one-off test):
```bash
sudo otelcol-contrib --config otel-collector/journald.yaml
```
* **Add your user to the `systemd-journal` group**, then log out and back in (group membership only takes effect in a new login session):
```bash
sudo usermod -aG systemd-journal "$USER"
# then log out and log back in, or start a new login shell
```
* **Run the collector as a systemd service** with `Group=systemd-journal` in its unit file — recommended for production.
To confirm the collector can see the journal before launching it, run this as the user that will run the collector:
```bash
journalctl -n 1 --no-pager
```
If you see `-- No entries --` or a “you are currently not seeing messages from other users” hint, the collector won’t see them either.
## Configure the collector
[Section titled “Configure the collector”](#configure-the-collector)
Point the `journald` receiver at your journal directory and filter by the systemd units you care about. Create `otel-collector/journald.yaml`:
```yaml
receivers:
journald:
directory: /var/log/journal
units:
- my-service.service
priority: info
operators:
- type: severity_parser
parse_from: body.PRIORITY
mapping:
error: "3"
warn: "4"
info: "6"
debug: "7"
- type: move
from: body.MESSAGE
to: body
processors:
batch: {}
resource:
attributes:
- key: service.name
value: "my-service"
action: insert
exporters:
otlphttp/kopai:
endpoint: http://localhost:4318
tls:
insecure: true
service:
pipelines:
logs:
receivers: [journald]
processors: [resource, batch]
exporters: [otlphttp/kopai]
```
Replace `my-service.service` with the systemd unit you actually want to ship to Kopai (e.g. `nginx.service`, `myapp.service`). To tail the entire journal instead of a single unit, omit the `units:` key entirely. The `severity_parser` maps journald `PRIORITY` values (3=error, 4=warning, 6=info, 7=debug) to OTel severity levels.
## Run the collector
[Section titled “Run the collector”](#run-the-collector)
```bash
otelcol-contrib --config otel-collector/journald.yaml
```
The collector uses journald’s native cursor mechanism to track its read position, so it resumes from the last entry after a restart — no duplicates, no gaps.
Note
You may see an error-level line at startup mentioning a journal file that “is truncated, ignoring file”, possibly with a stack trace. This comes from `journalctl` itself — it’s skipping a single corrupted journal file — and the receiver continues normally with the rest of the journal. You can safely ignore it as long as the collector reaches `Everything is ready`.
## Verify
[Section titled “Verify”](#verify)
Confirm logs arrived using the Kopai CLI:
* npm
```bash
# Recent entries with timestamps
npx @kopai/cli logs search --service my-service \
--fields Timestamp,Body --sort ASC
# Filter to errors only (severity-min 17 = ERROR)
npx @kopai/cli logs search --service my-service --severity-min 17 --json
```
* pnpm
```bash
# Recent entries with timestamps
pnpm dlx @kopai/cli logs search --service my-service \
--fields Timestamp,Body --sort ASC
# Filter to errors only (severity-min 17 = ERROR)
pnpm dlx @kopai/cli logs search --service my-service --severity-min 17 --json
```
* yarn
```bash
# Recent entries with timestamps
yarn dlx @kopai/cli logs search --service my-service \
--fields Timestamp,Body --sort ASC
# Filter to errors only (severity-min 17 = ERROR)
yarn dlx @kopai/cli logs search --service my-service --severity-min 17 --json
```
## Sending to Kopai.app in the cloud
[Section titled “Sending to Kopai.app in the cloud”](#sending-to-kopaiapp-in-the-cloud)
Swap the exporter endpoint and add a bearer token:
```yaml
exporters:
otlphttp/kopai:
endpoint: https://otlp-http.kopai.app
headers:
authorization: "Bearer YOUR_BACKEND_TOKEN"
```
Drop the `tls: insecure: true` block — the cloud endpoint uses real TLS.
## Working Example
[Section titled “Working Example”](#working-example)
For a complete runnable example with a demo systemd unit and nix dev shell:
**[Journald Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/journald/python-native)**
# rsyslog
> Forward syslog from legacy Linux services to Kopai through rsyslog and the OpenTelemetry Collector, without touching a line of the applications' code.
Ship logs from any application that writes to `/dev/log` — the standard Unix syslog socket — to Kopai via [rsyslog](https://www.rsyslog.com) and the OpenTelemetry Collector. The application needs **no code changes**: it already uses `syslog(3)` or a language equivalent, and rsyslog is already reading from `/dev/log` on every Debian, Ubuntu, and RHEL-family host.
This is the classic migration path — a Linux server with legacy services that already log via syslog, and you want their output in Kopai without touching a line of their code.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* rsyslog (standard on Debian, Ubuntu, and most RHEL-family distributions)
* [`otelcol-contrib`](https://github.com/open-telemetry/opentelemetry-collector-releases)
* Kopai running locally:
- npm
```bash
npx @kopai/app start
```
- pnpm
```bash
pnpm dlx @kopai/app start
```
- yarn
```bash
yarn dlx @kopai/app start
```
## Configure the collector
[Section titled “Configure the collector”](#configure-the-collector)
Create `otel-collector/rsyslog.yaml` with a `syslog` receiver on port 54527:
```yaml
receivers:
syslog:
tcp:
listen_address: "0.0.0.0:54527"
protocol: rfc3164
operators:
- type: move
from: attributes.message
to: body
processors:
batch: {}
resource:
attributes:
- key: service.name
value: "my-service"
action: insert
exporters:
otlphttp/kopai:
endpoint: http://localhost:4318
tls:
insecure: true
service:
pipelines:
logs:
receivers: [syslog]
processors: [resource, batch]
exporters: [otlphttp/kopai]
```
The receiver parses the RFC 3164 envelope, lifts the payload into the log body, and preserves facility, severity, and timestamp as attributes.
## Run the collector
[Section titled “Run the collector”](#run-the-collector)
```bash
otelcol-contrib --config otel-collector/rsyslog.yaml
```
When the collector is healthy you’ll see a final log line like `Everything is ready. Begin running and processing data.` and port 54527 will be listening (`ss -ltn | grep 54527`).
## Quick smoke test
[Section titled “Quick smoke test”](#quick-smoke-test)
Before touching rsyslog, you can validate the collector and Kopai end-to-end by piping a synthetic RFC 3164 message at the listener:
```bash
printf '<14>%s testhost kopai-test: hello-from-smoke-test\n' "$(date '+%b %e %H:%M:%S')" \
| nc -q1 127.0.0.1 54527
```
Then run the verification commands below and look for `hello-from-smoke-test` in the body.
## Forward from rsyslog
[Section titled “Forward from rsyslog”](#forward-from-rsyslog)
Add one rule to `/etc/rsyslog.conf` (or drop a file in `/etc/rsyslog.d/`) to forward all syslog traffic to the collector over TCP:
```plaintext
*.* action(
type="omfwd"
target="127.0.0.1"
port="54527"
protocol="tcp"
action.resumeRetryCount="10"
queue.type="linkedList"
queue.size="10000"
)
```
Note
**Timestamp accuracy**: RFC 3164 syslog messages contain no year and no timezone. The collector parses the wall-clock timestamp as UTC, so logs ingested this way will appear shifted by your local UTC offset in Kopai. If you need accurate timestamps (for trace correlation, incident timelines, etc.), use RFC 5424 instead by changing the rsyslog rule to `RSYSLOG_SyslogProtocol23Format` and setting `protocol: rfc5424` in the collector receiver.
The `linkedList` queue buffers messages if the collector is briefly unreachable. Reload rsyslog:
```bash
sudo systemctl restart rsyslog
```
Any process on the host that calls `syslog(3)`, writes to `/dev/log`, or uses a language syslog handler (Python’s `SysLogHandler`, Java’s `SyslogAppender`, etc.) now flows through to Kopai.
## Verify
[Section titled “Verify”](#verify)
Confirm logs arrived using the Kopai CLI:
* npm
```bash
# Recent entries with timestamps
npx @kopai/cli logs search --service my-service \
--fields Timestamp,Body --sort ASC
# Filter to errors only (severity-min 17 = ERROR)
npx @kopai/cli logs search --service my-service --severity-min 17 --json
```
* pnpm
```bash
# Recent entries with timestamps
pnpm dlx @kopai/cli logs search --service my-service \
--fields Timestamp,Body --sort ASC
# Filter to errors only (severity-min 17 = ERROR)
pnpm dlx @kopai/cli logs search --service my-service --severity-min 17 --json
```
* yarn
```bash
# Recent entries with timestamps
yarn dlx @kopai/cli logs search --service my-service \
--fields Timestamp,Body --sort ASC
# Filter to errors only (severity-min 17 = ERROR)
yarn dlx @kopai/cli logs search --service my-service --severity-min 17 --json
```
## Sending to Kopai.app in the cloud
[Section titled “Sending to Kopai.app in the cloud”](#sending-to-kopaiapp-in-the-cloud)
Swap the exporter endpoint and add a bearer token:
```yaml
exporters:
otlphttp/kopai:
endpoint: https://otlp-http.kopai.app
headers:
authorization: "Bearer YOUR_BACKEND_TOKEN"
```
Drop the `tls: insecure: true` block — the cloud endpoint uses real TLS.
## Working Example
[Section titled “Working Example”](#working-example)
For a complete runnable example with a demo Python app, rsyslog, and the collector all wired up under Docker Compose:
**[Rsyslog Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/syslog/python-rsyslog)**
# Browser / React
> Trace React, Vue, Angular or plain JavaScript in the browser with OpenTelemetry, using a Kopai frontend token that is safe to ship in your bundle.
Instrument your frontend applications (React, Vue, Angular, vanilla JS) to send traces to Kopai.
## Token Types
[Section titled “Token Types”](#token-types)
Kopai provides two token types:
| Token | Use for | Visibility |
| ------------ | ---------------- | -------------- |
| **Backend** | Server-side apps | keep secret |
| **Frontend** | Browser apps | safe to expose |
Frontend tokens are designed to be embedded in your frontend JS app bundle.
## Installation
[Section titled “Installation”](#installation)
* npm
```bash
npm install @opentelemetry/api \
@opentelemetry/sdk-trace-web \
@opentelemetry/context-zone \
@opentelemetry/instrumentation \
@opentelemetry/instrumentation-document-load \
@opentelemetry/instrumentation-fetch \
@opentelemetry/exporter-trace-otlp-http
```
* pnpm
```bash
pnpm add @opentelemetry/api \
@opentelemetry/sdk-trace-web \
@opentelemetry/context-zone \
@opentelemetry/instrumentation \
@opentelemetry/instrumentation-document-load \
@opentelemetry/instrumentation-fetch \
@opentelemetry/exporter-trace-otlp-http
```
* yarn
```bash
yarn add @opentelemetry/api \
@opentelemetry/sdk-trace-web \
@opentelemetry/context-zone \
@opentelemetry/instrumentation \
@opentelemetry/instrumentation-document-load \
@opentelemetry/instrumentation-fetch \
@opentelemetry/exporter-trace-otlp-http
```
## React Setup
[Section titled “React Setup”](#react-setup)
Create `src/instrumentation.js`:
```javascript
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { ZoneContextManager } from "@opentelemetry/context-zone";
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import {
ATTR_SERVICE_NAME,
ATTR_SERVICE_VERSION,
} from "@opentelemetry/semantic-conventions";
export function setupInstrumentation() {
const resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: "my-react-app",
[ATTR_SERVICE_VERSION]: "1.0.0",
});
const exporter = new OTLPTraceExporter({
url: "https://otlp-http.kopai.app/v1/traces",
headers: {
Authorization: `Bearer ${import.meta.env.VITE_KOPAI_TOKEN}`,
},
});
const provider = new WebTracerProvider({
resource,
spanProcessors: [new BatchSpanProcessor(exporter)],
});
provider.register({
contextManager: new ZoneContextManager(),
});
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation(),
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [/.+/g],
}),
],
});
}
```
Initialize in `main.jsx`:
```jsx
import { setupInstrumentation } from "./instrumentation";
setupInstrumentation();
// ... rest of your app
```
## Environment Variables
[Section titled “Environment Variables”](#environment-variables)
Store your token in environment variables:
.env
```bash
VITE_KOPAI_TOKEN=your_frontend_token
```
| Bundler | Prefix | Access |
| ---------------- | -------------- | --------------------------- |
| Vite | `VITE_` | `import.meta.env.VITE_*` |
| Create React App | `REACT_APP_` | `process.env.REACT_APP_*` |
| Next.js | `NEXT_PUBLIC_` | `process.env.NEXT_PUBLIC_*` |
## Auto-Instrumentation
[Section titled “Auto-Instrumentation”](#auto-instrumentation)
The setup above automatically captures:
* **Document load** - Page load timing, resources
* **Fetch requests** - All HTTP calls with timing
* **Trace propagation** - Links frontend to backend traces
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example with React frontend and Express backend:
**[React SPA Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/react-spa)**
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
### No traces appearing
[Section titled “No traces appearing”](#no-traces-appearing)
1. Check browser DevTools → Network for requests to `otlp-http.kopai.app`
2. Verify token format: `Authorization: Bearer `
3. Check console for CORS errors
### 401 Unauthorized
[Section titled “401 Unauthorized”](#401-unauthorized)
* Verify token is correct (no extra spaces)
* Check `Bearer` prefix is included
### CORS errors
[Section titled “CORS errors”](#cors-errors)
* Your origin must be in the token’s allowed origins
# C++
> Link OpenTelemetry into C++ apps with CMake or vcpkg and export spans to Kopai over OTLP HTTP. Your agent can handle the build setup via the Kopai skills.
Instrument your C++ applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
### CMake (Recommended)
[Section titled “CMake (Recommended)”](#cmake-recommended)
```cmake
find_package(opentelemetry-cpp CONFIG REQUIRED)
target_link_libraries(myapp
opentelemetry-cpp::trace
opentelemetry-cpp::logs
opentelemetry-cpp::metrics
opentelemetry-cpp::otlp_http_exporter
)
```
### vcpkg
[Section titled “vcpkg”](#vcpkg)
```bash
vcpkg install opentelemetry-cpp[otlp-http]
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
```cpp
#include
#include
#include
#include
#include
namespace otlp = opentelemetry::exporter::otlp;
namespace trace_sdk = opentelemetry::sdk::trace;
namespace trace_api = opentelemetry::trace;
void initTracer() {
// Get endpoint from environment
const char* endpoint = std::getenv("OTEL_EXPORTER_OTLP_ENDPOINT");
if (!endpoint) endpoint = "http://localhost:4318";
otlp::OtlpHttpExporterOptions options;
options.url = std::string(endpoint) + "/v1/traces";
auto exporter = otlp::OtlpHttpExporterFactory::Create(options);
auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(
std::move(exporter)
);
auto provider = trace_sdk::TracerProviderFactory::Create(
std::move(processor)
);
trace_api::Provider::SetTracerProvider(std::move(provider));
}
```
## Creating Spans
[Section titled “Creating Spans”](#creating-spans)
```cpp
auto tracer = trace_api::Provider::GetTracerProvider()
->GetTracer("my-app", "1.0.0");
auto span = tracer->StartSpan("my-operation");
auto scope = tracer->WithActiveSpan(span);
// Your code here
span->End();
```
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_SERVICE_NAME="my-service"
./myapp
```
| Variable | Description |
| ----------------------------- | ---------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Build Notes
[Section titled “Build Notes”](#build-notes)
Building opentelemetry-cpp requires:
* CMake 3.12+
* C++14 or later
* curl, gRPC, protobuf dependencies
For a complete working example:\
[C++ Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/cpp)
## Reference
[Section titled “Reference”](#reference)
[OpenTelemetry C++](https://opentelemetry.io/docs/languages/cpp/)
# .NET
> Add OpenTelemetry to ASP.NET Core and export .NET traces, metrics and logs to Kopai. Wire it by hand, or leave it to your agent and the Kopai skills.
Instrument your .NET applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
```bash
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
Add OpenTelemetry to your ASP.NET Core application:
```csharp
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
var endpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT")
?? "http://localhost:4318";
var serviceName = Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME")
?? "my-service";
// Setup Traces and Metrics
builder.Services.AddOpenTelemetry()
.ConfigureResource(r => r.AddService(serviceName))
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddOtlpExporter(opts =>
{
opts.Endpoint = new Uri($"{endpoint}/v1/traces");
opts.Protocol = OtlpExportProtocol.HttpProtobuf;
}))
.WithMetrics(metrics => metrics
.AddAspNetCoreInstrumentation()
.AddOtlpExporter(opts =>
{
opts.Endpoint = new Uri($"{endpoint}/v1/metrics");
opts.Protocol = OtlpExportProtocol.HttpProtobuf;
}));
// Setup Logs
builder.Logging.AddOpenTelemetry(logging =>
{
logging.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName));
logging.AddOtlpExporter(opts =>
{
opts.Endpoint = new Uri($"{endpoint}/v1/logs");
opts.Protocol = OtlpExportProtocol.HttpProtobuf;
});
});
```
**Important**: Use `OtlpExportProtocol.HttpProtobuf` and append the signal path (`/v1/traces`, `/v1/logs`, `/v1/metrics`) to the endpoint.
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_SERVICE_NAME="my-service"
dotnet run
```
| Variable | Description |
| ----------------------------- | ---------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Sending Headers
[Section titled “Sending Headers”](#sending-headers)
For authentication, add headers to the exporter:
```csharp
opts.Headers = "Authorization=Bearer YOUR_TOKEN";
```
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[.NET Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/dotnet)**
# Erlang/Elixir
> Erlang and Elixir services send traces to Kopai through the OpenTelemetry SDK and OTLP exporter. The Kopai skills let your agent do the wiring.
Instrument your Erlang and Elixir applications to send traces, logs, and metrics to Kopai.
## Installation (Elixir)
[Section titled “Installation (Elixir)”](#installation-elixir)
Add to your `mix.exs`:
```elixir
defp deps do
[
{:opentelemetry_api, "~> 1.5"},
{:opentelemetry, "~> 1.7"},
{:opentelemetry_exporter, "~> 1.10"},
{:opentelemetry_cowboy, "~> 1.0"} # for auto HTTP instrumentation
]
end
```
## Installation (Erlang)
[Section titled “Installation (Erlang)”](#installation-erlang)
Add to your `rebar.config`:
```erlang
{deps, [
{opentelemetry_api, "~> 1.5"},
{opentelemetry, "~> 1.7"},
{opentelemetry_exporter, "~> 1.10"}
]}.
```
## Configuration (Elixir)
[Section titled “Configuration (Elixir)”](#configuration-elixir)
config/runtime.exs
```elixir
config :opentelemetry,
span_processor: :batch,
traces_exporter: :otlp
config :opentelemetry_exporter,
otlp_protocol: :http_protobuf,
otlp_endpoint: System.get_env("OTEL_EXPORTER_OTLP_ENDPOINT")
```
## Creating Spans
[Section titled “Creating Spans”](#creating-spans)
```elixir
# In your application module
defmodule MyApp.Application do
use Application
require OpenTelemetry.Tracer, as: Tracer
def start(_type, _args) do
# Initialize auto-instrumentation for Cowboy
:opentelemetry_cowboy.setup()
children = [
# your supervision tree
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
# In your handler modules
defmodule MyApp.Handler do
require OpenTelemetry.Tracer, as: Tracer
def handle_request do
Tracer.with_span "process_request" do
Tracer.set_attributes([
{"http.method", "GET"},
{"custom.attribute", "value"}
])
# ... your code
end
end
end
```
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_SERVICE_NAME="my-service"
mix run --no-halt
```
| Variable | Description |
| ----------------------------- | ---------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Erlang Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/erlang)**
# Go
> Send Go traces to Kopai with the OpenTelemetry SDK and its OTLP HTTP exporter. Let your coding agent wire it up for you, or follow the manual steps.
Instrument your Go applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
```bash
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
go get go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
Initialize OpenTelemetry in your application:
```go
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)
func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
exporter, err := otlptracehttp.New(ctx)
if err != nil {
return nil, err
}
res, _ := resource.New(ctx,
resource.WithAttributes(semconv.ServiceName("my-service")),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)
return tp, nil
}
```
The SDK reads the OTLP endpoint from standard OpenTelemetry environment variables.
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
export OTEL_SERVICE_NAME="my-service"
go run main.go
```
| Variable | Description |
| ----------------------------- | ----------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_EXPORTER_OTLP_HEADERS` | Auth header with your backend token |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Graceful Shutdown
[Section titled “Graceful Shutdown”](#graceful-shutdown)
Always shut down the tracer provider to flush remaining spans:
```go
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
tp.Shutdown(ctx)
}()
```
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Go Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/go)**
# Java
> Trace Java apps in Kopai with zero code changes using OpenTelemetry's auto-instrumentation jar, or let your coding agent set it up with the Kopai skills.
Instrument your Java applications to send traces to Kopai using the OpenTelemetry Java agent.
## Java Agent (Recommended)
[Section titled “Java Agent (Recommended)”](#java-agent-recommended)
The [OpenTelemetry Java agent](https://github.com/open-telemetry/opentelemetry-java-instrumentation) provides **zero-code instrumentation** - no code changes required.
### Download the Agent
[Section titled “Download the Agent”](#download-the-agent)
```bash
curl -L -O https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
```
### Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set environment variables and run with the agent:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
export OTEL_SERVICE_NAME="my-java-service"
java -javaagent:opentelemetry-javaagent.jar -jar your-app.jar
```
| Variable | Description |
| ----------------------------- | ----------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_EXPORTER_OTLP_HEADERS` | Auth header with your backend token |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Auto-Instrumentation
[Section titled “Auto-Instrumentation”](#auto-instrumentation)
The Java agent automatically instruments:
* **HTTP** - Spring MVC, JAX-RS, Servlet, Netty, OkHttp
* **Databases** - JDBC, Hibernate, MongoDB, Redis, Cassandra
* **Messaging** - Kafka, RabbitMQ, JMS
* **Frameworks** - Spring Boot, Quarkus, Micronaut, Vert.x
* **Runtime** - JVM metrics, garbage collection, thread pools
See the [full list of supported libraries](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md).
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Java Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/java)**
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
### No traces appearing
[Section titled “No traces appearing”](#no-traces-appearing)
1. Verify the agent is loaded: look for `[otel.javaagent]` in startup logs
2. Check `OTEL_EXPORTER_OTLP_ENDPOINT` is set correctly
3. Verify token in `OTEL_EXPORTER_OTLP_HEADERS`
### Agent not attaching
[Section titled “Agent not attaching”](#agent-not-attaching)
* Ensure `-javaagent` flag comes **before** `-jar`
* Check Java version: agent requires Java 8+
### Debug logging
[Section titled “Debug logging”](#debug-logging)
Enable agent debug logs:
```bash
export OTEL_JAVAAGENT_DEBUG=true
```
## References
[Section titled “References”](#references)
* [OpenTelemetry Java Getting Started](https://opentelemetry.io/docs/languages/java/getting-started/)
* [Java Agent Releases](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases)
* [Supported Libraries](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md)
* [SDK Configuration](https://opentelemetry.io/docs/languages/sdk-configuration/)
# Node.js
> Node.js apps report traces and logs to Kopai through OpenTelemetry auto-instrumentation. Install the Kopai skills and your coding agent sets it up.
Instrument your Node.js applications to send traces and logs to Kopai.
## Installation
[Section titled “Installation”](#installation)
* npm
```bash
npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
```
* pnpm
```bash
pnpm add @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
```
* yarn
```bash
yarn add @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
Create an `instrumentation.mjs` file:
```javascript
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
const sdk = new NodeSDK({
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
```
The SDK automatically reads configuration from [standard OpenTelemetry environment variables](https://opentelemetry.io/docs/languages/sdk-configuration/).
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
export OTEL_SERVICE_NAME="my-service"
node --import ./instrumentation.mjs app.js
```
| Variable | Description |
| ----------------------------- | ----------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_EXPORTER_OTLP_HEADERS` | Auth header with your backend token |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Auto-Instrumentation
[Section titled “Auto-Instrumentation”](#auto-instrumentation)
The `@opentelemetry/auto-instrumentations-node` package provides automatic instrumentation for:
* HTTP/HTTPS
* Express, Fastify, Koa
* MongoDB, PostgreSQL, MySQL
* Redis, Memcached
* gRPC
* And more
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Node.js Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/node-js)**
# PHP
> Connect a PHP app to Kopai with the OpenTelemetry SDK and OTLP exporter from Composer. Your coding agent sets it up once you add the Kopai skills.
Instrument your PHP applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
```bash
composer require open-telemetry/sdk open-telemetry/exporter-otlp \
php-http/guzzle7-adapter guzzlehttp/guzzle nyholm/psr7
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
```php
merge(
ResourceInfo::create(Attributes::create([
ResourceAttributes::SERVICE_NAME => $serviceName,
]))
);
// Create OTLP HTTP exporter
$transport = PsrTransportFactory::discover()->create(
"$endpoint/v1/traces",
ContentTypes::JSON
);
$exporter = new SpanExporter($transport);
// Build and register tracer provider
$tracerProvider = TracerProvider::builder()
->addSpanProcessor(new SimpleSpanProcessor($exporter))
->setResource($resource)
->build();
// Get tracer and create spans
$tracer = $tracerProvider->getTracer('my-app');
$span = $tracer->spanBuilder('my-operation')->startSpan();
// ... your code
$span->end();
```
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_SERVICE_NAME="my-service"
php -S 0.0.0.0:8080
```
| Variable | Description |
| ----------------------------- | ---------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[PHP Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/php)**
# Python
> Instrument a Python service with OpenTelemetry and watch its traces and metrics arrive in Kopai. Your agent can do the setup through the Kopai skills.
Instrument your Python applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
```bash
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
Initialize OpenTelemetry in your application:
```python
from opentelemetry import trace, metrics
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
# Create resource
resource = Resource.create({"service.name": "my-service"})
# Setup traces
trace_provider = TracerProvider(resource=resource)
trace_provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(trace_provider)
# Setup metrics
metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter())
meter_provider = MeterProvider(resource=resource, metric_readers=[metric_reader])
metrics.set_meter_provider(meter_provider)
```
The SDK reads the OTLP endpoint from standard OpenTelemetry environment variables.
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_TOKEN"
export OTEL_SERVICE_NAME="my-service"
python app.py
```
| Variable | Description |
| ----------------------------- | ----------------------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_EXPORTER_OTLP_HEADERS` | Auth header with your backend token |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Auto-Instrumentation
[Section titled “Auto-Instrumentation”](#auto-instrumentation)
For automatic instrumentation, use the distro package:
```bash
pip install opentelemetry-distro
opentelemetry-bootstrap -a install
opentelemetry-instrument python app.py
```
This provides automatic instrumentation for Flask, Django, FastAPI, requests, and more.
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Python Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/python)**
# Ruby
> Ruby apps ship traces to Kopai with the OpenTelemetry SDK gems and optional auto-instrumentation. With the Kopai skills, your coding agent adds them.
Instrument your Ruby applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
```bash
gem install opentelemetry-sdk opentelemetry-exporter-otlp
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
```ruby
require 'opentelemetry/sdk'
require 'opentelemetry/exporter/otlp'
endpoint = ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] || 'http://localhost:4318'
service_name = ENV['OTEL_SERVICE_NAME'] || 'my-service'
# Create OTLP exporter with explicit endpoint
exporter = OpenTelemetry::Exporter::OTLP::Exporter.new(
endpoint: "#{endpoint}/v1/traces"
)
OpenTelemetry::SDK.configure do |c|
c.service_name = service_name
c.add_span_processor(
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(exporter)
)
end
# Get tracer and create spans
tracer = OpenTelemetry.tracer_provider.tracer('my-app')
tracer.in_span('my-operation') do |span|
span.set_attribute('key', 'value')
# ... your code
end
```
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_SERVICE_NAME="my-service"
ruby app.rb
```
| Variable | Description |
| ----------------------------- | ---------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Auto-Instrumentation
[Section titled “Auto-Instrumentation”](#auto-instrumentation)
For Sinatra, Rails, or other frameworks:
```bash
gem install opentelemetry-instrumentation-all
```
```ruby
require 'opentelemetry/instrumentation/all'
OpenTelemetry::SDK.configure do |c|
c.use_all
end
```
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Ruby Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/ruby)**
# Rust
> Export Rust traces to Kopai using the opentelemetry, opentelemetry_sdk and opentelemetry-otlp crates. Ask your agent to do it, or follow this guide.
Instrument your Rust applications to send traces, logs, and metrics to Kopai.
## Installation
[Section titled “Installation”](#installation)
Add to your `Cargo.toml`:
```toml
[dependencies]
opentelemetry = "0.31"
opentelemetry_sdk = { version = "0.31", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.31", features = ["http-json"] }
tokio = { version = "1", features = ["full"] }
```
## Basic Setup
[Section titled “Basic Setup”](#basic-setup)
```rust
use opentelemetry::global;
use opentelemetry::trace::Tracer;
use opentelemetry_otlp::{Protocol, WithExportConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::Resource;
use std::env;
fn init_tracer() -> SdkTracerProvider {
let endpoint = env::var("OTEL_EXPORTER_OTLP_ENDPOINT")
.unwrap_or_else(|_| "http://localhost:4318".to_string());
let service_name = env::var("OTEL_SERVICE_NAME")
.unwrap_or_else(|_| "my-service".to_string());
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_http()
.with_protocol(Protocol::HttpJson)
.with_endpoint(format!("{}/v1/traces", endpoint))
.build()
.expect("Failed to create exporter");
let resource = Resource::builder()
.with_service_name(service_name)
.build();
let provider = SdkTracerProvider::builder()
.with_resource(resource)
.with_batch_exporter(exporter)
.build();
global::set_tracer_provider(provider.clone());
provider
}
fn main() {
let provider = init_tracer();
let tracer = global::tracer("my-app");
tracer.in_span("my-operation", |_cx| {
// ... your code
});
provider.shutdown().expect("Shutdown failed");
}
```
## Running Your Application
[Section titled “Running Your Application”](#running-your-application)
Set the required environment variables and run:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_SERVICE_NAME="my-service"
cargo run --release
```
| Variable | Description |
| ----------------------------- | ---------------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Kopai OTLP endpoint |
| `OTEL_SERVICE_NAME` | Name shown in Kopai UI |
## Working Example
[Section titled “Working Example”](#working-example)
For a complete working example:
**[Rust Example](https://github.com/kopai-app/kopai-integration-examples/tree/main/rust)**
# Kopai Cloud
> Send telemetry to Kopai Cloud and set up your team
Kopai Cloud is the hosted backend. Your app sends telemetry to a managed endpoint and your whole team reads it in one dashboard at `app.kopai.app`. It is free while in alpha, no credit card.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* An application instrumented with OpenTelemetry. The [Quickstart](/docs/quickstart/) is the fastest way: your agent sets it up and tests it locally first. [Manual Setup](/docs/manual-setup/) covers hand-wiring.
## Step 1: Create an Account
[Section titled “Step 1: Create an Account”](#step-1-create-an-account)
Sign up at [app.kopai.app/signup](https://app.kopai.app/signup). You sign in with a magic link sent to your email.
## Step 2: Generate a Backend Token
[Section titled “Step 2: Generate a Backend Token”](#step-2-generate-a-backend-token)
Your app authenticates to Kopai Cloud with a backend token. Creating tokens requires the owner or admin role.
1. Open **Settings** → **Access Tokens** in the dashboard
2. Click **Generate Token** and choose **Backend Token**
3. Copy the token. It is shown once.
Building a browser app? Use a **Frontend Token** with allowed origins instead. The [browser guide](/docs/integration/languages/browser/) covers that setup.
## Step 3: Point Your App at Kopai Cloud
[Section titled “Step 3: Point Your App at Kopai Cloud”](#step-3-point-your-app-at-kopai-cloud)
Set two environment variables for your instrumented app:
```bash
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-http.kopai.app"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_BACKEND_TOKEN"
```
Your instrumentation stays identical to local. Only the endpoint and the auth header change.
## Step 4: Verify Ingestion
[Section titled “Step 4: Verify Ingestion”](#step-4-verify-ingestion)
Open [app.kopai.app/observability](https://app.kopai.app/observability) and confirm traces, logs, and metrics are arriving from your app.
## Step 5: Invite Your Team
[Section titled “Step 5: Invite Your Team”](#step-5-invite-your-team)
1. Go to **Team** in the dashboard
2. Click **Invite Member**
Invited members get the member role. They can read all telemetry. Creating tokens stays with owners and admins.
## Step 6: Authenticate the CLI
[Section titled “Step 6: Authenticate the CLI”](#step-6-authenticate-the-cli)
The CLI queries Kopai Cloud with its own read-only token. An owner or admin creates it:
1. Open **Settings** → **Access Tokens**
2. Click **Generate Token** and choose **CLI Token**
3. Copy the token
Then log in:
* npm
```bash
npx @kopai/cli login
```
* pnpm
```bash
pnpm dlx @kopai/cli login
```
* yarn
```bash
yarn dlx @kopai/cli login
```
Paste the token at the prompt. The CLI saves it to `./.kopairc` with the server URL `https://api.kopai.app/v2`. See [CLI authentication](/docs/authentication/#cli-authentication) for flags, expiry, and logout.
## Next Steps
[Section titled “Next Steps”](#next-steps)
[Authentication](/docs/authentication/)Token types, best practices, and the CLI config file
[AI Agent Skills](/docs/skills/)Let your coding agent instrument apps and query telemetry
# Manual Setup
> Instrument your app with OpenTelemetry by hand and query it yourself
The [Quickstart](/docs/quickstart/) lets your coding agent do this work. This page is the same journey by hand. It is also the path for stacks the `otel-instrumentation` skill does not cover yet.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Node.js v22.13.0+ installed
## Step 1: Start Kopai
[Section titled “Step 1: Start Kopai”](#step-1-start-kopai)
Run the local backend:
* npm
```bash
npx @kopai/app start
```
* pnpm
```bash
pnpm dlx @kopai/app start
```
* yarn
```bash
yarn dlx @kopai/app start
```
This starts an OTLP collector on port `4318` and the dashboard with its API server on port `8000`.
## Step 2: Instrument Your Application
[Section titled “Step 2: Instrument Your Application”](#step-2-instrument-your-application)
Follow the OpenTelemetry guide for your language:
[Node.js](/docs/integration/languages/node/)
[JavaScript in the browser](/docs/integration/languages/browser/)
[Python](/docs/integration/languages/python/)
[Go](/docs/integration/languages/go/)
[Java](/docs/integration/languages/java/)
[.NET](/docs/integration/languages/dotnet/)
[Ruby](/docs/integration/languages/ruby/)
[PHP](/docs/integration/languages/php/)
[Rust](/docs/integration/languages/rust/)
[C++](/docs/integration/languages/cpp/)
[Erlang](/docs/integration/languages/erlang/)
We also cover the [Next.js](/docs/integration/frameworks/nextjs/), [Mastra](/docs/integration/frameworks/mastra/), and [LangChain](/docs/integration/frameworks/langchain/) frameworks, plus infrastructure logs from [journald](/docs/integration/infrastructure-logs/journald/) and [rsyslog](/docs/integration/infrastructure-logs/rsyslog/).
## Step 3: See Your Telemetry
[Section titled “Step 3: See Your Telemetry”](#step-3-see-your-telemetry)
Run your app and send it a few requests. Then open the dashboard at `http://localhost:8000` and inspect the Traces, Logs, and Metrics.
## Step 4: Query With the CLI
[Section titled “Step 4: Query With the CLI”](#step-4-query-with-the-cli)
The CLI reads from the local backend with no login and no flags:
* npm
```bash
npx @kopai/cli traces search --limit 5
```
* pnpm
```bash
pnpm dlx @kopai/cli traces search --limit 5
```
* yarn
```bash
yarn dlx @kopai/cli traces search --limit 5
```
Swap `traces search` for `logs search` or `metrics discover` to query the other signals.
## Next Steps
[Section titled “Next Steps”](#next-steps)
[Kopai Cloud](/docs/kopai-cloud/)Send telemetry to a hosted backend and share it with your team, free while in alpha
[Quickstart](/docs/quickstart/)Hand this same workflow to your coding agent
# Quickstart
> Point your coding agent at Kopai and see traces, logs, and metrics from your app
Kopai is built for coding agents. You install the skills. Your agent does the rest. Everything runs locally without an account.
Prefer to wire things by hand? Follow [Manual Setup](/docs/manual-setup/). No app to instrument yet? [Run the demo app](/docs/try-demo/) instead.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Node.js v22.13.0+ installed
## Quick Prompt
[Section titled “Quick Prompt”](#quick-prompt)
Paste this into your coding agent and it handles Steps 1 and 2 for you:
```text
Read https://kopai.app/docs/quickstart.md and set up Kopai in my project.
```
When it finishes, pick up at [Step 3](#step-3-ask-questions).
## Step 1: Install the Kopai Skills
[Section titled “Step 1: Install the Kopai Skills”](#step-1-install-the-kopai-skills)
* npm
```bash
npx skills add kopai-app/kopai-mono
```
* pnpm
```bash
pnpm dlx skills add kopai-app/kopai-mono
```
* yarn
```bash
yarn dlx skills add kopai-app/kopai-mono
```
The installer asks which skills to add. Select all three:
* `otel-instrumentation` — instruments your app and proves telemetry arrives
* `root-cause-analysis` — investigates issues by querying your telemetry
* `create-dashboard` — builds dashboards from your metrics, logs, and traces
Coding agents running this command skip the picker and install all three automatically. The skills work with Claude Code and 20+ other agents. See [AI Agent Skills](/docs/skills/) for details and other install options.
## Step 2: Ask Your Agent to Instrument
[Section titled “Step 2: Ask Your Agent to Instrument”](#step-2-ask-your-agent-to-instrument)
```text
Add observability to my app and confirm telemetry reaches Kopai.
```
The `otel-instrumentation` skill runs the whole loop: it starts the local Kopai backend, installs the OpenTelemetry SDK, instruments your code, drives traffic through the app, and asserts on the telemetry that arrives. It covers Node.js, Next.js, Python, Go, Java, .NET, Ruby, PHP, Rust, Erlang, and C++. Browser JavaScript needs [Manual Setup](/docs/manual-setup/).
## Step 3: Ask Questions
[Section titled “Step 3: Ask Questions”](#step-3-ask-questions)
Ask these in a fresh chat. Instrumentation fills the agent’s context, and a new conversation gives better answers. One check first: the skill started Kopai inside the old chat, so if you closed that chat, the server is likely down. Ask your agent to start Kopai again, or run `npx @kopai/app start` yourself.
The validation run already produced telemetry: the skill hit every entry point and drove one failure on purpose. Ask about it:
```text
Find the request that failed during validation and explain what happened.
```
Then use your app and ask about the real traffic:
```text
What are the slowest requests, and where do they spend their time?
```
The `root-cause-analysis` skill answers by querying your telemetry through the Kopai SDK and CLI, and backs every finding with evidence. The dashboard at `http://localhost:8000` shows the same data if you want to browse it yourself.
## Next Steps
[Section titled “Next Steps”](#next-steps)
Your telemetry is flowing. Pick what comes next:
[Kopai Cloud](/docs/kopai-cloud/)Send telemetry to a hosted backend and share it with your team, free while in alpha
[Try with Demo App](/docs/try-demo/)Run a full microservices demo, GenAI services included
[AI Agent Skills](/docs/skills/)Debug production issues and build dashboards with the other Kopai skills
# AI Agent Skills
> Extend your AI coding agent with Kopai observability skills
Skills are portable instruction files that extend AI coding agents with domain-specific knowledge. Instead of reading docs and explaining best practices to your agent manually, skills give your agent direct awareness of Kopai’s CLI, workflows, and conventions.
Kopai ships three skills that cover the full observability workflow — from instrumenting your app, to debugging production issues, to building dashboards.
## What are skills?
[Section titled “What are skills?”](#what-are-skills)
Skills follow the [Agent Skills open standard](https://agentskills.io/) — `SKILL.md` files with YAML frontmatter that any compatible agent can read. Your agent loads the skill’s instructions and automatically invokes them when your conversation matches the skill’s description, or when you trigger them manually via slash command.
The standard is supported by [20+ AI coding agents](https://skills.sh/) including [Claude Code](https://docs.anthropic.com/en/docs/claude-code/skills), [Cursor](https://docs.cursor.com/context/skills), and [Codex CLI](https://github.com/openai/codex). See the [Agent Skills specification](https://agentskills.io/specification) for details.
## Install Kopai skills
[Section titled “Install Kopai skills”](#install-kopai-skills)
Pick one of the three options below. Each installs the same skills.
**Option 1 - skills CLI** (all three skills at once, pulls latest from GitHub):
* npm
```bash
npx skills add kopai-app/kopai-mono
```
* pnpm
```bash
pnpm dlx skills add kopai-app/kopai-mono
```
* yarn
```bash
yarn dlx skills add kopai-app/kopai-mono
```
**Option 2 - Tessl registry** (install individually, version-pinned, quality-reviewed):
* npm
```bash
npx tessl i kopai/otel-instrumentation
npx tessl i kopai/root-cause-analysis
npx tessl i kopai/create-dashboard
```
* pnpm
```bash
pnpm dlx tessl i kopai/otel-instrumentation
pnpm dlx tessl i kopai/root-cause-analysis
pnpm dlx tessl i kopai/create-dashboard
```
* yarn
```bash
yarn dlx tessl i kopai/otel-instrumentation
yarn dlx tessl i kopai/root-cause-analysis
yarn dlx tessl i kopai/create-dashboard
```
Browse all Kopai skills on the [Tessl registry](https://tessl.io/registry/kopai).
**Option 3 - manually**, if you prefer not to use a package manager: clone the [kopai-mono repo](https://github.com/kopai-app/kopai-mono) and copy the `skills/` directories into your agent’s skills folder. See the docs links above for your agent’s exact path.
| Agent | Skills directory | Docs |
| -------------------------------------------------------------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------- |
| [Claude Code](https://code.claude.com/docs/en/skills#where-skills-live) | `.claude/skills/` | [Where skills live](https://code.claude.com/docs/en/skills#where-skills-live) |
| [GitHub Copilot](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/create-skills) | `.github/skills/` | [Creating skills](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/create-skills) |
| [Cursor](https://cursor.com/docs/context/skills) | `.cursor/skills/` | [Skill directories](https://cursor.com/docs/context/skills) |
| [Codex CLI](https://developers.openai.com/codex/skills#where-to-save-skills) | `.codex/skills/` | [Where to save skills](https://developers.openai.com/codex/skills#where-to-save-skills) |
| [Gemini CLI](https://geminicli.com/docs/cli/skills/#skill-discovery-tiers) | `.gemini/skills/` | [Skill discovery tiers](https://geminicli.com/docs/cli/skills/#skill-discovery-tiers) |
| [OpenCode](https://opencode.ai/docs/skills/#place-files) | `.opencode/skills/` | [Place files](https://opencode.ai/docs/skills/#place-files) |
Note
Review the skill files before installing. Skills provide instructions that shape how your agent works — read through them to ensure you’re comfortable with the content.
## Available skills
[Section titled “Available skills”](#available-skills)
### `otel-instrumentation`
[Section titled “otel-instrumentation”](#otel-instrumentation)
Instruments your application with OpenTelemetry SDK and validates that telemetry reaches Kopai.
* **Supports:** Node.js, Next.js, Python, Go, Java, .NET, Ruby, PHP, Rust, Erlang, C++
* **Triggers automatically when you say things like:** “add observability to my app”, “set up tracing”, “my traces aren’t showing up”
* **Slash command:** `/otel-instrumentation`
* **What to expect:** your agent starts the Kopai backend, installs the right OTEL SDK packages, instruments your code, drives traffic through the app, and asserts on the telemetry that arrives. It reports done only when validation passes
[View on GitHub →](https://github.com/kopai-app/kopai-mono/tree/main/skills/otel-instrumentation)
### `root-cause-analysis`
[Section titled “root-cause-analysis”](#root-cause-analysis)
Analyzes telemetry data to debug errors, latency issues, and distributed failures.
* **Triggers automatically when you say things like:** “why is my API slow”, “getting 500 errors”, “service is down”, “requests are timing out”
* **Slash command:** `/root-cause-analysis`
* **Prerequisite:** services sending OpenTelemetry data to Kopai
* **What to expect:** your agent searches error traces → gets full trace context → correlates logs → checks metrics → presents findings with a summary, evidence, impact assessment, and suggested fix
[View on GitHub →](https://github.com/kopai-app/kopai-mono/tree/main/skills/root-cause-analysis)
### `create-dashboard`
[Section titled “create-dashboard”](#create-dashboard)
Builds observability dashboards from OTEL metrics, logs, and traces.
* **Triggers automatically when you say things like:** “create a dashboard”, “show me a metrics view”, “build a monitoring panel”
* **Slash command:** `/create-dashboard`
* **Prerequisite:** metrics data ingested into Kopai
* **What to expect:** your agent discovers available metrics via `npx @kopai/cli metrics discover`, designs a component layout, and creates the dashboard via the CLI — you get a link to view it in the Kopai web UI
[View on GitHub →](https://github.com/kopai-app/kopai-mono/tree/main/skills/create-dashboard)
# Try with Demo App
> Run a full microservices demo, GenAI services included, on Kopai in minutes
Try Kopai with the [OpenTelemetry Astronomy Shop](https://github.com/open-telemetry/opentelemetry-demo), a pre-instrumented microservices demo with 10+ services generating traces, metrics, and logs.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Docker and Docker Compose
* Node.js v22.13.0+ — Kopai stores telemetry through the built-in `node:sqlite` module, which stays behind `--experimental-sqlite` until v22.13.0
* 3–6 GB of free memory, depending on the mode you pick in Step 2
## Step 1: Start Kopai
[Section titled “Step 1: Start Kopai”](#step-1-start-kopai)
* npm
```bash
npx @kopai/app start
```
* pnpm
```bash
pnpm dlx @kopai/app start
```
* yarn
```bash
yarn dlx @kopai/app start
```
Note
On **Linux**, bind to all interfaces so Docker containers can reach Kopai:
* npm
```bash
HOST=0.0.0.0 npx @kopai/app start
```
* pnpm
```bash
HOST=0.0.0.0 pnpm dlx @kopai/app start
```
* yarn
```bash
HOST=0.0.0.0 yarn dlx @kopai/app start
```
## Step 2: Clone & Start the Demo
[Section titled “Step 2: Clone & Start the Demo”](#step-2-clone--start-the-demo)
In a separate terminal:
```bash
git clone https://github.com/kopai-app/opentelemetry-demo.git
cd opentelemetry-demo
make start-kopai
```
Pick the mode that fits your machine:
| Command | Services | Memory |
| -------------------------- | ------------------------------------------------------------- | --------- |
| `make start-kopai` | Core demo + Kafka, accounting, fraud detection | \~4.1 GiB |
| `make start-kopai-minimal` | Core demo only | \~3.1 GiB |
| `make start-kopai-agentic` | Everything above, plus the [GenAI services](#genai-telemetry) | \~5.6 GiB |
All three stop with `make stop-kopai`.
## Step 3: Generate Traffic
[Section titled “Step 3: Generate Traffic”](#step-3-generate-traffic)
Open and browse the Astronomy Shop — add items to cart, place orders, etc. This generates telemetry across all services.
## Step 4: Query Telemetry
[Section titled “Step 4: Query Telemetry”](#step-4-query-telemetry)
Use the Kopai CLI to explore what’s been collected:
* npm
```bash
# Recent traces
npx @kopai/cli traces search --limit 5
# Logs from a specific service
npx @kopai/cli logs search --service cart --fields Timestamp,Body --sort ASC
# Discover available metrics
npx @kopai/cli metrics discover
```
* pnpm
```bash
# Recent traces
pnpm dlx @kopai/cli traces search --limit 5
# Logs from a specific service
pnpm dlx @kopai/cli logs search --service cart --fields Timestamp,Body --sort ASC
# Discover available metrics
pnpm dlx @kopai/cli metrics discover
```
* yarn
```bash
# Recent traces
yarn dlx @kopai/cli traces search --limit 5
# Logs from a specific service
yarn dlx @kopai/cli logs search --service cart --fields Timestamp,Body --sort ASC
# Discover available metrics
yarn dlx @kopai/cli metrics discover
```
## Step 5: Open the Dashboard
[Section titled “Step 5: Open the Dashboard”](#step-5-open-the-dashboard)
View your telemetry visually at .
## Step 6: Ask Your Coding Agent
[Section titled “Step 6: Ask Your Coding Agent”](#step-6-ask-your-coding-agent)
With the [Kopai skills](/docs/skills/) installed, your agent can investigate the demo for you:
```text
Use @kopai/cli to find errors in my services
```
## Step 7: Stop the Demo
[Section titled “Step 7: Stop the Demo”](#step-7-stop-the-demo)
```bash
make stop-kopai
```
## GenAI Telemetry
[Section titled “GenAI Telemetry”](#genai-telemetry)
`make start-kopai-agentic` adds the demo’s agent, chatbot, and MCP services, so Kopai also receives LLM spans, token usage, and MCP tool calls. Chat with the shop at .
LLM calls replay from recorded cassettes by default — no API key, no cost. Cassettes exist only for the `azure/gpt-5.5` and `claude-opus-4-7` models; point `LLM_MODEL` anywhere else and there’s nothing to replay.
For live traffic, set these in `.env.override` — any OpenAI-compatible endpoint works:
```bash
USE_VCR=False
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL=gpt-4o-mini
API_KEY=
```
Caution
`.env.override` is tracked in git. Don’t commit a real key.
## How It Works
[Section titled “How It Works”](#how-it-works)
```plaintext
┌────────────────────────────────────────────────┐
│ Docker Compose Network │
│ │
│ ┌─────────────────────────────┐ │
│ │ Astronomy Shop Demo │ │
│ │ (10+ microservices) │ │
│ │ │ │
│ │ All services instrumented │ │
│ │ with OpenTelemetry │ │
│ └────────────┬────────────────┘ │
│ │ │
│ │ OTLP (traces, metrics, logs) │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ OTel Collector │ │
│ │ (in Docker) │ │
│ └────────────┬────────────────┘ │
│ │ │
│ │ OTLP/HTTP │
│ ▼ │
│ host.docker.internal:4318 │
└───────────────┬────────────────────────────────┘
│
▼
┌────────────────────────────────────┐
│ @kopai/app (on host machine) │
│ │
│ OTel collector: localhost:4318 │
│ Dashboard: localhost:8000 │
└────────────────────────────────────┘
```
The demo loads a customization file, `otelcol-config-extras.yml`, last in its collector config chain. Kopai mounts its own exporter config over that seam and skips the demo’s observability layer entirely, so the only upstream file the fork touches is the `Makefile`.
## Learn More
[Section titled “Learn More”](#learn-more)
For details on the collector config, Compose layering, and running Kopai alongside the bundled stack, see the [full README on GitHub](https://github.com/kopai-app/opentelemetry-demo/tree/main/kopai).
# Legal Notice
> Company information and legal details for NIBBLE d.o.o.
## Company Information
[Section titled “Company Information”](#company-information)
**Legal Name**: NIBBLE društvo s ograničenom odgovornošću za usluge
**Trading As**: Kopai
**Legal Form**: društvo s ograničenom odgovornošću (Limited Liability Company / d.o.o.)
**Registered Office**: Prilesje 18 10000 Zagreb Croatia
**Corporate Identification Number (OIB)**: 96011589399
**Registration Number (MBS)**: 081090607
**Registration Court**: Trgovački sud u Zagrebu (Commercial Court in Zagreb)
**Date of Incorporation**: April 4, 2017
**Primary Business Activity**: K62109 - Ostalo računalno programiranje (Other Computer Programming)
## Management
[Section titled “Management”](#management)
**Director**: Vladimir Adamić
## Contact Information
[Section titled “Contact Information”](#contact-information)
**Email**:
**Phone**:
* +385 1 2395468
* +385 91 7887441
**Website**:
## VAT Information
[Section titled “VAT Information”](#vat-information)
In Croatia, the OIB serves as the VAT identification number.
**VAT Number**: HR96011589399
## Regulatory Information
[Section titled “Regulatory Information”](#regulatory-information)
NIBBLE d.o.o. is registered with the Croatian Commercial Court and operates in accordance with Croatian law.
***
*Last updated: December 12, 2025*
# Cookie Policy
> How Kopai uses cookies and tracking technologies
*Last updated: October 8, 2026*
## What Are Cookies?
[Section titled “What Are Cookies?”](#what-are-cookies)
Cookies are small text files stored on your device when you visit a website. They help us provide a better user experience and understand how you use our service.
## Cookies We Use
[Section titled “Cookies We Use”](#cookies-we-use)
### 1. Essential Cookies (Required)
[Section titled “1. Essential Cookies (Required)”](#1-essential-cookies-required)
These cookies are necessary for the service to function. You cannot opt out.
| Cookie Name | Purpose | Duration | Provider |
| --------------- | ------------------------------- | ------------------ | ---------- |
| `sb-*` | Supabase authentication session | Session/persistent | Supabase |
| `__cf_bm` | Cloudflare bot management | 30 minutes | Cloudflare |
| `__Secure-csrf` | CSRF protection | Session | Kopai |
**Why these are essential:**
* **Authentication cookies** (`sb-*`): Required for secure login and session management. Without these, you cannot access your account.
* **Security cookies** (`__cf_bm`, `__Secure-csrf`): Protect against malicious attacks (DDoS, cross-site request forgery). Critical for platform security.
### 2. Analytics Storage (Optional - Requires Consent)
[Section titled “2. Analytics Storage (Optional - Requires Consent)”](#2-analytics-storage-optional---requires-consent)
To understand how visitors interact with our marketing site we run a self-hosted OpenTelemetry Real User Monitoring (RUM) SDK in the browser. The SDK stays inactive until you click **Accept All** in the cookie banner. While analytics is enabled we use two browser-storage entries — these are technically `localStorage` / `sessionStorage` rather than HTTP cookies, but the ePrivacy Directive treats them the same way and they are listed here for transparency.
With the same consent we also load Google Analytics 4 (see [Google Analytics](#3-google-analytics-optional---requires-consent) below).
| Storage key | Storage type | Purpose | Duration | Provider |
| ---------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -------- |
| `kopai-cookie-consent` | `localStorage` | Records your cookie banner choice (essential / analytics) so we don’t ask again on every page | Until you withdraw consent or until the policy version is bumped | Kopai |
| `kopai-otel-session` | `sessionStorage` | Tab-scoped random UUID v4 (`session.id`) that lets us tell whether two analytics events came from the same browsing session | Deleted automatically when you close the tab | Kopai |
**What `session.id` is and what it isn’t:**
* It is a random UUID generated in your browser (e.g. `b6422eeb-e604-f625-ee90-3b1bc654d808`). It contains no personal data.
* It is **tab-scoped**: opening the site in two tabs creates two independent IDs. Closing the tab destroys the ID — there is no way for us to recognise you on a return visit.
* It is **not** synced across devices, browsers, or to any account.
**Data captured by the analytics SDK while consent is granted:**
* Page paths visited and the order you visited them (e.g. `/`, `/blog/`, `/docs/quickstart/`)
* Referrer page within `kopai.app`
* Click events on the **Sign up** and **Demo** call-to-action buttons (button identifier and destination URL — not the surrounding text you typed)
* Page-load performance (Web Vitals: LCP, FCP, CLS, INP, TTFB) and a list of static resources the page fetched
* Browser metadata reported by your browser: user-agent string, preferred language, platform name, mobile flag
* Display metadata: screen size, viewport size, device pixel ratio
* Network class reported by your browser: effective connection type (e.g. `4g`, `3g`)
* The `session.id` described above
**What we do not capture:**
* We do not capture your IP address in the telemetry payload (Cloudflare necessarily sees it as part of any HTTP request, but it is not stored as a span attribute).
* We do not capture form contents, search queries, keystrokes, mouse movements, or scroll positions.
* We do not capture JavaScript errors or stack traces from the marketing site.
**Withdrawing consent mid-session:**
You can withdraw analytics consent at any time via the **Reject analytics** button in the footer of every page (single click — no reload). The button flips the `analytics` flag in `kopai-cookie-consent` from `true` to `false`, the OTel SDK shuts down immediately, the `kopai-otel-session` entry is deleted from `sessionStorage`, Google Analytics is switched to denied mode and its `_ga` / `_ga_*` cookies are deleted, and no further analytics events leave your browser until you re-grant consent. Re-granting consent in the same tab generates a **new** `session.id` — the old one is gone.
If you want to revisit your full cookie preferences (essential vs. analytics) instead of just opting out of analytics, the **Cookie Settings** button in the same footer resets the banner so you can choose again.
**What we may capture about your network location:**
If you visit Kopai through Cloudflare (the default for our production sites), your browser sends our analytics pipeline two coarse network attributes derived by Cloudflare from your IP address — never the IP itself:
| Attribute | Source | Example | Purpose |
| ----------------- | ----------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `cloud.region` | Cloudflare `cf-ipcountry` response header | `HR`, `DE`, `US` | Country-level traffic distribution for capacity planning. ISO 3166-1 alpha-2 country code only — no city or coordinates. |
| `cloudflare.colo` | Cloudflare `cf-ray` response header | `ZAG`, `FRA`, `IAD` | Identifies which Cloudflare data centre served the request, used for performance debugging. Three-letter airport-style code. |
Both are derived server-side by Cloudflare and forwarded to us via standard response headers; we read them in the browser and tag them on the same telemetry that the `session.id` is attached to. They are gated by the same analytics consent — when you reject analytics, neither is collected.
**Legal basis**: Your consent (GDPR Art. 6(1)(a); ePrivacy Directive Art. 5(3))
### 3. Google Analytics (Optional - Requires Consent)
[Section titled “3. Google Analytics (Optional - Requires Consent)”](#3-google-analytics-optional---requires-consent)
We use Google Analytics 4 to measure visits to our marketing site and docs. The Google tag (`gtag.js`) is not downloaded at all until you click **Accept all**; before that, no request is sent to Google and no Google cookie is set.
| Cookie Name | Purpose | Duration | Provider |
| -------------------- | ----------------------------------------------------------------- | -------- | -------- |
| `_ga` | Distinguishes unique visitors with a randomly generated client ID | 2 years | Google |
| `_ga_` | Persists the session state for our Google Analytics property | 2 years | Google |
**Data Google Analytics collects while consent is granted:** pages visited, referrer, approximate location (derived from your IP address, which Google Analytics 4 does not log or store), device, browser and screen information, and engagement events such as scrolls and outbound link clicks.
**Advertising signals are off:** we send Google `ad_storage`, `ad_user_data` and `ad_personalization` as denied, so the data is not used for advertising or ad personalisation.
**Legal basis**: Your consent (GDPR Art. 6(1)(a); ePrivacy Directive Art. 5(3))
### 4. Dashboard (`app.kopai.app`)
[Section titled “4. Dashboard (app.kopai.app)”](#4-dashboard-appkopaiapp)
The dashboard is a separate site from `kopai.app` and asks for analytics consent separately, with its own banner. Its browser OpenTelemetry SDK does not load until you click **Accept**. You can change your choice at any time in **Settings → Privacy → Analytics**.
| Storage key | Storage type | Purpose | Duration | Category |
| ------------------------- | ---------------- | ----------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------- |
| `sb-*-auth-token` | `localStorage` | Your login session (Supabase) | Until you sign out or the session expires | Essential |
| `kopai-dashboard-consent` | `localStorage` | Records your analytics choice in the dashboard | Until you change it, or until the policy version is bumped | Essential |
| `kopai-ui-theme` | `localStorage` | Remembers your light/dark theme | Until you change it | Preference (set only when you choose a theme) |
| `sidebar_state` | Cookie | Remembers whether the sidebar is open or collapsed | 7 days | Preference (set only when you toggle the sidebar) |
| `kopai-otel-session` | `sessionStorage` | Tab-scoped random ID grouping analytics events from one tab | Deleted when you close the tab or withdraw consent | Analytics — only with consent |
While dashboard analytics is enabled, it captures the same kinds of data as the marketing-site SDK above, plus your user ID, the workspace you are viewing and your role in it. See the [Privacy Policy](/privacy/) for details.
## Managing Cookies
[Section titled “Managing Cookies”](#managing-cookies)
### Consent Management
[Section titled “Consent Management”](#consent-management)
When you first visit Kopai, you’ll see a cookie consent banner. You can:
* **Accept all**: Enable analytics (our self-hosted RUM and Google Analytics)
* **Essential only**: Only use required cookies
You can change your preferences anytime by clicking “Cookie Settings” in the footer.
### Browser Settings
[Section titled “Browser Settings”](#browser-settings)
You can also control cookies via your browser settings:
**Chrome:**
1. Settings > Privacy and security > Cookies and other site data
2. Choose “Block third-party cookies” or “Block all cookies”
**Firefox:**
1. Settings > Privacy & Security > Cookies and Site Data
2. Choose “Delete cookies and site data when Firefox is closed”
**Safari:**
1. Preferences > Privacy > Cookies and website data
2. Choose “Block all cookies”
**Edge:**
1. Settings > Privacy, search, and services > Cookies
2. Choose “Block third-party cookies”
**Note:** Blocking essential cookies will prevent you from logging in to your Kopai account.
### Cookie Duration
[Section titled “Cookie Duration”](#cookie-duration)
* **Session cookies**: Deleted when you close your browser
* **Persistent cookies**: Stored for a specific period (see table above)
* **Revocation**: You can delete all cookies anytime via browser settings
## Third-Party Cookies
[Section titled “Third-Party Cookies”](#third-party-cookies)
We use the following third-party services that may set cookies:
### Supabase (Authentication)
[Section titled “Supabase (Authentication)”](#supabase-authentication)
* **Purpose**: User authentication and session management
* **Data collected**: Email, user ID, session tokens
* **Privacy policy**: [Supabase Privacy Policy](https://supabase.com/privacy)
* **Cookie control**: Essential - cannot be disabled without losing login functionality
### Google Analytics (Analytics)
[Section titled “Google Analytics (Analytics)”](#google-analytics-analytics)
* **Purpose**: Website usage statistics for our marketing site and docs
* **Data collected**: See [Google Analytics](#3-google-analytics-optional---requires-consent) above
* **Privacy policy**: [Google Privacy Policy](https://policies.google.com/privacy) and [How Google uses information from sites that use its services](https://policies.google.com/technologies/partner-sites)
* **Cookie control**: Optional - loaded only after you click **Accept all**; withdraw anytime with **Reject analytics** in the footer
### Cloudflare (CDN & Security)
[Section titled “Cloudflare (CDN & Security)”](#cloudflare-cdn--security)
* **Purpose**: Content delivery, DDoS protection, TLS termination
* **Data collected**: IP address, user agent, request patterns
* **Privacy policy**: [Cloudflare Privacy Policy](https://www.cloudflare.com/privacypolicy/)
* **Cookie control**: Essential - required for site security and performance
## Your Rights Under GDPR
[Section titled “Your Rights Under GDPR”](#your-rights-under-gdpr)
You have the right to:
* **Withdraw consent**: Change your cookie preferences at any time
* **Access information**: Request details about cookies we use
* **Data portability**: Receive your data in machine-readable format
* **Lodge a complaint**: Contact the Croatian DPA (AZOP) at
For cookie-related questions or to exercise your rights, contact:
## Changes to This Policy
[Section titled “Changes to This Policy”](#changes-to-this-policy)
We may update this Cookie Policy to reflect:
* Changes in cookie usage
* New features or analytics tools
* Legal or regulatory requirements
* User feedback and improvements
**Notification:**
* Material changes: Email notification 30 days in advance
* Minor changes: Updated “Last updated” date only
## Children’s Privacy
[Section titled “Children’s Privacy”](#childrens-privacy)
Kopai does not knowingly collect data from individuals under 16 years of age. If you are under 16, please do not use our service without parental consent.
## Contact
[Section titled “Contact”](#contact)
For questions about our cookie usage or to exercise your privacy rights:
**Email**:
**Data Protection Authority**: Croatian Personal Data Protection Agency (AZOP) Selska cesta 136, HR-10 000 Zagreb Website:
***
*This cookie policy complies with the EU Cookie Directive (ePrivacy Directive 2002/58/EC), GDPR, and Croatian data protection law.*
# Terms of Service
> Terms and conditions for using Kopai services
*Last updated: October 8, 2026*
## 1. Agreement to Terms
[Section titled “1. Agreement to Terms”](#1-agreement-to-terms)
By accessing or using Kopai services, you agree to be bound by these Terms of Service.
**Service Provider**: NIBBLE društvo s ograničenom odgovornošću za usluge Prilesje 18, 10000 Zagreb, Croatia OIB: 96011589399
## 2. Service Description
[Section titled “2. Service Description”](#2-service-description)
Kopai is an observability platform that provides the following services:
### Data Ingestion
[Section titled “Data Ingestion”](#data-ingestion)
* **OpenTelemetry-compatible protocols**: OTLP over gRPC and HTTP
* **Trace collection**: Distributed tracing across your microservices
* **Log collection**: Application and system logs
* **Metrics collection**: Performance metrics and measurements
### Data Storage and Processing
[Section titled “Data Storage and Processing”](#data-storage-and-processing)
* **ClickHouse database**: High-performance columnar storage for telemetry data
* **Secure tenant isolation**: Your data is isolated from other customers
* **Configurable retention**: Data retention periods based on your pricing tier
### Visualization and Analysis
[Section titled “Visualization and Analysis”](#visualization-and-analysis)
* **Web-based dashboard**: View and analyze your observability data
* **Real-time data access**: Query your telemetry data as it arrives
* **Search and filtering**: Find specific traces, logs, and metrics
* **Advanced querying**: Powerful query capabilities for telemetry analysis
### Access Control
[Section titled “Access Control”](#access-control)
* **API token management**: Generate backend and frontend access tokens
* **CORS configuration**: Control which frontend applications can access your data
* **Token types**: Separate tokens for server-side and browser-based access
* **Secure authentication**: Integration with Supabase for user authentication
### Service Availability
[Section titled “Service Availability”](#service-availability)
The service is currently in **beta** and provided free of charge (see Section 5 for paid plans). We make continuous improvements and may experience occasional downtime for maintenance.
## 3. Account Terms
[Section titled “3. Account Terms”](#3-account-terms)
### Eligibility
[Section titled “Eligibility”](#eligibility)
To use Kopai, you must:
* Be at least 18 years old, OR
* Represent a business entity with authority to bind that entity to these terms
* Provide accurate and complete registration information
* Comply with all applicable laws and regulations
### Account Registration
[Section titled “Account Registration”](#account-registration)
* **Authentication**: Accounts are created through Supabase authentication
* **Email requirement**: You must provide a valid email address
* **One account per email**: Each email address may be associated with only one account
* **Account information**: You are responsible for maintaining accurate account details
### Account Security
[Section titled “Account Security”](#account-security)
You are responsible for:
* **Password security**: Maintaining the confidentiality of your password
* **Unauthorized access**: All activity that occurs under your account
* **Immediate notification**: Informing us immediately of any unauthorized access at
* **Account sharing**: Not sharing your account credentials with others
We are not liable for any loss or damage arising from your failure to maintain account security.
### Account Termination
[Section titled “Account Termination”](#account-termination)
**By You**:
* You may delete your account at any time through the dashboard
* Upon deletion, your data will be retained for 30 days, then permanently deleted
* You can request immediate data deletion by contacting us
**By Us**:
* We may suspend or terminate accounts that violate these Terms of Service
* We will provide 7 days’ notice before termination except in cases of:
* Illegal activity
* Security threats
* Abuse of the service
* You will receive an email notification to your registered email address
## 4. Acceptable Use Policy
[Section titled “4. Acceptable Use Policy”](#4-acceptable-use-policy)
You agree not to use Kopai to:
### Illegal Activity
[Section titled “Illegal Activity”](#illegal-activity)
* Violate any local, national, or international laws or regulations
* Infringe intellectual property rights of others
* Distribute malware, viruses, or other harmful code
* Engage in fraudulent or deceptive practices
### Service Abuse
[Section titled “Service Abuse”](#service-abuse)
* **Excessive API usage**: Generate unreasonable API request volumes that degrade service performance for others
* **Resource exhaustion**: Attempt to overwhelm or disable our infrastructure
* **Unauthorized access**: Access accounts, data, or systems you are not authorized to access
* **Security testing**: Perform penetration testing or security scanning without prior written permission
### Prohibited Business Practices
[Section titled “Prohibited Business Practices”](#prohibited-business-practices)
* **Reselling or redistribution**: Resell, redistribute, or provide access to Kopai services without our written authorization
* **Competitive use**: Use the service to build a competing observability platform
* **Reverse engineering**: Attempt to reverse engineer, decompile, or discover source code of our platform
### Harmful Content
[Section titled “Harmful Content”](#harmful-content)
* Send telemetry data containing:
* Malicious code or exploits
* Personally identifiable information (PII) of individuals without proper consent
* Illegal content or content that violates others’ rights
* Use the service to monitor applications without proper authorization
### Account Misuse
[Section titled “Account Misuse”](#account-misuse)
* Create multiple accounts to circumvent rate limits or restrictions
* Impersonate other users or organizations
* Share or sell account credentials
### Enforcement
[Section titled “Enforcement”](#enforcement)
Violation of this policy may result in:
* Warning and request to cease the activity
* Temporary suspension of service access
* Permanent account termination
* Legal action if necessary
If you suspect a violation of this policy, report it to .
## 5. Pricing and Payment
[Section titled “5. Pricing and Payment”](#5-pricing-and-payment)
### Current Status
[Section titled “Current Status”](#current-status)
Kopai is currently in beta and provided **free of charge**. Paid plans are not available yet. We will email all users at least **30 days** before we start charging. From that date, the rules in this section apply.
### Plans
[Section titled “Plans”](#plans)
* **Free plan**: Your telemetry data is kept for **1 day**. No payment card is required.
* **Paid plan**: You choose how many days your telemetry data is kept, from **2 to 365 days** (your “retention”).
* **Dedicated plans**: Customers on dedicated infrastructure are billed under a separate written agreement, not under this section.
Seats and API calls are not charged on any plan.
### Price
[Section titled “Price”](#price)
* **€1 per retention day per month**, excluding VAT. For example, 30 days of retention costs €30 per month plus VAT.
* **VAT** is calculated and added at checkout based on your billing address. EU businesses that enter a valid VAT ID are charged under the reverse-charge mechanism where it applies.
* We may change prices with at least **30 days’** email notice. A price change applies from your first billing date after the notice period ends.
### Billing and Payment
[Section titled “Billing and Payment”](#billing-and-payment)
* Paid plans are billed **monthly in advance**, starting on the day you subscribe.
* Payments are processed by our payment provider, **Stripe**. We do not store your full card details.
* We issue an invoice for every payment. Invoices are available in the dashboard to workspace owners.
* Only the workspace owner can subscribe, change the plan, or cancel.
### Changing Your Retention
[Section titled “Changing Your Retention”](#changing-your-retention)
* **Increasing retention** takes effect immediately.
* **Decreasing retention** takes effect at your next billing date, so you keep what you have paid for until then.
* When retention decreases, data older than the new retention period is **permanently deleted** and cannot be recovered.
### Cancellation and Refunds
[Section titled “Cancellation and Refunds”](#cancellation-and-refunds)
You can cancel a paid plan at any time in the dashboard, in one of two ways:
1. **At the end of the billing period**: you keep your paid retention until the period ends, then move to the free plan. You can undo the cancellation until the period ends. No refund is issued.
2. **Immediately, with a refund**: your subscription ends at once and we refund your **last paid invoice in full**, including VAT. Your workspace moves to the free plan straight away, and data older than 1 day is **permanently deleted**.
Immediate refunds are available a limited number of times per customer and only when your last invoice was paid. When an immediate refund is not available, cancellation takes effect at the end of the billing period instead. If you cannot access the dashboard, contact to cancel.
### Failed Payments
[Section titled “Failed Payments”](#failed-payments)
If a payment fails, Stripe will retry it. While the payment is being retried, your paid retention stays in place. If the payment still fails, your subscription ends and your workspace moves to the free plan, and data older than 1 day is permanently deleted.
### Your Statutory Rights
[Section titled “Your Statutory Rights”](#your-statutory-rights)
Nothing in this section limits rights you have as a consumer under Croatian or EU law, including the right of withdrawal described in Section 11.
## 6. Data Ownership
[Section titled “6. Data Ownership”](#6-data-ownership)
### Your Data
[Section titled “Your Data”](#your-data)
**You retain full ownership** of all telemetry data (traces, logs, metrics) you send to Kopai.
We process your data solely to provide the observability service. Specifically:
* **No resale**: We will never sell or license your telemetry data to third parties
* **No cross-customer sharing**: Your data is isolated and never shared with other Kopai customers
* **Limited processing**: We process your data only as necessary to provide the service (storage, visualization, analysis)
* **Your control**: You can export or delete your data at any time
### License You Grant Us
[Section titled “License You Grant Us”](#license-you-grant-us)
By using Kopai, you grant us a **limited, non-exclusive license** to:
* Store your telemetry data on our infrastructure
* Process and analyze your data to provide dashboard visualizations
* Display your data to users you authorize (via your access tokens and CORS settings)
* Perform technical operations (backups, maintenance, optimization)
This license terminates when you delete your account or specific data.
### Our Service and Platform
[Section titled “Our Service and Platform”](#our-service-and-platform)
Kopai retains all intellectual property rights to:
* Platform software and source code
* Infrastructure and architecture
* Documentation and user interfaces
* Trademarks and branding (“Kopai” trademark)
* Improvements and modifications to the platform
### Open Source Components
[Section titled “Open Source Components”](#open-source-components)
Kopai uses the following open source components:
* **ClickHouse**: Apache License 2.0
* **OpenTelemetry SDKs**: Apache License 2.0
* **Traefik**: MIT License
These components retain their respective licenses and attributions.
### Aggregated and Anonymized Data
[Section titled “Aggregated and Anonymized Data”](#aggregated-and-anonymized-data)
We may create aggregated, anonymized statistics from telemetry data (e.g., “average trace duration across all tenants”). Such anonymized data:
* Cannot identify you or your organization
* May be used for product improvements and marketing
* Is not subject to data ownership restrictions
## 7. Service Level Agreement
[Section titled “7. Service Level Agreement”](#7-service-level-agreement)
### Current SLA: Best Effort
[Section titled “Current SLA: Best Effort”](#current-sla-best-effort)
Kopai is currently provided on a **best-effort basis** with no guaranteed uptime commitments.
**What this means:**
* **No uptime guarantees**: We do not commit to specific availability percentages (e.g., 99% uptime)
* **No response time guarantees**: Support is provided on a best-effort basis via email
* **Planned maintenance**: We may perform maintenance with or without advance notice
* **Beta service**: As a beta service, you should expect occasional disruptions and improvements
### Service Monitoring
[Section titled “Service Monitoring”](#service-monitoring)
We actively monitor service health and will:
* Work to restore service quickly in case of outages
* Provide status updates via email for significant incidents
* Notify users of planned maintenance when possible
### Support Channels
[Section titled “Support Channels”](#support-channels)
**Email support**:
* Best-effort response times
* No guaranteed resolution times
* Community support forum (coming soon)
### Future SLA Tiers
[Section titled “Future SLA Tiers”](#future-sla-tiers)
When Kopai transitions from beta to commercial operation, we may introduce:
* Paid tiers with uptime commitments (e.g., 99.9% SLA)
* Guaranteed support response times
* Service credits for SLA violations
* Priority support channels
We will notify users at least 30 days before implementing any SLA changes.
## 8. Intellectual Property
[Section titled “8. Intellectual Property”](#8-intellectual-property)
### Kopai Platform Rights
[Section titled “Kopai Platform Rights”](#kopai-platform-rights)
All intellectual property rights in the Kopai platform belong to **NIBBLE d.o.o.** This includes:
* Software, source code, and algorithms
* User interfaces and design elements
* Documentation, guides, and tutorials
* Trademarks, logos, and branding
* Database structures and schemas
* API designs and specifications
### Your Data Rights
[Section titled “Your Data Rights”](#your-data-rights)
**You retain full ownership** of all telemetry data you send to Kopai. See Section 6 (Data Ownership) for details.
### Open Source Components
[Section titled “Open Source Components”](#open-source-components-1)
Kopai incorporates the following open source software:
| Component | License | Usage |
| ------------------ | ---------- | ------------------------------- |
| ClickHouse | Apache 2.0 | Database engine |
| OpenTelemetry SDKs | Apache 2.0 | Data collection instrumentation |
| Traefik | MIT | Reverse proxy and load balancer |
Full license texts and attributions are available in our platform documentation.
### Trademark Policy
[Section titled “Trademark Policy”](#trademark-policy)
“Kopai” is a trademark of NIBBLE d.o.o. You may not use our trademarks without prior written permission, except:
* Factually referring to the Kopai service (e.g., “We use Kopai for observability”)
* Fair use in comparative advertising
### Copyright Infringement
[Section titled “Copyright Infringement”](#copyright-infringement)
If you believe content on Kopai infringes your copyright, contact with:
* Description of the copyrighted work
* Location of the infringing material
* Your contact information
* Statement of good faith belief
* Statement under penalty of perjury that you are authorized to act
### Restrictions
[Section titled “Restrictions”](#restrictions)
You may not:
* Copy, modify, or create derivative works of Kopai’s platform
* Reverse engineer or decompile the service
* Remove or alter copyright notices or trademarks
* Frame or mirror any part of the service
## 9. Limitation of Liability
[Section titled “9. Limitation of Liability”](#9-limitation-of-liability)
### Service “AS IS”
[Section titled “Service “AS IS””](#service-as-is)
Kopai is provided **“AS IS”** and **“AS AVAILABLE”** without warranties of any kind, either express or implied, including but not limited to:
* Warranties of merchantability
* Fitness for a particular purpose
* Non-infringement
* Uninterrupted or error-free operation
### No Liability for Data Loss
[Section titled “No Liability for Data Loss”](#no-liability-for-data-loss)
**You are responsible for maintaining backups of your data.** We are not liable for:
* Loss or corruption of telemetry data
* Inability to access your data due to service outages
* Data deletion due to account termination or retention policies
* Data loss due to user error or account compromise
### Service Interruptions
[Section titled “Service Interruptions”](#service-interruptions)
We are not liable for service interruptions, downtime, or degraded performance caused by:
* Scheduled or emergency maintenance
* Third-party service failures (hosting, DNS, network providers)
* Force majeure events (natural disasters, wars, strikes)
* Cyber attacks or security incidents
* User actions or misconfigurations
### Limited Liability Amount
[Section titled “Limited Liability Amount”](#limited-liability-amount)
**To the maximum extent permitted by Croatian law**, our total liability for any claims arising from these Terms or the service is limited to:
* The fees you paid to Kopai in the 12 months prior to the claim, OR
* €100 (whichever is greater)
Currently, as the service is free, this amount is €100.
### Exclusions
[Section titled “Exclusions”](#exclusions)
Nothing in these Terms excludes or limits our liability for:
* Death or personal injury caused by negligence
* Fraud or fraudulent misrepresentation
* Any liability that cannot be excluded by Croatian law
### Croatian Consumer Law
[Section titled “Croatian Consumer Law”](#croatian-consumer-law)
For Croatian consumers, your statutory rights under Croatian consumer protection law (OG 19/2022) are not affected by these limitations.
### Third-Party Services
[Section titled “Third-Party Services”](#third-party-services)
We are not responsible for:
* Actions or failures of third-party processors (Hetzner, Supabase, Resend, Cloudflare, Stripe)
* Third-party integrations or applications
* Content or services accessed through external links
## 10. Termination
[Section titled “10. Termination”](#10-termination)
### Termination by You
[Section titled “Termination by You”](#termination-by-you)
You may terminate your Kopai account at any time by:
1. Logging into your dashboard
2. Navigating to account settings
3. Selecting “Delete Account”
4. Confirming deletion
**Immediate effects:**
* You lose access to your account and data
* API tokens are immediately revoked
* Telemetry data ingestion stops
**Data retention:**
* Your data is retained for **30 days** after account deletion
* During this period, you can contact us to restore your account
* After 30 days, all data is **permanently deleted** and cannot be recovered
**Requesting immediate deletion:**
* Contact to request immediate data deletion
* We will process requests within 30 days
### Termination by Kopai
[Section titled “Termination by Kopai”](#termination-by-kopai)
We may suspend or terminate your account if:
* You violate these Terms of Service
* You engage in illegal activity
* Your account poses a security threat
* You abuse the service (excessive API usage, etc.)
* Your account is inactive for 2+ years
**Notice period:**
* We will provide **7 days’ notice** via email before termination
* **Immediate termination** without notice for:
* Illegal activity
* Security threats
* Severe Terms violations (reselling, hacking attempts)
**After termination:**
* You lose access to your account immediately
* Data is retained for 30 days, then permanently deleted
* You may request a data export before deletion
### Data Export Before Termination
[Section titled “Data Export Before Termination”](#data-export-before-termination)
Before deleting your account, you can export your data:
* **Via API**: Use your access tokens to query and export telemetry data
* **Via Dashboard**: Download data from the web interface
* **Request assistance**: Contact for help with data export
### Fees After Termination
[Section titled “Fees After Termination”](#fees-after-termination)
Refunds are available only as described in Section 5 (Cancellation and Refunds). Otherwise, fees already paid are not refunded, including when:
* You terminate partway through a billing period
* We suspend or terminate your account for violating these Terms
### Effect on Agreements
[Section titled “Effect on Agreements”](#effect-on-agreements)
Termination does not affect:
* Provisions that should survive (liability limitations, dispute resolution, IP rights)
* Your obligations for activity that occurred before termination
* Our right to retain data as required by law
## 11. Consumer Rights (Croatian Consumers)
[Section titled “11. Consumer Rights (Croatian Consumers)”](#11-consumer-rights-croatian-consumers)
For Croatian consumers, you have the right to withdraw from the contract within 14 days, in accordance with the Croatian Consumer Protection Act (OG 19/2022).
To exercise withdrawal rights, contact:
## 12. Dispute Resolution
[Section titled “12. Dispute Resolution”](#12-dispute-resolution)
### Initial Contact
[Section titled “Initial Contact”](#initial-contact)
If you have a dispute or complaint about Kopai:
**Step 1**: Contact us at with:
* Description of the issue
* Relevant account information
* Desired resolution
* Any supporting documentation
**Step 2**: We will respond within **5 business days** and attempt to resolve the issue
**Step 3**: If unresolved, we will engage in good-faith negotiations for **30 days**
### Mediation (Optional)
[Section titled “Mediation (Optional)”](#mediation-optional)
If direct negotiation fails, we encourage mediation before litigation:
* Either party may propose mediation
* Mediator selected by mutual agreement
* Costs split equally between parties
* Mediation conducted in Zagreb, Croatia
### Out-of-Court Dispute Resolution
[Section titled “Out-of-Court Dispute Resolution”](#out-of-court-dispute-resolution)
**For EU consumers**, the European Commission publishes a list of approved consumer dispute resolution bodies in each Member State:
* Website:
* Purpose: Resolve disputes without going to court
### Governing Law and Jurisdiction
[Section titled “Governing Law and Jurisdiction”](#governing-law-and-jurisdiction)
**Governing Law**: These Terms are governed by the **laws of Croatia**.
**Jurisdiction**:
* **Commercial users**: Disputes will be resolved exclusively by the **Commercial Court in Zagreb** (Trgovački sud u Zagrebu)
* **Consumers**: Croatian consumers may bring claims in their local court as permitted by Croatian law
### Class Action Waiver
[Section titled “Class Action Waiver”](#class-action-waiver)
To the extent permitted by Croatian law, you agree to resolve disputes individually, not as part of a class or collective action.
### Complaints to Authorities
[Section titled “Complaints to Authorities”](#complaints-to-authorities)
You may also file complaints with:
* **Consumer Protection Authority**: Croatian Consumer Protection Agency
* **Data Protection Authority**: Croatian Personal Data Protection Agency (AZOP) at
* **Competition Authority**: Croatian Competition Agency (AZTN)
### Exceptions
[Section titled “Exceptions”](#exceptions)
The dispute resolution procedures above do not apply to:
* Intellectual property claims
* Urgent injunctive relief
* Small claims court proceedings (below statutory thresholds)
## 13. Governing Law
[Section titled “13. Governing Law”](#13-governing-law)
These terms are governed by the laws of Croatia.
**Jurisdiction**: The courts of Croatia have exclusive jurisdiction over disputes arising from these terms.
## 14. Changes to Terms
[Section titled “14. Changes to Terms”](#14-changes-to-terms)
### Our Right to Modify
[Section titled “Our Right to Modify”](#our-right-to-modify)
We may update these Terms of Service at any time to:
* Reflect changes in our service features
* Comply with legal or regulatory requirements
* Address security or technical issues
* Clarify existing terms
* Introduce new pricing or service tiers
### Notification of Changes
[Section titled “Notification of Changes”](#notification-of-changes)
**Material changes** (affecting your rights or obligations):
* **Email notification**: Sent to your registered email address
* **Advance notice**: At least **30 days** before changes take effect
* **On-site notice**: Banner or notification when you log in
* **Updated “Last updated” date** at the top of this page
**Non-material changes** (clarifications, formatting, minor corrections):
* Updated “Last updated” date
* No email notification required
* Take effect immediately upon publication
### Your Options
[Section titled “Your Options”](#your-options)
When we notify you of material changes, you may:
1. **Accept changes**: Continue using the service (constitutes acceptance)
2. **Reject changes**: Delete your account before the effective date
3. **Request clarification**: Contact us at
**Continued use** of Kopai after the effective date constitutes your acceptance of the updated Terms.
### Withdrawal After Changes
[Section titled “Withdrawal After Changes”](#withdrawal-after-changes)
If you disagree with material changes:
* You have **30 days** from notification to delete your account
* No fees will be charged for the remainder of your billing period (when pricing is introduced)
* You can export your data before deletion
### Version History
[Section titled “Version History”](#version-history)
We maintain a version history of these Terms:
* Current version: October 8, 2026
* Previous versions available upon request at
### Croatian Consumer Rights
[Section titled “Croatian Consumer Rights”](#croatian-consumer-rights)
For Croatian consumers, changes to material terms may provide additional withdrawal rights under the Consumer Protection Act (OG 19/2022). Contact us for questions about your specific rights.
## 15. Contact
[Section titled “15. Contact”](#15-contact)
For questions about these terms:
***
*These terms comply with Croatian law and EU regulations.*
*Note: We are assessing applicability of the Digital Services Act (EU 2022/2065). Updates will be published if required.*
# Privacy Policy
> How NIBBLE d.o.o. (Kopai) collects, uses, and protects your data
*Last updated: October 8, 2026*
## 1. Data Controller
[Section titled “1. Data Controller”](#1-data-controller)
**NIBBLE društvo s ograničenom odgovornošću za usluge** Prilesje 18, 10000 Zagreb, Croatia OIB: 96011589399 Email:
## 2. Supervisory Authority
[Section titled “2. Supervisory Authority”](#2-supervisory-authority)
Croatian Personal Data Protection Agency (Agencija za zaštitu osobnih podataka) Selska cesta 136 HR-10 000 Zagreb Croatia Website:
## 3. Data We Collect
[Section titled “3. Data We Collect”](#3-data-we-collect)
We collect the following types of data:
### Account Information
[Section titled “Account Information”](#account-information)
* Email address
* Organization name
* User ID (generated by authentication system)
### Authentication Data
[Section titled “Authentication Data”](#authentication-data)
Managed by Supabase (our authentication provider):
* Email address
* Password hashes (never stored in plain text)
* Session tokens
* Account creation and last login timestamps
### Billing Information
[Section titled “Billing Information”](#billing-information)
Only if you subscribe to a paid plan (not yet available — see the [Terms of Service](/terms/)):
* Billing name, billing address and email
* VAT ID, if you enter one
* Payment card details — collected and stored by Stripe, never by us (we see only the card brand, last four digits and expiry date)
* Subscription, invoice, payment and refund history
### Tenant Configuration
[Section titled “Tenant Configuration”](#tenant-configuration)
* Allowed CORS origins (URLs authorized to access your data)
* API token names and descriptions
* Token types (backend or frontend)
* Token expiration settings
### OpenTelemetry Data
[Section titled “OpenTelemetry Data”](#opentelemetry-data)
Traces, logs, and metrics sent by your applications:
* Application telemetry data (spans, events, measurements)
* Service names and attributes
* Timestamps and trace IDs
* Custom attributes you include in your telemetry
### Marketing-Site Usage Analytics
[Section titled “Marketing-Site Usage Analytics”](#marketing-site-usage-analytics)
Self-hosted OpenTelemetry Real User Monitoring (RUM) on `kopai.app` and its subdomains. **Disabled until you click “Accept All” in the cookie banner.** When enabled, we capture:
* A random tab-scoped session identifier (`session.id`, UUID v4 stored in `sessionStorage`, deleted on tab close)
* Pages visited and their order; clicks on Sign-up / Demo call-to-action buttons
* Web Vitals (LCP, FCP, CLS, INP, TTFB) and the list of static resources each page loaded
* Browser metadata reported by your browser (user-agent string, language, platform, mobile flag, screen and viewport dimensions, device pixel ratio, network class)
* Coarse Cloudflare-derived attributes about your network location (`cloud.region` from ISO country code, `cloudflare.colo` from Cloudflare data centre code) — never your IP itself
For an exhaustive list, see the [Cookie Policy](/cookies/). We **do not** capture form contents, search queries, keystrokes, mouse movements, scroll positions, or JavaScript stack traces from the marketing site.
### Google Analytics (Marketing Site)
[Section titled “Google Analytics (Marketing Site)”](#google-analytics-marketing-site)
Google Analytics 4 on `kopai.app` (marketing site, blog and docs). **Not loaded until you click “Accept All” in the cookie banner** — before that, your browser sends nothing to Google. When enabled, Google Analytics collects:
* A random client identifier stored in the `_ga` and `_ga_*` cookies
* Pages visited, referrer, and engagement events (for example scrolls and outbound link clicks)
* Device, browser, operating system and screen information
* Approximate location (country and city), derived by Google from your IP address
We send Google the advertising consent signals `ad_storage`, `ad_user_data` and `ad_personalization` as **denied**, so this data is not used for advertising or ad personalisation. See the [Cookie Policy](/cookies/) for cookie names and durations.
### Authenticated Dashboard Telemetry
[Section titled “Authenticated Dashboard Telemetry”](#authenticated-dashboard-telemetry)
The Kopai dashboard at `app.kopai.app` can run the same OpenTelemetry browser SDK. **It is off until you click “Accept” in the dashboard’s analytics banner**, or turn on **Settings → Privacy → Analytics**. The dashboard is a separate site from `kopai.app`, so it asks separately; your choice on one does not apply to the other. Until you accept, the SDK is not even downloaded. When enabled, it captures page loads, requests, clicks and Web Vitals, and tags them with:
* `session.id` — same tab-scoped UUID as on the marketing site
* `enduser.id` — your Supabase user UUID (the `sub` claim of your access-token JWT). This identifier is stable per registered user account and lets us answer support questions like “what request did this specific user make”.
* `tenant.id` — the tenant whose data the dashboard tab is currently viewing
* `user.roles` — your role within that tenant (`["owner"]`, `["admin"]`, or `["member"]`)
* Browser, viewport, network, and Cloudflare attributes as listed above
`enduser.id` and `tenant.id` are personal data under GDPR. They are processed only with your consent (Art. 6(1)(a)), to measure dashboard performance and answer your support requests. They are **not** stamped on telemetry while you are on the public login page.
You can withdraw consent at any time by turning off **Settings → Privacy → Analytics**: the SDK stops immediately, the `session.id` is deleted from your browser, and no further dashboard analytics are sent.
### Newsletter Subscription
[Section titled “Newsletter Subscription”](#newsletter-subscription)
If you subscribe to product updates on `kopai.app`, we collect:
* Your email address
* The signup source (for example “Website”)
* Whether you open our emails or click their links, and when you unsubscribe (recorded by our email provider, Loops)
### Security and Technical Data
[Section titled “Security and Technical Data”](#security-and-technical-data)
* IP addresses (visible to Cloudflare for security, rate limiting, and fraud prevention; not stored as a span attribute by our analytics SDK)
* User agent strings (from HTTP request logs)
* Request logs and audit trails
* API access patterns
## 4. Legal Basis for Processing
[Section titled “4. Legal Basis for Processing”](#4-legal-basis-for-processing)
We process your personal data based on the following legal grounds under GDPR:
### Contract Performance (Art. 6(1)(b) GDPR)
[Section titled “Contract Performance (Art. 6(1)(b) GDPR)”](#contract-performance-art-61b-gdpr)
Processing necessary to provide our observability services:
* Account creation and authentication
* OpenTelemetry data ingestion, storage, and visualization
* API token management
* Dashboard access and data queries
* Paid subscriptions: payment processing and invoicing
### Legal Obligation (Art. 6(1)(c) GDPR)
[Section titled “Legal Obligation (Art. 6(1)(c) GDPR)”](#legal-obligation-art-61c-gdpr)
* Keeping invoices and accounting records as required by Croatian tax and accounting law
### Legitimate Interests (Art. 6(1)(f) GDPR)
[Section titled “Legitimate Interests (Art. 6(1)(f) GDPR)”](#legitimate-interests-art-61f-gdpr)
Processing necessary for our legitimate business interests:
* Security monitoring and fraud prevention
* Service performance optimization
* Technical troubleshooting and support
* Product improvements and feature development
### Consent (Art. 6(1)(a) GDPR)
[Section titled “Consent (Art. 6(1)(a) GDPR)”](#consent-art-61a-gdpr)
Processing based on your explicit consent:
* Analytics cookies, Real User Monitoring (RUM) and Google Analytics on the marketing site
* Dashboard analytics (browser telemetry on `app.kopai.app`)
* Marketing communications: product-update emails, only if you subscribe (via Loops)
You can withdraw consent at any time by adjusting your cookie settings or contacting us.
## 5. Purpose of Processing
[Section titled “5. Purpose of Processing”](#5-purpose-of-processing)
We use your personal data for the following purposes:
### Service Delivery
[Section titled “Service Delivery”](#service-delivery)
* **OpenTelemetry data ingestion**: Receive traces, logs, and metrics via OTLP (gRPC and HTTP protocols)
* **Data storage and management**: Store your telemetry data securely in ClickHouse
* **Visualization and dashboards**: Display your observability data in web-based dashboards
* **Query and analysis**: Enable you to search, filter, and analyze your telemetry data
### Account Management
[Section titled “Account Management”](#account-management)
* **Authentication**: Verify your identity and manage secure access to your account
* **API token management**: Generate and manage backend and frontend access tokens
* **CORS configuration**: Control which frontend origins can access your data
* **User dashboard access**: Provide access to ClickHouse credentials and token information
### Communications
[Section titled “Communications”](#communications)
* **Service notifications**: Send transactional emails via Resend (password resets, account changes)
* **Technical updates**: Notify you about service status, maintenance, or security issues
* **Support responses**: Reply to your questions and support requests
* **Product updates**: Send occasional newsletter emails via Loops if you subscribed — every email has an unsubscribe link
### Service Improvement
[Section titled “Service Improvement”](#service-improvement)
* **Performance monitoring**: Track website performance using OpenTelemetry RUM
* **Marketing-site statistics**: Measure visits and traffic sources with Google Analytics (consent only)
* **Error tracking**: Identify and fix technical issues affecting user experience
* **Usage analytics**: Understand how users interact with our service to improve features
* **Product development**: Inform decisions about new features and improvements
### Security and Compliance
[Section titled “Security and Compliance”](#security-and-compliance)
* **Fraud prevention**: Detect and prevent unauthorized access or abuse
* **Rate limiting**: Prevent excessive API usage that could degrade service performance
* **Security monitoring**: Monitor for suspicious activity and potential security threats
* **Audit logging**: Maintain records for security and compliance purposes
## 6. Data Retention
[Section titled “6. Data Retention”](#6-data-retention)
We retain your personal data only as long as necessary for the purposes outlined in this policy:
### OpenTelemetry Data (Traces, Logs, Metrics)
[Section titled “OpenTelemetry Data (Traces, Logs, Metrics)”](#opentelemetry-data-traces-logs-metrics)
Retention period **varies by pricing tier** (to be defined in pricing plans):
* Free tier: 7 days (planned)
* Paid tiers: 30-365 days depending on plan (to be defined)
* You can request earlier deletion of your telemetry data at any time
The free and paid tiers above are planned: billing is not live yet, so telemetry on those tiers is not deleted on a schedule today.
### Account Information
[Section titled “Account Information”](#account-information-1)
* **Active accounts**: Retained while your account is active
* **Deleted accounts**: Data retained for 30 days after account deletion, then permanently deleted
* **Inactive accounts**: Accounts inactive for 2+ years may be deleted with 30-day email notice
### Access Tokens
[Section titled “Access Tokens”](#access-tokens)
* **Active tokens**: Retained until revoked or expired
* **Revoked tokens**: Immediately deleted from active database
* **Expired tokens**: Deleted 90 days after expiration
### Authentication Data
[Section titled “Authentication Data”](#authentication-data-1)
Managed by Supabase according to their retention policy:
* Session tokens expire based on configured session length
* Login history retained while account is active
### Logs and Security Data
[Section titled “Logs and Security Data”](#logs-and-security-data)
* **Application logs**: 90 days
* **Audit logs**: 1 year for security and compliance
* **Access logs**: 90 days
### Google Analytics
[Section titled “Google Analytics”](#google-analytics)
* **User- and event-level data**: 2 months, then deleted automatically by Google
* **`_ga` / `_ga_*` cookies**: up to 2 years in your browser, or until you click **Reject analytics** in the footer, which deletes them
### Billing Records
[Section titled “Billing Records”](#billing-records)
* **Invoices and payment records**: Kept for 11 years, as required by Croatian accounting law
### Email Communications
[Section titled “Email Communications”](#email-communications)
* **Transactional emails**: Records retained 1 year for support purposes
* **Support correspondence**: Retained 2 years after case closure
* **Newsletter subscription**: Kept until you unsubscribe. After that we keep only your email address marked as unsubscribed, so you are never emailed again; email to have it deleted entirely
You can request deletion of your data at any time by contacting . We will comply with deletion requests within 30 days, except where retention is required by law.
## 7. Third-Party Processors
[Section titled “7. Third-Party Processors”](#7-third-party-processors)
We work with the following third-party service providers who process personal data on our behalf:
| Processor | Purpose | Location | Data Shared |
| ----------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Hetzner** | ClickHouse database hosting | Germany, Finland (EU) | OpenTelemetry data (traces, logs, metrics), tenant metadata, organization names |
| **Supabase** | User authentication | EU (Frankfurt region) | Email addresses, password hashes, user IDs, session tokens |
| **Resend** | Transactional email delivery | USA (GDPR-compliant via EU-U.S. DPF) | Email addresses, user names, email content |
| **Cloudflare** | CDN, static site hosting, TLS termination | Global edge network | IP addresses, user agent strings, page requests, cookies |
| **Stripe** (Stripe Payments Europe, Ltd.) | Payment processing, subscriptions, invoices — only for paid plans | EU (Ireland), with transfers to the USA (EU-U.S. DPF) | Billing name, address and email, VAT ID, payment card details, payment history |
| **Loops** (Astrodon Corporation) | Newsletter email delivery, only if you subscribe | USA (Standard Contractual Clauses) | Email address, signup source, email open and click activity |
| **Google** (Google Ireland Limited) | Marketing-site analytics (Google Analytics 4), only with your consent | EU, with transfers to the USA (EU-U.S. DPF) | Pseudonymous client ID, pages visited, referrer, device and browser data, IP address (used for approximate location) |
### Data Processing Agreements
[Section titled “Data Processing Agreements”](#data-processing-agreements)
We ensure all processors comply with GDPR through Data Processing Agreements (DPAs) that include:
* Appropriate technical and organizational security measures
* Confidentiality obligations
* Assistance with data subject requests
* Data breach notification procedures
* Restrictions on sub-processing
### Processor Security
[Section titled “Processor Security”](#processor-security)
All processors have been evaluated for:
* GDPR compliance and certifications
* Security measures (encryption, access controls)
* Data residency and international transfer safeguards
* Incident response capabilities
* Regular security audits
## 8. International Transfers
[Section titled “8. International Transfers”](#8-international-transfers)
Most of your data is stored and processed within the European Union:
### Primary Data Storage (EU)
[Section titled “Primary Data Storage (EU)”](#primary-data-storage-eu)
* **Hetzner (Germany, Finland)**: All ClickHouse databases hosting your OpenTelemetry data
* **Supabase (Frankfurt)**: User authentication and account data
### Transfers Outside EU/EEA
[Section titled “Transfers Outside EU/EEA”](#transfers-outside-eueea)
#### Resend (United States)
[Section titled “Resend (United States)”](#resend-united-states)
**Purpose**: Transactional email delivery (password resets, notifications)
**Data Transferred**: Email addresses, user names, email content
**Legal Basis**: Resend is certified under the **EU-U.S. Data Privacy Framework (DPF)**. The DPF provides adequate data protection safeguards as recognized by the European Commission. Resend’s DPF certification ensures:
* Compliance with EU data protection principles
* Independent dispute resolution mechanisms
* Enforcement by the U.S. Federal Trade Commission
* Annual recertification requirements
Learn more: [Resend Privacy Policy](https://resend.com/legal/privacy-policy)
#### Cloudflare (Global Network)
[Section titled “Cloudflare (Global Network)”](#cloudflare-global-network)
**Purpose**: Content delivery, DDoS protection, TLS termination
**Data Transferred**: IP addresses, user agent strings, cookies, page requests
**Legal Basis**: Cloudflare processes data across its global edge network but maintains GDPR compliance through:
* EU Data Processing Addendum
* Standard Contractual Clauses (SCCs)
* EU data residency options for sensitive data
Learn more: [Cloudflare GDPR Compliance](https://www.cloudflare.com/trust-hub/gdpr/)
#### Stripe (United States)
[Section titled “Stripe (United States)”](#stripe-united-states)
**Purpose**: Processing payments for paid plans
**Data Transferred**: Billing name, address and email, VAT ID, payment card details, payment history
**Legal Basis**: Stripe Payments Europe, Ltd. (Ireland) processes payments for EU customers. Data may be transferred to Stripe, Inc. in the United States, which is certified under the **EU-U.S. Data Privacy Framework (DPF)**, with Standard Contractual Clauses as a fallback.
Learn more: [Stripe Privacy Policy](https://stripe.com/privacy)
#### Loops (United States)
[Section titled “Loops (United States)”](#loops-united-states)
**Purpose**: Sending product-update emails to people who subscribe on our website
**Data Transferred**: Email address, signup source, email open and click activity
**Legal Basis**: Loops (Astrodon Corporation) processes this data on our behalf under its Data Processing Agreement, which uses Standard Contractual Clauses for transfers outside the EEA.
Learn more: [Loops Data Processing Agreement](https://loops.so/dpa)
#### Google (United States)
[Section titled “Google (United States)”](#google-united-states)
**Purpose**: Marketing-site analytics (Google Analytics 4), only after you consent
**Data Transferred**: Pseudonymous client ID, pages visited, referrer, device and browser data, IP address (used for approximate location)
**Legal Basis**: Google Ireland Limited processes this data on our behalf under Google’s data processing terms. Data may be transferred to Google LLC in the United States, which is certified under the **EU-U.S. Data Privacy Framework (DPF)**, with Standard Contractual Clauses as a fallback.
Learn more: [How Google uses information from sites that use its services](https://policies.google.com/technologies/partner-sites)
### Your Rights Regarding International Transfers
[Section titled “Your Rights Regarding International Transfers”](#your-rights-regarding-international-transfers)
You have the right to:
* Request information about international transfers affecting your data
* Object to transfers where appropriate safeguards are not in place
* Request that your data be processed only within the EU (may limit service availability)
Contact for questions about international data transfers.
## 9. Your Rights Under GDPR
[Section titled “9. Your Rights Under GDPR”](#9-your-rights-under-gdpr)
You have the following rights:
* **Right to access** (Art. 15): Request copies of your personal data
* **Right to rectification** (Art. 16): Request correction of inaccurate data
* **Right to erasure** (Art. 17): Request deletion of your data (“right to be forgotten”)
* **Right to restriction** (Art. 18): Request limitation of data processing
* **Right to data portability** (Art. 20): Receive your data in machine-readable format
* **Right to object** (Art. 21): Object to processing based on legitimate interests
* **Right to withdraw consent**: Where processing is based on consent
To exercise these rights, contact:
## 10. Data Security
[Section titled “10. Data Security”](#10-data-security)
We implement appropriate technical and organizational measures to protect your data, including:
* Encryption in transit (TLS)
* Encryption at rest
* Access controls
* Regular security assessments
## 11. Cookies and Tracking
[Section titled “11. Cookies and Tracking”](#11-cookies-and-tracking)
We use cookies for authentication and service functionality. For complete details, see our [Cookie Policy](/cookies/).
### Essential Cookies (Required)
[Section titled “Essential Cookies (Required)”](#essential-cookies-required)
These cookies are necessary for the service to function and cannot be disabled:
* **Supabase authentication cookies** (`sb-*`): Required for secure login and session management
* **CSRF protection cookies** (`__Secure-csrf`): Security tokens to prevent cross-site request forgery
* **Cloudflare security cookies** (`__cf_bm`): Bot management and DDoS protection
### Analytics Storage (Optional - Requires Consent)
[Section titled “Analytics Storage (Optional - Requires Consent)”](#analytics-storage-optional---requires-consent)
These browser-storage entries help us improve the marketing site (technically `localStorage` / `sessionStorage` rather than HTTP cookies, but treated as cookies under the ePrivacy Directive):
* **`kopai-cookie-consent`** (`localStorage`): records your cookie banner choice so we don’t ask on every page.
* **`kopai-otel-session`** (`sessionStorage`): tab-scoped random UUID v4 (`session.id`) used to correlate analytics events from the same browsing session. Deleted automatically when you close the tab.
* **`_ga` / `_ga_*`** (HTTP cookies, set by Google Analytics): pseudonymous client ID and session state for our Google Analytics property. Up to 2 years.
For the full list of attributes captured by the analytics SDK while consent is granted (page paths, Web Vitals, browser/viewport/network metadata, Cloudflare-derived `cloud.region` and `cloudflare.colo`), see the [Cookie Policy](/cookies/).
### Managing Your Cookie Preferences
[Section titled “Managing Your Cookie Preferences”](#managing-your-cookie-preferences)
You can control analytics at any time:
* Use the cookie consent banner shown on your first visit.
* Click **Reject analytics** in the footer of every page to withdraw consent in a single click — the SDK shuts down immediately, the session ID is deleted, Google Analytics stops and its `_ga` cookies are deleted, and no further analytics events leave your browser.
* Click **Cookie Settings** in the footer to reset and re-prompt the banner.
* Configure your browser to block third-party cookies.
**Note**: Blocking essential cookies will prevent you from logging in to your account.
For detailed information about each cookie, duration, and purpose, see our full [Cookie Policy](/cookies/).
## 12. Children’s Data
[Section titled “12. Children’s Data”](#12-childrens-data)
We do not knowingly collect data from individuals under 16 years of age without parental consent, in accordance with Croatian GDPR implementation.
## 13. Changes to This Policy
[Section titled “13. Changes to This Policy”](#13-changes-to-this-policy)
We will notify users of material changes to this privacy policy via email and by updating the “Last updated” date.
## 14. Contact and Complaints
[Section titled “14. Contact and Complaints”](#14-contact-and-complaints)
For privacy questions:
To file a complaint with the Croatian DPA: Croatian Personal Data Protection Agency Selska cesta 136, HR-10 000 Zagreb Website:
***
*This privacy policy complies with the General Data Protection Regulation (GDPR) and Croatian implementation law.*
*Note: We are assessing applicability of the Digital Services Act (EU 2022/2065). Updates will be published if required.*