# Amazon S3 SIEM Integration

Export Capsule detections, findings, and policy violations to a dedicated Amazon S3 bucket that Capsule provisions and manages, then point any SIEM that ingests from S3 at it. Use this when your SIEM pulls from object storage rather than accepting a push.

## Overview

Capsule writes each event to the bucket as it is produced, as newline-delimited JSON (NDJSON), with the same verbose record Capsule holds internally.

| Event type | What gets exported |
|  --- | --- |
| **Detections** | Real-time detections raised on agent activity, with full event context |
| **Findings** | Posture and configuration findings across your AI inventory |
| **Policy violations** | Guardrail and policy breaches, with the full evidence the decision was made on |


```
Capsule event (detection / finding / policy violation)
  → written as NDJSON to a Capsule-managed S3 bucket (one per integration)
  → your SIEM assumes the read-only role (External ID enforced)
  → your SIEM ingests the objects
```

**You do not create the bucket or the role.** Both live in Capsule's AWS account. Your only input is the AWS principal your SIEM reads with, and an external ID. Capsule provisions everything else and hands back the bucket name, region, and role ARN to finish the SIEM side.

> This is separate from **Settings → Notifications**. Notifications push individual alerts to a channel; the SIEM integration streams the full, verbose event record for ingestion and long-term correlation.


If your SIEM accepts a push over HTTP Event Collector, use the [Splunk SIEM integration](/guides/splunk-siem) instead. For Hunters specifically, follow the [Hunters SIEM guide](/guides/hunters-siem), which is this integration with the Hunters-side steps filled in.

## Prerequisites

- A **Capsule** account with admin access, and the **SIEM** tab visible under **Settings**
- A SIEM that can ingest from an S3 bucket it does not own, by assuming a cross-account IAM role
- The **AWS principal** your SIEM reads with, and an **external ID** for the role assumption


## Step 1: Get the principal and external ID from your SIEM

Your SIEM publishes the AWS identity it reads S3 with. Find it in its S3 or cloud-storage data source setup, usually alongside an external ID it generates per connection.

- **Principal ARN** is the SIEM's AWS identity. Capsule accepts a 12-digit account ID, `arn:aws:iam::123456789012:root`, or a specific role or user ARN.
- **External ID** is the value the SIEM sends on `sts:AssumeRole`. This is what protects the role against confused-deputy access, so Capsule requires one whenever a principal is set.


Stop once you have both values. Any instructions your SIEM gives for creating the bucket, the read policy, or the role do not apply here - Capsule performs all of that.

## Step 2: Create the SIEM integration in Capsule

1. In Capsule, go to **Settings → SIEM** and click **Add integration**.
2. Set **Destination** to **Amazon S3**.
3. Enable the event types you want: **Detections**, **Findings**, **Policy violations**.
4. Choose an **Event detail** level:
  - **Full** sends the whole envelope.
  - **Compact** omits activity message bodies. Those bodies dominate an event's size, so use Compact if your SIEM bills by ingested volume and the transcript is not what you search on.
5. Choose a **Data retention** period: 30, 90, 180, or 365 days. This sets the bucket's expiry lifecycle rule.
6. Paste the **Principal ARN** and **External ID** from Step 1.
7. Click **Save**, then **enable** the integration from the list. A new integration starts disabled and exports nothing until you turn it on.


On save, Capsule provisions a **dedicated S3 bucket** for this integration and a **read-only IAM role** whose trust policy allows your principal to assume it, but only when the request carries your external ID.

> You can create more than one SIEM integration. Each gets its own isolated bucket and role, so different SIEM environments or event scopes never share a destination.


## Step 3: Copy the connection details to your SIEM

1. In the SIEM list, open the integration's row menu and choose **View details**.
2. Under **Connection details**, copy the values Capsule generated:
  - **Bucket name** - the S3 bucket to ingest from
  - **Region** - the AWS region the bucket lives in
  - **Read role ARN** - the role your SIEM assumes to read it
3. Provide these to your SIEM's S3 data source, along with the external ID from Step 1, and run its connection validation.


> **Connection details appear once the bucket is provisioned.** If the section shows a pending hint, give provisioning a moment and reopen the drawer.


Use the row menu's **Send test event** to confirm the bucket is writable before you rely on it. The test object lands under `connection-tests/`. The **Delivery** column on the SIEM list shows the outcome of the most recent attempt, and the details drawer shows the last error in full.

## Bucket layout

Every object lives under one of three top-level prefixes. These are the only event keys at the bucket root:

```
detections/
findings/
policy-violations/
```

Objects are partitioned by UTC date and keyed by event:

```
<event-type>/YYYY/MM/DD/<timestamp>-<id>.ndjson
```

If your SIEM asks for a prefix or path template, point it at the partitioned layout so it lists new partitions efficiently rather than scanning the whole bucket. Drop any event types you did not enable.

## What arrives in the bucket

### Event envelope

Each object is NDJSON: one JSON event per line, every line carrying the same envelope with the record under `event`:

```json
{ "schemaVersion": 2, "eventType": "policy_violation.created", "tenantId": "...", "exportedAt": "...", "event": { ... } }
```

`eventType` is one of `detection.created`, `finding.created`, `policy_violation.created`. This is the same envelope the Splunk destination sends, so a tenant exporting to both sees identical content.

### What each event contains

Every event carries its anchor resource in full - id, name, type, external id, parent, and the environment and connector it was discovered through - so events correlate against the rest of your inventory without a lookup.

- **`policy_violation.created`** is self-contained: the violation, the policy that raised it including its conditions, and the complete finding, detection and activity records the decision was made on. You do not need to join it against the other two prefixes to triage it.
- **`detection.created`** carries the detection, its evidence, and the session activities it was raised on.
- **`finding.created`** carries the finding, its evidence, and its remediation steps.


Triage state (`triageVerdict`, `triagedAt`, who triaged it, and the reason) is exported on all three.

Secrets Capsule identified in evidence are masked to a short prefix before export, in evidence text and activity message bodies alike.

> **One violation, one event.** A violation is exported once, when it is first raised. If it is later resolved and the same condition re-triggers, Capsule reuses the existing violation and does not emit a second `policy_violation.created`.


## Delivery behaviour

- **Retention** - objects expire automatically after the retention period you configured. Configure your SIEM to ingest continuously so nothing is missed before expiry.
- **Backfill** - export begins when the integration is enabled. Events raised before that are not sent.
- **Visibility** - the outcome of the most recent attempt is shown per integration in **Settings → SIEM**, with the full error in the details drawer.


## How access is scoped

- The bucket and role live in **Capsule's managed AWS account**, not yours. There is nothing to maintain on your side.
- The read role grants only `s3:ListBucket`, `s3:GetBucketLocation`, and `s3:GetObject`, and only on that one integration's bucket.
- The trust policy requires **both** the principal ARN **and** the matching external ID, so no other party can assume the role even if the ARN is known.


## Troubleshooting

### Connection details never appear

Reopen the details drawer after a short wait. The bucket and role are provisioned on save.

### Your SIEM cannot read the bucket

- Verify the **External ID** in Capsule matches exactly what your SIEM sends. A mismatch blocks the role assumption.
- Verify the **Principal ARN** you pasted is the one your SIEM reads with, with no trailing spaces or truncation.
- Confirm the SIEM is configured with the **Read role ARN**, **Bucket name**, and **Region** exactly as shown in the details drawer.


### Nothing arrives at all

- Confirm the integration is **enabled** and at least one event type is turned on.
- Use **Send test event**. It writes through the same path a real export uses, so a broken bucket or a revoked role surfaces there.
- Remember objects expire at the end of the retention period. A paused ingestion can miss older data.


## Support

For help with this integration, contact [support@capsule.security](mailto:support@capsule.security).