TrackLane docs · API v1

Справочник Ingest API

Ingest API — это точка входа для всей телеметрии, которую ваши приложения передают в TrackLane: метрик производительности, событий, логов и спанов трассировки. Данные принимаются граничными узлами (Edge PoP), ближайшими к клиенту, и асинхронно доставляются в Analytics Engine.

Базовый URL
https://in.tracklane.space
Версия протокола
TLP/2 · API v1
Формат
JSON · zstd / gzip

Если вы используете официальный SDK (@tracklane/sdk для JavaScript, tracklane-python, tracklane-go), формирование пакетов, сжатие, подпись и повторная доставка выполняются автоматически. Этот справочник описывает протокол на уровне HTTP — он понадобится, если вы пишете собственный клиент, отправляете данные с встраиваемых устройств или проксируете телеметрию через свой бэкенд.

Быстрый старт

Создайте проект в консоли, скопируйте ingest-ключ из раздела Настройки → Ключи доступа и установите SDK:

terminal
npm install @tracklane/sdk

Инициализируйте клиент как можно раньше — до загрузки остального кода приложения, чтобы SDK успел зарегистрировать наблюдатели Performance API:

src/main.ts
import { TrackLane } from '@tracklane/sdk';

export const tl = TrackLane.init({
  publishableKey: 'tl_pub_8Kq2vN4xR7mT1pLz',   // публичный ключ проекта
  signingKeyEndpoint: '/api/telemetry/key',     // выдаёт эфемерный ключ подписи
  service: 'storefront-web',
  environment: 'production',
  release: '2026.10.3',
  batch: { maxEvents: 500, maxDelayMs: 5000 },
});

Аутентификация

Каждый запрос к Ingest API аутентифицируется ключом проекта в заголовке Authorization. TrackLane использует два типа ключей:

  • tl_pub_… — публичный ключ. Безопасно встраивается в браузерные и мобильные приложения. Разрешает только запись в конкретный проект и требует подписи пакета эфемерным ключом.
  • tl_sk_… — секретный ключ. Используется только на серверах. Позволяет подписывать пакеты напрямую и выпускать эфемерные ключи подписи для клиентов через POST /v1/signing-keys.

Эфемерный ключ подписи действует 15 минут и привязан к session ID. Ваш бэкенд запрашивает его у TrackLane и передаёт клиенту — так секретный ключ никогда не покидает сервер.

Важно. Никогда не размещайте ключ tl_sk_… в клиентском коде или в публичных репозиториях. Скомпрометированный ключ отзывается в консоли за одну секунду, и новые запросы с ним будут отклоняться кодом 401.

Протокол TLP/2

TrackLane Lane Protocol — это асинхронный протокол передачи пакетов поверх HTTPS. Клиент накапливает события в буфере, формирует пакет (batch) и отправляет его одним запросом. Граничный узел проверяет подпись, записывает пакет в локальный журнал упреждающей записи (WAL) и сразу отвечает 202 Accepted. Валидация отдельных событий, обогащение и агрегация выполняются уже после ответа.

  1. 01
    buffer
    ≤ 500 событий / 5 с
  2. 02
    hash + sign
    SHA-256, HMAC
  3. 03
    POST /v1/ingest
    zstd, HTTP/3
  4. 04
    202 Accepted
    ≈ 9 мс на Edge

Ключевые свойства протокола:

  • Неблокирующая доставка. Ответ не зависит от состояния аналитического ядра: даже при его недоступности узел принимает данные и хранит журнал до 72 часов.
  • Упорядоченность внутри сессии. Пакеты одной сессии нумеруются полем session.seq; пропуски обнаруживаются на стороне сервера по цепочке хэшей.
  • Идемпотентность. Повторная отправка пакета с тем же batch_id в течение 24 часов не приводит к дублированию данных.
  • Транспорт. HTTP/1.1, HTTP/2 и HTTP/3 (QUIC). Для фоновой отправки при закрытии вкладки поддерживается navigator.sendBeacon (до 64 КБ).

POST/v1/ingest

Принимает один пакет телеметрии. Тело запроса — JSON-объект, опционально сжатый zstd или gzip. Максимальный размер после распаковки зависит от тарифа: 256 КБ, 1 МБ или 5 МБ.

Заголовки запроса

ЗаголовокОбязателенОписание
AuthorizationдаBearer tl_pub_… или Bearer tl_sk_…
Content-Typeдаapplication/json
Content-Encodingнетzstd, gzip или identity
X-TL-SessionдаИдентификатор сессии; должен совпадать с session.id в теле
X-TL-Signatureдаt=<unix>,v1=<hmac_hex> — см. подпись пакета
X-TL-ProtocolнетВерсия протокола, по умолчанию 2

Формат пакета

Пакет состоит из четырёх блоков: метаданных пакета, контекста ресурса (сервис, релиз, окружение), сессии и массива событий. Блок integrity содержит хэш массива событий и хэш предыдущего пакета сессии — это позволяет серверу обнаружить искажение данных и пропущенные пакеты.

POST /v1/ingest — тело запроса
{
  "protocol": 2,
  "batch_id": "bat_01JA8ZK4T3M9Q2XW7RVN5HCD6E",
  "sent_at": "2026-10-15T09:41:27.384Z",
  "resource": {
    "service": "storefront-web",
    "environment": "production",
    "release": "2026.10.3",
    "sdk": { "name": "@tracklane/sdk", "version": "4.12.0" }
  },
  "session": {
    "id": "ses_01JA8YQ7C5W2B8N4K6TXMZ3RFP",
    "started_at": "2026-10-15T09:32:04.112Z",
    "seq": 14,
    "page_views": 0,
    "device": { "type": "desktop", "os": "macOS 15.1", "browser": "Chrome 130" },
    "geo_hint": "DE"
  },
  "events": [
    {
      "type": "metric",
      "name": "web.lcp",
      "ts": "2026-10-15T09:41:22.051Z",
      "value": 1184.6,
      "unit": "ms",
      "attrs": { "route": "/catalog/:slug", "element": "img.hero", "connection": "4g" }
    },
    {
      "type": "metric",
      "name": "web.inp",
      "ts": "2026-10-15T09:41:24.907Z",
      "value": 96,
      "unit": "ms",
      "attrs": { "route": "/catalog/:slug", "interaction": "click", "target": "button.add-to-cart" }
    },
    {
      "type": "span",
      "name": "POST /api/cart/items",
      "ts": "2026-10-15T09:41:25.002Z",
      "duration_ms": 212.4,
      "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
      "span_id": "00f067aa0ba902b7",
      "status": "ok",
      "attrs": { "http.status_code": 201, "http.response_size": 1840 }
    }
  ],
  "integrity": {
    "alg": "sha256",
    "canonicalization": "jcs",
    "events_count": 3,
    "events_hash": "8c1f0e6b42d7a95e3f60c2b18d4a7e19f53b0c86d2e17a4f9b3c05e81d6a2f74",
    "prev_batch_hash": "e3b4c2a17f9d05864b1ac7e02d93f58a6c41b70e2d895fa3c16e07b24d9a8c51"
  }
}

Поля верхнего уровня

ПолеТипОписание
batch_idstringУникальный идентификатор пакета: префикс bat_ + ULID. Ключ идемпотентности.
sent_atRFC 3339Время отправки по часам клиента. Используется для коррекции дрейфа часов событий.
resourceobjectСервис, окружение и релиз. Применяется ко всем событиям пакета.
sessionobjectКонтекст сессии, см. идентификаторы сессий.
eventsarrayОт 1 до 5 000 событий. Порядок внутри массива не важен.
integrityobjectХэши для проверки целостности, см. подпись.

Типы событий

Поле type определяет схему события и полосу, в которую оно будет направлено на граничном узле.

metric

Числовое измерение: name, value, unit. Сворачивается в DDSketch-гистограммы с окном 10 секунд.

span

Участок трассировки в формате W3C Trace Context: trace_id, span_id, duration_ms.

log

Структурированная запись: level, message и произвольные атрибуты до 64 КБ.

error

Исключение со стеком: error.type, error.message, stack. Группируется по отпечатку.

Имена метрик должны соответствовать шаблону ^[a-z][a-z0-9_.]{1,127}$. Атрибуты — плоский объект, не более 64 ключей на событие; значения — строки до 1 024 символов, числа или логические значения.

Идентификаторы сессий

TrackLane использует бесстраничный учёт сессий: сессия не привязана к загрузке страницы и не хранится в cookie. Идентификатор генерируется на клиенте и живёт в памяти приложения (или в sessionStorage для восстановления после перезагрузки вкладки).

  • Формат: префикс ses_ и 26 символов ULID в алфавите Crockford Base32. Первые 10 символов кодируют время создания в миллисекундах — это позволяет сортировать сессии без отдельного индекса.
  • Продление: любая активность пользователя — ввод, сетевой запрос, переход в SPA, возврат вкладки в видимое состояние — продлевает сессию.
  • Ротация: новый ID выпускается после 30 минут неактивности (настраивается sessionTimeoutMs) или через 24 часа непрерывной работы.
  • Нумерация: поле session.seq монотонно растёт с каждым пакетом, начиная с 0. При ротации счётчик сбрасывается.
session.ts — генерация и ротация
import { ulid } from 'ulid';

const TIMEOUT_MS = 30 * 60 * 1000;

let session = { id: `ses_${ulid()}`, startedAt: Date.now(), lastActive: Date.now(), seq: 0 };

export function currentSession() {
  const now = Date.now();
  if (now - session.lastActive > TIMEOUT_MS) {
    session = { id: `ses_${ulid()}`, startedAt: now, lastActive: now, seq: 0 };
  }
  session.lastActive = now;
  return session;
}

export function nextSeq() {
  return currentSession().seq++;
}

Целостность и подпись пакетов

Каждый пакет защищён двумя механизмами: хэшем содержимого, который гарантирует, что события не были изменены при передаче или в промежуточных прокси, и HMAC-подписью, которая подтверждает, что пакет сформирован владельцем ключа.

Алгоритм

  • Сериализуйте массив events в каноническую форму JSON по RFC 8785 (JCS): ключи отсортированы, без пробелов, числа в кратчайшем представлении.
  • Вычислите events_hash = hex(SHA-256(canonical_events)) и запишите его в integrity.events_hash.
  • В integrity.prev_batch_hash передайте events_hash предыдущего пакета этой сессии. Для первого пакета (seq = 0) — 64 нуля.
  • Сформируйте строку подписи: {t}.{batch_id}.{session.id}.{events_hash}, где t — текущее Unix-время в секундах.
  • Вычислите HMAC-SHA256(signing_key, строка) и передайте заголовок X-TL-Signature: t={t},v1={hex}.

Граничный узел отклоняет пакеты, у которых t отличается от серверного времени более чем на 300 секунд, — это защищает от повторного воспроизведения перехваченных запросов.

// Подпись и отправка пакета (браузер или Node.js 18+, Web Crypto API)
import canonicalize from 'canonicalize'; // RFC 8785

const enc = new TextEncoder();
const hex = (buf) => [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, '0')).join('');

async function sha256Hex(text) {
  return hex(await crypto.subtle.digest('SHA-256', enc.encode(text)));
}

async function hmacHex(keyHex, message) {
  const raw = Uint8Array.from(keyHex.match(/../g), (h) => parseInt(h, 16));
  const key = await crypto.subtle.importKey('raw', raw, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
  return hex(await crypto.subtle.sign('HMAC', key, enc.encode(message)));
}

export async function sendBatch({ batch, signingKey, publishableKey, prevHash }) {
  const eventsHash = await sha256Hex(canonicalize(batch.events));
  batch.integrity = {
    alg: 'sha256',
    canonicalization: 'jcs',
    events_count: batch.events.length,
    events_hash: eventsHash,
    prev_batch_hash: prevHash ?? '0'.repeat(64),
  };

  const t = Math.floor(Date.now() / 1000);
  const signature = await hmacHex(signingKey, `${t}.${batch.batch_id}.${batch.session.id}.${eventsHash}`);

  const res = await fetch('https://in.tracklane.space/v1/ingest', {
    method: 'POST',
    keepalive: true,
    headers: {
      Authorization: `Bearer ${publishableKey}`,
      'Content-Type': 'application/json',
      'X-TL-Session': batch.session.id,
      'X-TL-Signature': `t=${t},v1=${signature}`,
    },
    body: JSON.stringify(batch),
  });

  if (res.status === 202) return eventsHash;          // станет prev_batch_hash следующего пакета
  if (res.status === 409) return eventsHash;          // дубликат — пакет уже принят ранее
  throw Object.assign(new Error(`ingest failed: ${res.status}`), { retryAfter: res.headers.get('Retry-After') });
}

Повторная доставка и идемпотентность

Клиент обязан повторять отправку при сетевых ошибках, таймаутах и ответах 429 или 5xx. Пакет при повторе отправляется без изменений — с тем же batch_id и events_hash, но с новой подписью и новым t.

  • Задержка: экспоненциальная с джиттером — min(30 с, 0,5 с × 2n) × random(0,5…1).
  • Если сервер вернул заголовок Retry-After, он имеет приоритет над расчётной задержкой.
  • Неотправленные пакеты SDK сохраняет в IndexedDB (браузер) или на диск (сервер) — до 50 МБ — и досылает после восстановления сети.
  • Ответ 409 duplicate_batch означает, что пакет уже был принят: удалите его из очереди и продолжайте.
HTTP/2 202 Accepted
{
  "status": "accepted",
  "batch_id": "bat_01JA8ZK4T3M9Q2XW7RVN5HCD6E",
  "events_accepted": 3,
  "events_rejected": 0,
  "edge": "fra1-07",
  "received_at": "2026-10-15T09:41:27.402Z",
  "chain": { "expected_seq": 15, "gap_detected": false }
}

Коды ответов

Ошибки возвращаются в едином формате: {"error": {"code": "…", "message": "…", "request_id": "…"}}. Указывайте request_id при обращении в поддержку.

HTTPКодЗначениеПовторять
202acceptedПакет записан в журнал граничного узла—
400invalid_payloadНевалидный JSON или нарушена схема пакетанет
401invalid_keyКлюч не найден, отозван или не принадлежит проектунет
403signature_mismatchПодпись не совпала или t вне окна 300 спосле пересчёта
409duplicate_batchПакет с этим batch_id уже принятнет
413payload_too_largeРазмер превышает лимит тарифа — разделите пакетпосле разделения
422integrity_failedevents_hash не соответствует содержимомунет
429rate_limitedПревышен лимит запросов или месячный объёмда, Retry-After
503edge_drainingУзел выводится из ротации; DNS переключит на соседнийда

Лимиты

Лимиты применяются на уровне проекта. Текущее потребление возвращается в заголовках каждого ответа: X-RateLimit-Remaining, X-TL-Quota-Used-Bytes и X-TL-Quota-Limit-Bytes.

ЛимитРазработчикПроЭнтерпрайз
Объём в месяц10 ГБ500 ГБ + overageот 10 ТБ
Запросов в секунду502 000по договору
Размер пакета256 КБ1 МБ5 МБ
Событий в пакете5005 0005 000
Уникальных имён метрик20010 000без ограничений

История изменений

  1. 2026-09-30 · TLP/2

    Добавлены поддержка HTTP/3, сжатие zstd и цепочка хэшей prev_batch_hash для обнаружения пропущенных пакетов.

  2. 2026-06-12 · API v1.8

    Эфемерные ключи подписи для браузерных клиентов, эндпоинт POST /v1/signing-keys.

  3. 2026-03-04 · API v1.6

    Поддержка канонизации RFC 8785; устаревший режим sorted-keys будет отключён 1 марта 2027 года.

© 2026 TrackLane · Документация обновлена 15 октября 2026