> ## Documentation Index
> Fetch the complete documentation index at: https://quintsecurity.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Event Ingestion

> How the edge daemon delivers events to the cloud — batching, overflow, SNS/SQS fan-out

# Event Ingestion

The edge daemon delivers events to the cloud via a single HTTP endpoint: `POST https://api.quintai.dev/v1/ingest`. The ingest service authenticates the deploy token, validates the payload, stamps `org_id`, and publishes to an SNS FIFO topic for fan-out.

<Note>
  Earlier versions of Quint used NATS JetStream for fan-out. In 2026-Q1 the cloud was redesigned around SNS FIFO + SQS for managed delivery and simpler ops. NATS has been removed from the production path.
</Note>

## Edge-side forwarder

`internal/cloud/forwarder.go` in the daemon:

| Setting         | Value                                                    |
| --------------- | -------------------------------------------------------- |
| Buffer capacity | 5,000 events                                             |
| Batch size      | 500 events (cap: 10 MB per request)                      |
| Flush interval  | 1 second                                                 |
| Max retries     | 5 (exponential backoff 1s → 5min)                        |
| Overflow        | JSONL file on disk (`~/.quint/forwarder-overflow.jsonl`) |
| Recovery        | On next successful flush, recovered events are prepended |

```mermaid theme={null}
flowchart LR
    BUF["Ring Buffer<br/>(5000)"] --> BATCH["Batch ≤500 events<br/>or ≤10 MB"]
    BATCH --> PUSH["POST /v1/ingest"]
    PUSH -->|200| ACK["Clear batch"]
    PUSH -->|failure| RETRY["Retry 5x<br/>exp backoff"]
    RETRY -->|exhausted| DISK["overflow.jsonl"]
    DISK -->|next flush| PREPEND["Prepend + retry"]
```

The forwarder is non-blocking. If the buffer fills faster than it drains, oldest events are dropped — telemetry is lossy under pathological load, never the critical path.

## Wire format

Each request body:

```json theme={null}
{
  "events": [
    {
      "schema_version": 2,
      "event_id": "uuid-v4",
      "timestamp": "2026-04-25T03:09:32.138Z",
      "source": "proxy",
      "platform": "claude-code",
      "action_type": "tools/call",
      "session_id": "59247-1777085379101",
      "agent_id": "ne-pid-59247",
      "tool_name": "Bash",
      "arguments_json": "{...}",
      "decision": "allow",
      "labels": {"process_name": "claude", "process_pid": "59247"}
    },
    ...
  ]
}
```

Schema validated server-side at ingest. Max 500 events per request, max 10 MB payload.

## Authentication

Header: `Authorization: Bearer <deploy_token>`

The ingest service SHA256-hashes the token and looks it up in Redis (write-through cache backed by the `api_tokens` Postgres table). The response carries the `org_id`, which is stamped on every event before fan-out.

Token types:

| Type         | Use                                               | Lifetime   |
| ------------ | ------------------------------------------------- | ---------- |
| `install`    | First-contact token shipped with `.pkg` installer | Single-use |
| `enrollment` | Exchanged during `POST /v1/machines/register`     | Single-use |
| `service`    | Long-lived token used for subsequent ingest       | Rotatable  |
| `personal`   | Developer tokens for CLI / dashboard              | Manual     |

See [Auth Overview](/cloud/auth-overview) for the full token hierarchy.

## Fan-out

After validation, each event is published to SNS FIFO topic `quint-events-{env}`. Three SQS queues subscribe:

* `quint-events-pipeline` → `pipeline` service → writes to Postgres `actions` (partitioned by month)
* `quint-events-sessions` → `session-processor` → upserts `sessions` table
* `quint-events-alerts` → `alert-processor` → evaluates rules → writes `alerts`

FIFO ordering is per-session (`MessageGroupId = session_id`), so the session-processor sees a session's lifecycle events in order even under parallel consumption.

## Session lifecycle events

Separate endpoint: `POST /v1/sessions/ingest`. Payload is a single `LifecycleEvent`:

```json theme={null}
{
  "event_type": "session_start",
  "session_id": "59247-1777085379101",
  "agent_id": "ne-pid-59247",
  "platform": "claude-code",
  "working_dir": "/Users/you/project",
  "started_at": "2026-04-25T03:09:11Z"
}
```

Types: `session_start`, `session_resume`, `session_end`. The `session-processor` service upserts the cloud `sessions` row and emits an SSE event to connected dashboard clients.

## Observability

| Metric                     | Where                                           |
| -------------------------- | ----------------------------------------------- |
| Edge forwarder queue depth | Daemon `/debug/flows` endpoint                  |
| Overflow file size         | `ls -la ~/.quint/forwarder-overflow.jsonl`      |
| Ingest 4xx/5xx rate        | CloudWatch alarm on `quint-prod-ingest`         |
| SQS depth per queue        | CloudWatch `ApproximateNumberOfMessagesVisible` |
