# Log streaming, inline hooks and log integrity (https://docs.idra.zone/docs/admin/logs-and-hooks)

Send the System Log to a SIEM, call your endpoint during a flow, and prove the log hasn't been changed.

## Log streams

**Workflow → Log Streams** sends every System Log event to a SIEM.

| Destination | What IdRa sends |
|---|---|
| Splunk HTTP Event Collector | One `{"time","sourcetype":"idra:system_log","event":{…}}` per line, with `Authorization: Splunk <token>` |
| HTTPS endpoint | A JSON array of events, with your credential in the header you name (`Authorization` by default) |

Each event has the shape of an Okta `LogEvent`, the same as `/api/v1/logs`.

- **Delivery** runs about once a minute, in batches of up to 500. It's at least once and in order, so de-duplicate on `uuid`.
- **Starting point:** a new stream starts with events logged from then on.
- **If your SIEM is down,** sign-ins and every other action carry on: logging never waits for a stream. IdRa retries with a growing delay, up to an hour, then catches up. It logs `log_stream.delivery.fail` once when an outage starts. Events older than your [retention](#retention) are deleted even if a stream never received them, so fix outages sooner than that.
- **Privacy erasure** applies to events not yet delivered. It can't reach what your SIEM has already received, so erase there too.
- **Each organization's** streams carry that organization's events only.

Log streams only send to public addresses over HTTPS. AWS EventBridge isn't supported yet.

## Event hook filters

An event hook can have a **Filter**: an [expression](https://docs.idra.zone/docs/admin/expressions) on `event`, on top of its event types. Only events it's true for are delivered, for example `event.outcome == 'failure'` or `event.target.type == 'app'`. It reads `event.type`, `event.outcome`, `event.actor`, `event.target`, `event.reason`, `event.client` and `event.data`. An expression that fails on an event delivers nothing for that event.

## Inline hooks

**Workflow → Inline Hooks** registers an endpoint that IdRa calls during a flow, in the format of Okta inline hooks. One hook can be active for each type: token, SAML assertion, registration, user import, password import and telephony.

No sign-in flow calls these hooks yet. Each flow adopts them in its own release, and this page will say when. Until then a hook only runs when you press **Test**.

### What your endpoint receives and answers

A `POST` with `eventType` (such as `com.okta.oauth2.tokens.transform`), `eventId`, `eventTime`, `source` and `data`. Answer with a 2xx and one of:

```json
{ "commands": [{ "type": "com.okta.identity.patch", "value": [] }] }
```

```json
{ "error": { "errorSummary": "Shown to the person" } }
```

An empty answer means carry on. An `error` always stops the flow.

### Timeouts and failures

IdRa makes one attempt and waits up to **3 seconds**. A call has failed if it times out, can't connect, answers anything but a 2xx (redirects aren't followed), answers over 256 KB, answers something that isn't a hook response, or sends a command the hook's type doesn't allow.

Each hook sets what a failure does:

| Setting | Effect | Use when |
|---|---|---|
| **Stop the flow** (default) | Nobody gets through that flow until your endpoint is back or you deactivate the hook | The hook makes a security decision |
| **Continue without the hook** | The flow goes on with no changes from the hook, after waiting out the timeout | The hook only adds data |

A password import hook always stops the flow. Every call is logged as `inline_hook.invoke`, with the outcome and never the data.

### Hook keys

Every call is signed with a hook key: `X-IdRa-Signature: t=<unix seconds>,v1=<hex>`, where `v1` is `HMAC-SHA256(secret, "<t>.<body>")`, the same scheme as event hooks. Reject calls with an old `t`.

The secret is shown once. To rotate, create a key, have your endpoint accept both, move the hook to the new key, then delete the old one.

## Log integrity

**Reports → Log Integrity** shows whether the System Log has been changed outside IdRa.

Every 15 minutes IdRa seals new events into a signed chain of checkpoints, and writes the latest in a `system.log.checkpoint` event. Each day it checks the whole chain, and logs `security.log.tamper_detected` if anything differs. **Verify now** runs the same check. Alert on that event.

It finds direct changes to sealed events: edits to any column, deletions and backdated insertions.

- **Privacy erasure** and **retention** are recorded in the chain, so they don't read as tampering. Erasing someone replaces the signature that covered their data, so nothing in the chain can be used to confirm what was erased.

### What it can't do

- Events are unsealed for up to about 20 minutes after they're written.
- Someone holding both database access and `ENCRYPTION_KEY` could rebuild the chain, and someone with database access could remove its newest checkpoints along with their events. Only an outside copy shows either. Send the log to a [stream](#log-streams) and compare `system.log.checkpoint` events with the **Latest checkpoint** on this page.
- If background jobs stop, the page warns about unsealed events. That warning looks the same as a removed end of the chain.

## Retention

**Settings → Account** sets how long the System Log is kept. Retention applies to the log in IdRa, not to what a stream has delivered.