Log streams
Log streams
Console page: Monitoring › Log streams in an environment console, and Audit log › Log streams in an organization's console

A log stream sends a copy of every Audit log entry to your security team's own tools, such as Splunk, Elastic or Datadog, or into an Amazon S3 or Google Cloud Storage bucket for archiving, as it is written. An investigation then starts in the tools your team already uses, not with a request for an export.
What is sent
- Every Audit log entry the stream covers: sign-ins, membership changes, roles,
connections, keys, settings. Each one carries its chain fields (
sequence,hash,prev_hash) and itsorganization_id. The event'sidis the entry's chain hash, so your SIEM can recognise a duplicate. - App audit logs events, to streams an organization owns. These are
marked
source: app, so a SIEM can tell your app'suser.createdfrom Cbox ID's.
Which entries a stream gets depends on who owns it:
| Created in | Owner | Carries |
|---|---|---|
| An environment console | The environment | Every organization's entries in the environment, plus the environment's own. Not App audit logs events. |
| An organization's console, or the Admin Portal | That organization | That organization's entries, and its App audit logs events. Nothing from any other organization. |
An organization admin never sees the environment's own streams.
Every entry is mapped to a category (authentication, session, authorization, identity and
access, configuration, threat or audit) from its action, an outcome (failure for actions
such as *.failed or *.denied) and a severity, so your SIEM's rules have something to
match on.
Destinations
Five destinations are HTTP collectors: an HTTPS POST of newline-delimited records
to a URL you give.
Destination (destination) |
Format | Default authentication |
|---|---|---|
Splunk HEC (splunk_hec) |
Splunk HTTP Event Collector events, sourcetype cbox:siem. A URL with no path gets /services/collector/event. |
Splunk token |
Elastic (ECS) (elastic_ecs) |
Elastic Common Schema documents | Bearer token |
Graylog (GELF) (graylog_gelf) |
GELF over HTTP | Bearer token |
CEF over HTTP (cef_http) |
ArcSight CEF lines, text/plain |
Bearer token |
Generic JSON (generic_json) |
One JSON object per entry: id, timestamp, action, category, outcome, severity, actor, target, source_ip, message, context |
Bearer token |
Three are cloud destinations. You give their settings rather than a URL, and they authenticate their own way:
Destination (destination) |
Where it goes | Credential (the stream's secret) |
|---|---|---|
Datadog (datadog) |
The Datadog Logs intake for your site, gzip, at most 1000 entries per request | A Datadog API key |
Amazon S3 (s3) |
One newline-delimited JSON object per batch in your bucket; also MinIO and Cloudflare R2 | A secret access key, or none with an assumed role |
Google Cloud Storage (gcs) |
One newline-delimited JSON object per batch in your bucket | A service-account JSON key |
A bucket receives objects named {prefix}/{yyyy}/{mm}/{dd}/{hh}/{batch-id}.ndjson.gz,
partitioned by the UTC hour the batch was written, one event per line. Every batch is a
new object, so a retry never overwrites one and write-only permissions are enough. Turn
Compress off ("gzip": false) for plain .ndjson.
There is no syslog destination; CEF over HTTP or Generic JSON reaches most collectors that also accept syslog.
Authentication
For the HTTP collectors:
Scheme (auth) |
What is sent |
|---|---|
None (none) |
No authentication header. |
Bearer token (bearer) |
Authorization: Bearer <secret> |
Splunk (splunk) |
Authorization: Splunk <token> |
HMAC (hmac) |
X-Cbox-Timestamp: <unix time> and X-Cbox-Signature: t=<timestamp>,v1=<hex>, where v1 is HMAC-SHA256 of timestamp + "." + body, keyed with the stream's signing key. The same scheme as webhooks. |
With HMAC, leave the secret empty and a 64-character signing key is generated for you and shown once. Copy it into your receiver then; only the encrypted form is stored, so it cannot be shown again.
A token, API key, secret access key or service-account key you supply is stored encrypted, used only to authenticate deliveries, scrubbed from any error message, and never shown again: not on the stream's page, not on its edit form, not in an API answer.
Create one
In the console, choose New stream (you are asked for your password first):
- Name it, for example "Splunk — production".
- Pick the Destination. The form then asks for what that destination needs.
- For an HTTP collector, paste the Endpoint URL and choose the Auth scheme. The URL must be a public address: private, reserved and cloud metadata addresses are refused, and redirects are not followed. Paste the Secret, or leave it empty with HMAC for a generated key.
- For Datadog, Amazon S3 or Google Cloud Storage, fill in the settings and the credential as described below.
- Create stream. Entries flow from the next one written; earlier entries are not backfilled.
Then choose Send test event on the stream's page to check it before real entries arrive.
Datadog
- In Datadog, go to Organization Settings › API Keys › New Key. Use an API key, not an application key.
- Choose the Datadog site your account is on: the domain of the Datadog URL you sign
in at. That is
datadoghq.com(US1),us3.datadoghq.com,us5.datadoghq.com,datadoghq.eu(EU1),ap1.datadoghq.com,ap2.datadoghq.comorddog-gov.com(US1-FED). A key only works on its own site. - Paste the key as the API key.
- Optionally set Service (default: this platform's name), Source (
ddsource, defaultcbox), Tags (key:value, comma-separated, for exampleenv:prod, team:security) and Hostname.
Each entry arrives as one log whose message is the event's JSON, which Datadog parses into attributes.
Amazon S3
-
Create the bucket, with your own retention and lifecycle rules.
-
Create this permissions policy. It allows writing objects under the prefix and nothing else: nothing can be read, listed or deleted. The stream's page shows it filled in for your bucket and prefix, with a copy button.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "CboxAuditWriteOnly", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::acme-audit-logs/cbox/audit/*" } ] }With SSE-KMS on a customer-managed key, add a statement allowing
kms:GenerateDataKeyon the key's ARN. -
Choose the Credentials:
-
Access key: attach the policy to an IAM user, create an access key for it, and give its Access key ID and Secret access key.
-
Assume an IAM role: no secret is stored with us. Attach the policy to a role, give its IAM role ARN, and create the stream. Then set the role's trust policy to the one the stream's page shows. It names this platform's AWS principal and requires the stream's external ID, a value generated for this stream alone:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::<platform-account-id>:user/<platform-user>" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "<the stream's external ID>" } } } ] }The external ID is what stops another customer of this platform from pointing a stream at your role. Nothing is written to the bucket until the trust policy is in place; send a test event once it is. This option is offered only when the operator has given the platform an AWS identity (environment variables).
-
-
Give the Bucket, its AWS Region (for example
eu-west-1), and optionally a Prefix and Server-side encryption (AES256, oraws:kmswith a KMS key).
For MinIO or Cloudflare R2, set the Custom endpoint to the store's https://
address (R2's region is auto). A custom endpoint uses path-style addressing.
Google Cloud Storage
-
Create the bucket, then a service account for this platform.
-
Grant the service account Storage Object Creator (
roles/storage.objectCreator) on the bucket only. It can create objects and nothing else:gcloud storage buckets add-iam-policy-binding gs://acme-audit-logs \ --member="serviceAccount:[email protected]" \ --role="roles/storage.objectCreator" -
Create a JSON key for the service account and paste the whole file as the Service account key.
-
Give the Bucket and optionally a Prefix.
The key signs a short-lived token request to Google's token endpoint. The key file's own
token_uri is ignored, so a key file cannot change where the platform sends requests.
Delivery
- At least once. Each entry is written to an outbox in the same transaction as the
Audit log entry itself, then delivered. A retry after a timeout can deliver an entry
twice; deduplicate on
id. - Batched. A scheduled pump runs every minute and sends up to 500 records, or 512 KB, per request (Datadog: never more than its intake accepts).
- Retried. A failed batch is retried with exponential backoff and jitter, up to 12 attempts, about two and a half hours in all. After that the entries are dead-lettered and not retried.
- Circuit-broken, not disabled. After 5 consecutive failures the stream is skipped for 5 minutes, then one probe is tried. A refused credential or setting opens the circuit at once and the entries wait without using up their attempts (see below). The stream is never switched off for you.
- Bounded. A stream holds at most 100,000 pending entries. Beyond that the oldest are dropped by default, so a receiver that is down for days does not grow the outbox forever.
The operator of the deployment can change these limits; see the framework's audit streaming guide. Delivery needs the scheduler and a queue worker running.
Health and testing
Each stream shows how delivery is going:
Status (health) |
Meaning | What to do |
|---|---|---|
Delivering (healthy) |
The last attempt succeeded, or none has failed. | Nothing. |
Retrying (degraded) |
Recent failures that may pass: a timeout, 429, a 5xx. Still retrying. |
Usually nothing. |
Paused after failures (paused) |
5 failures in a row; the stream is skipped for 5 minutes, then tried again. | Check the destination is up. |
Action required (action_required) |
The destination refused the credential or the settings: 401/403, a missing bucket, a wrong region, a refused token exchange. |
Fix the stream. Entries wait, without using up their attempts. |
The stream's page shows the last error, scrubbed of the credential. Edit the stream to replace a key or correct a setting: saving checks the settings again and retries delivery on the next run. A successful Send test event also clears Action required.
Send test event sends one marked event (action siem.stream.test, "safe to
ignore") straight to the destination, with the stream's own format and credential. It
does not go through the outbox and is not retried. The page says whether it was accepted,
or gives the destination's reason.
Edit, disable, resume, delete
- Edit changes the name, the destination, its settings or the credential, behind your password like creating. The credential field starts empty, and empty keeps the current one. An assumed-role stream keeps its external ID when you edit it.
- Disable stream stops delivery and keeps what is pending. Resume sends it with the next batch, so nothing is lost while it was off. Disabling is critical: it is how a trail goes dark.
- Delete stream stops delivery at once and destroys the stored credential. The Audit log itself is untouched.
Each change is recorded on the Audit log as log_stream.created, log_stream.updated,
log_stream.disabled, log_stream.enabled or log_stream.deleted, with the destination
and its settings but never a credential. log_stream.updated lists which fields changed,
secret among them when the credential was replaced.
From code
The same lifecycle is a set of actions, on the management API, MCP and the CLI alike:
| Action | REST | Scope | Danger |
|---|---|---|---|
log_streams.list, log_streams.get |
GET /api/v1/log-streams, …/{id} |
log_streams:read |
read |
log_streams.create |
POST /api/v1/log-streams |
log_streams:write |
critical |
log_streams.update |
PATCH /api/v1/log-streams/{id} |
log_streams:write |
critical |
log_streams.test |
POST /api/v1/log-streams/{id}/test |
log_streams:write |
write |
log_streams.delete |
DELETE /api/v1/log-streams/{id} |
log_streams:write |
destructive |
log_streams.create takes name, destination, either organization_id or
"environment_wide": true, and:
- for an HTTP collector,
endpoint_url, and optionallyauthandsecret; - for Datadog, S3 and GCS,
secret(the API key, secret access key or service-account JSON key; none for an assumed role) andoptions: Datadogsite,service,source,tags(a list),hostname; S3bucket,region,prefix,access_key_idorrole_arn,sse,kms_key_id,path_style,gzip; GCSbucket,prefix,gzip. Leave outendpoint_urlfor the destination's own endpoint, or give anhttpsURL for an S3-compatible store.
{
"name": "Audit archive",
"destination": "s3",
"options": {
"bucket": "acme-audit-logs",
"region": "eu-west-1",
"prefix": "cbox/audit",
"role_arn": "arn:aws:iam::123456789012:role/cbox-audit-writer"
},
"environment_wide": true
}
The answer carries secret only when the platform generated it (an HMAC key), and
external_id for an assumed-role S3 stream: put it in the role's trust policy. A setting
the destination does not accept is refused with invalid_stream_configuration and a
message naming the field; a role when the platform has no AWS identity, with
assumed_role_unavailable. Every stream also answers its options, health,
last_error, last_failure_kind and last_failure_at.
log_streams.update takes any of name, destination, endpoint_url, auth,
secret, options and enabled (false to disable, true to resume). options are
merged over the stored ones: send only what changes, and null to remove a key, such as
access_key_id when moving to a role_arn. Changing the destination starts its options
and credential afresh. Any change to the settings is checked again and resets the
stream's failure count.
log_streams.test sends one test event (siem.stream.test) straight to the destination
and answers { "delivered": true|false, "failure": …, "error": … }, where failure is
transient, authentication or configuration. It is not retried and does not go
through the outbox.
Creating and updating a stream are critical, so a key with an approval policy may wait for a person first (step-up approvals).
Related
- Audit log — what is being streamed.
- App audit logs — your app's own events, which organization-owned streams also carry.
- Admin Portal — let a customer's IT admin point their organization's entries at their own SIEM or bucket.
- Log streams, for IT admins — what that IT admin sees.
- Environment variables — the platform's AWS identity for assumed-role S3 streams.
- Webhooks — notifications for your systems to react to, rather than a record.