Audit Logging (OCSF)
When enabled, the Trust Domain Server emits an audit trail of its security-relevant operations as OCSF events, so you can ingest them into any standard OCSF-compatible security pipeline or SIEM without custom field mapping.
Events are plain OCSF 1.8.0 with no proprietary or non-standard fields added, so they pass strict OCSF validation as-is. The server writes them to its standard output, where your existing log collection can route them to your audit destination.
Audit logging is opt-in and disabled by default. Turning it on does not change SVID issuance behavior. It only adds the audit stream.
What Gets Audited
The server emits two OCSF event classes. Each operation produces an event on both success and failure — a denied or failed operation is recorded with its reason, not silently dropped.
| OCSF Class | class_uid | When it's emitted | What it represents |
|---|---|---|---|
| Authentication | 3002 | On each X.509-SVID / JWT-SVID mint attempt, and on each token exchange attempt on /oauth/token | Each issuance or exchange attempt: the credential or token issued on success, the failure reason on failure |
| Entity Management | 3004 | On each signing-key lifecycle operation | A change to a signing key set |
Signing key lifecycle → OCSF activity
| Keyset operation | OCSF activity | activity_id |
|---|---|---|
| Prepared | Create | 1 |
| Activated | Activate | 10 |
| Tainted | Update | 3 |
| Untainted | Update | 3 |
| Removed | Delete | 4 |
Taint and untaint are modeled as an Update rather than a Deactivate: a tainted key set stays published (its IDs are still distributed so workloads distrust and rotate away), so the key set is never taken out of service. The event's message field disambiguates taint from untaint.
For background on the keyset lifecycle, see Signing Key Rotation.
Output Format
When enabled, the server writes one OCSF event per line to stdout as NDJSON. Each event is a single newline-terminated JSON object, with no surrounding log envelope. Multi-line content (e.g. error messages) is escaped, so every event stays on one line. The pretty-printed JSON in the Event Reference below is for readability only.
The server writes events to stdout but does not ship them anywhere itself. Routing events to your audit destination is the operator's responsibility. See Routing to a SIEM below.
Enabling Audit Logging
Audit logging is controlled by an enable switch and optional cloud context stamped onto every event.
Helm values
ocsf:
# Whether to enable OCSF audit logging. Disabled by default.
enabled: false
# Optional cloud provider (e.g. "AWS", "GCP", "Azure")
cloudProvider: ""
# Optional cloud region (e.g. "us-east-1")
cloudRegion: ""
# Optional cloud account UID (e.g. an AWS account ID)
cloudAccount: ""
Parameters
| Parameter | Helm key | CLI flag | Type | Default | Required |
|---|---|---|---|---|---|
| Enable | ocsf.enabled | --ocsf-enable | bool | false | Yes (to emit anything) |
| Cloud provider | ocsf.cloudProvider | --ocsf-cloud-provider | string | "" | No |
| Cloud region | ocsf.cloudRegion | --ocsf-cloud-region | string | "" | No |
| Cloud account | ocsf.cloudAccount | --ocsf-cloud-account | string | "" | No |
enabled controls event emission. When false (the default), the server emits nothing, regardless of the cloud settings. Set it to true to turn on real emission.
cloudProvider / cloudRegion / cloudAccount are optional context describing the cloud environment the deployment runs in. They are stamped onto the cloud object of every event so downstream pipelines can attribute events to an environment. The cloud account is included only when you supply one.
The cloud fields are independent of the enable switch: All three are optional even when audit logging is enabled. If cloudProvider is left empty, events still emit and validate with a blank cloud.provider dimension, and the server logs a single informational warning at startup to flag it.
Example: enabled with cloud context
ocsf:
enabled: true
cloudProvider: "AWS"
cloudRegion: "us-west-2"
cloudAccount: "123456789012"
Deploy with your usual Helm process. On startup the server begins writing OCSF events to stdout; no other behavior changes.
Event Origin Tags
Every event carries OCSF metadata.tags — key/value pairs that identify where the event came from, so events from multiple trust domains and deployments landing in one SIEM can be attributed to their source.
| Tag | On which events | Meaning |
|---|---|---|
trust_domain_id | All | The trust domain the server belongs to |
trust_domain_deployment_id | All | The trust domain deployment that emitted the event |
cluster_id | Authentication (issuance) | The cluster the issuance request came from |
cluster_version_id | Authentication (issuance) | The cluster version the issuance request came from |
realm | Authentication (issuance) | The realm the request's cluster belongs to |
realm_id | Authentication (issuance) | The ID of that realm |
key_set_id | Authentication (issuance) | The signing key set that produced the credential |
attestor_name | Authentication (issuance) | The server-side workload attestor extension that ran. |
attestor_version | Authentication (issuance) | The version of that attestor extension |
obo_issuer | Authentication (delegation) | The identity provider that issued the subject token of a delegated exchange |
obo_audience | Authentication (delegation) | The audience the delegated credential was requested for |
oauth_issuer | Authentication (OAuth access token) | The OAuth issuer stamped on the access token |
oauth_audience | Authentication (OAuth access token) | The resource the access token was requested for |
oauth_scope | Authentication (OAuth access token) | The scope the access token carries, or the requested scope when issuance failed |
txn_audience | Authentication (Transaction Token) | The trust domain the Transaction Token is valid in, or the requested audience when issuance failed |
txn_scope | Authentication (Transaction Token) | The transaction's scope, or the requested scope when issuance failed |
txn_subject_issuer | Authentication (Transaction Token) | The issuer of the subject access token when it is not this trust domain's OAuth issuer. Only a workload's own access token is exchanged, so this tag appears on the refusal of any other access token |
trust_domain_id and trust_domain_deployment_id are constant for a given server and appear on every event. The remaining tags describe individual requests and appear on Authentication events only. SVID issuance events carry the cluster, realm, key set, and attestor tags:
cluster_id,cluster_version_id,realm, andrealm_idare omitted when issuance fails before the caller's session is established, since no cluster or realm origin is known.key_set_idis omitted on any failure path; it is only present when a credential was successfully signed.attestor_nameandattestor_versionare recorded even when attestation fails, so a failed issuance is attributable to the extension responsible.
Token exchange events carry none of the SVID issuance tags. Each carries the tags of the grant that ran, as listed above, so a SIEM can tell a delegated logon from a workload acting as itself without parsing the event body. See Authentication (token exchange).
Any tag whose value is unavailable is omitted rather than emitted empty. A token exchange that fails before the subject token is parsed, for example when client authentication fails, therefore carries no issuer tag.
Event Reference
Every event shares a common set of OCSF base fields, then adds the fields specific to its class.
Common fields
Present on every event regardless of class.
| Field | Description |
|---|---|
time | The time the event occurred, as epoch milliseconds. |
metadata.version | The OCSF schema version the event conforms to ("1.8.0"). |
metadata.product | The product that reported the event — its vendor (Defakto) and name (spirl-server). |
metadata.tags | Origin tags identifying the event's source (see Event Origin Tags). |
cloud | The cloud environment the deployment runs in, from the cloud configuration. |
osint | OCSF open-source-intelligence enrichments. Required by the schema; always emitted as an empty array. |
severity_id | The normalized severity — 1 (Informational) on success, 3 (Medium) on failure. |
status_id | The normalized outcome — 1 (Success) or 2 (Failure). |
message | A human-readable summary of the outcome. |
status_code | A low-cardinality outcome code from the source. On failure, the gRPC status code for SVID issuance and key lifecycle events, or an exchange failure reason code for token exchange events. On an idempotent success that changed nothing, Unchanged. Omitted otherwise. |
status_detail | A sanitized, free-text description of the outcome — the failure reason. Present on failure only. |
Authentication — SVID issuance
Records a credential issued to a workload. SPIFFE has no dedicated entry in the OCSF auth_protocol_id enum, so the credential type rides on auth_protocol paired with the "Other" id (99).
A single Authentication event represents the entire issuance attempt — on both success and failure. The workload receiving the SVID is the subject (user), the calling agent is the actor, and the RPC and peer details ride on api and src_endpoint.
| Field | Description |
|---|---|
class_uid | The Authentication class identifier — 3002. |
category_uid | The category identifier — 3 (Identity & Access Management). |
type_uid | The event type identifier — 300201 (class_uid * 100 + activity_id). |
activity_id | The normalized activity — Logon (1). |
auth_protocol | The credential type: "SPIFFE X.509-SVID" or "SPIFFE JWT-SVID". |
auth_protocol_id | The normalized protocol identifier — Other (99). |
user | The subject the credential was issued to — the workload, identified by its SPIFFE ID. |
service | The service that performed the authentication — the trust domain. |
actor | The principal that requested issuance on the subject's behalf — the calling agent. actor.user.uid and actor.user.name hold the agent's ID. Omitted when no authenticated agent is known. |
api | The API call that carried the request — operation is the gRPC full-method name. |
src_endpoint | The source endpoint of the request — the caller's IP. Omitted when no peer address is known. |
When issuance fails before the workload's identity is resolved (for example, a required argument is missing, or the workload's attributes match no registered identity), the event is still emitted, attributed to the canonical Unknown subject (user.name "unknown", user.type "Unknown") — so even an unresolved attempt is audited. Issuance that fails after the identity is resolved (for example, a signing error) carries the resolved user.
Success — X.509-SVID
{
"activity_id": 1,
"category_uid": 3,
"class_uid": 3002,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" },
{ "name": "cluster_id", "value": "c-55zzfbmonu" },
{ "name": "cluster_version_id", "value": "cv-kwalu86v7w" },
{ "name": "realm", "value": "production" },
{ "name": "realm_id", "value": "rlm-1zt9yzr92e" },
{ "name": "key_set_id", "value": "ks_3FBEEV8pFwANfqEWPOzGZ8RGAD5" },
{ "name": "attestor_name", "value": "custom:https://attestation-service.company.com/webhook" },
{ "name": "attestor_version", "value": "1.0" }
]
},
"severity_id": 1,
"time": 1781540126644,
"type_uid": 300201,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 1,
"message": "issued X.509-SVID",
"user": {
"uid": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"name": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"type": "Service"
},
"service": { "name": "example.com" },
"auth_protocol": "SPIFFE X.509-SVID",
"auth_protocol_id": 99,
"actor": {
"user": {
"uid": "prod-k8s-node-01",
"name": "prod-k8s-node-01",
"type": "Service"
}
},
"api": { "operation": "/com.spirl.agent.v1.agent.API/MintX509SVID" },
"src_endpoint": { "ip": "10.244.0.5" }
}
A JWT-SVID issuance is identical in shape; only three fields differ: auth_protocol is "SPIFFE JWT-SVID", message is "issued JWT-SVID", and api.operation is /com.spirl.agent.v1.agent.API/MintJWTSVID.
Failure — after the identity is resolved
The signing or key-manager step failed after the workload's SPIFFE ID was known, so the event carries the resolved user, status_id 2, severity_id 3, and the failure reason.
{
"activity_id": 1,
"category_uid": 3,
"class_uid": 3002,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" },
{ "name": "cluster_id", "value": "c-55zzfbmonu" },
{ "name": "cluster_version_id", "value": "cv-kwalu86v7w" },
{ "name": "realm", "value": "production" },
{ "name": "realm_id", "value": "rlm-1zt9yzr92e" },
{ "name": "attestor_name", "value": "custom:https://attestation-service.company.com/webhook" },
{ "name": "attestor_version", "value": "1.0" }
]
},
"severity_id": 3,
"time": 1781541841296,
"type_uid": 300201,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 2,
"message": "X.509-SVID issuance failed",
"status_code": "Internal",
"status_detail": "failed to sign X.509-SVID",
"user": {
"uid": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"name": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"type": "Service"
},
"service": { "name": "example.com" },
"auth_protocol": "SPIFFE X.509-SVID",
"auth_protocol_id": 99,
"actor": {
"user": {
"uid": "prod-k8s-node-01",
"name": "prod-k8s-node-01",
"type": "Service"
}
},
"api": { "operation": "/com.spirl.agent.v1.agent.API/MintX509SVID" },
"src_endpoint": { "ip": "10.244.0.5" }
}
Failure — before the identity is resolved
The request was rejected before the workload's SPIFFE ID could be resolved, so the subject is the canonical Unknown placeholder. Here a JWT-SVID issuance was rejected up front for an invalid argument, before attestation ran. There is no key_set_id or attestor tag, but the agent's session was already established, so the cluster and realm origin tags are present. The authenticated agent is still recorded as the actor.
{
"activity_id": 1,
"category_uid": 3,
"class_uid": 3002,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" },
{ "name": "cluster_id", "value": "c-55zzfbmonu" },
{ "name": "cluster_version_id", "value": "cv-kwalu86v7w" },
{ "name": "realm", "value": "production" },
{ "name": "realm_id", "value": "rlm-1zt9yzr92e" }
]
},
"severity_id": 3,
"time": 1781541364308,
"type_uid": 300201,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 2,
"message": "JWT-SVID issuance failed",
"status_code": "InvalidArgument",
"status_detail": "audience cannot be empty",
"user": {
"name": "unknown",
"type": "Unknown",
"type_id": 0
},
"service": { "name": "example.com" },
"auth_protocol": "SPIFFE JWT-SVID",
"auth_protocol_id": 99,
"actor": {
"user": {
"uid": "prod-k8s-node-01",
"name": "prod-k8s-node-01",
"type": "Service"
}
},
"api": { "operation": "/com.spirl.agent.v1.agent.API/MintJWTSVID" },
"src_endpoint": { "ip": "10.244.0.5" }
}
Authentication (token exchange)
Records each attempt on the Trust Domain Server's /oauth/token endpoint, for every grant it provides: Delegation (the RFC 8693 on-behalf-of exchange and re-exchange), OAuth access tokens (the jwt-bearer grant), and Transaction Tokens. One event represents the whole attempt (success or failure), so a refused exchange is recorded with its reason even though the caller only receives a generic OAuth error.
The event has the same shape as an SVID issuance event, with these differences:
| Field | Description |
|---|---|
auth_protocol | The token class the exchange issued or attempted: "SPIFFE JWT-SVID-OBO" for delegation, "SPIFFE OAuth-Access-Token" for the jwt-bearer grant, "SPIFFE Transaction-Token" for a Transaction Token. |
user | The person the token acts for, when there is one. A delegated exchange records the verified subject of the user's token, and a refused Transaction Token exchange records the verified subject of an access token that was not the requesting workload's own. Two fields are set: user.uid holds the subject identifier and user.type is "User". user.name is left unset for a verified subject because a subject identifier can be personal data (the Unknown placeholder does carry user.name "unknown"). A workload acting as itself (the jwt-bearer grant, or a Transaction Token minted from the workload's own access token) has no human principal, so the Unknown placeholder stands in. A failure before the subject token verified also records Unknown, never an unverified identity. |
service | The trust domain. |
actor | The workload that called the endpoint, by SPIFFE ID in actor.user.uid and actor.user.name, with actor.user.type "Service". Omitted when client authentication failed. |
api | The API call that carried the request. operation is /oauth/token. |
src_endpoint | Not recorded on token exchange events. |
message | "issued JWT-SVID-OBO token", "issued OAuth access token", or "issued transaction token" on success. On failure, "JWT-SVID-OBO token issuance failed", "OAuth access token issuance failed", or "transaction token issuance failed". |
status_code | Present only on failure, and contains one of the failure reason codes below. |
status_detail | Present only on failure, and contains the sanitized failure reason. |
The metadata.tags carry the grant's issuance context (see Event Origin Tags). The OCSF event records the principals and those tags only. The transaction identifier, the delegation chain, the presented subject token's audience, and the issued token's claims are recorded on the console's Token Exchanges view, which is fed by the event stream rather than by these OCSF events. Each is recorded when the exchange carries it, so a request that failed before the subject token was verified has no transaction identifier or delegation chain to show.
The failure reason codes a failed exchange can carry in status_code:
status_code | Meaning |
|---|---|
authorization_denied | The exchange was not authorized. A Transaction Token requested from an access token issued to a principal other than the requesting workload, such as a user's access token, is refused with this code. |
issuer_not_allowlisted | The subject token's issuer is not in the TokenExchangePolicy allowlist, or the trust domain has no TokenExchangePolicy. |
audience_not_allowed | The subject token's audience is not allowlisted for its issuer. |
invalid_subject_token | The subject token failed validation, such as a bad signature, an expired token, or a wrong typ. |
missing_subject_claim | The subject token lacks a claim that the policy's additionalClaims names. |
actor_chain_too_deep | A re-exchange would grow the delegation chain beyond 10 actors. |
invalid_client | Client authentication failed. |
replay_reject | The client assertion's jti was already used. |
audience_not_configured | The server has no expected audience configured for client assertions. |
dpop_proof_invalid, dpop_proof_replayed, dpop_unsupported_on_grant | The DPoP proof was invalid, was replayed, or was sent on a grant that does not support DPoP. The client receives invalid_dpop_proof in all three cases, so the audit code is what tells them apart. |
invalid_grant, unsupported_grant_type, invalid_request | The OAuth error class returned to the client. invalid_request is the catch-all for a malformed or disallowed request. |
server_error | An internal failure. |
Example Success Event (delegated exchange)
The checkout workload exchanged a user's identity token for a delegated credential. The user is the subject, the workload is the actor, and the tags record the user's identity provider and the requested audience.
{
"activity_id": 1,
"category_uid": 3,
"class_uid": 3002,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" },
{ "name": "obo_issuer", "value": "https://login.example.com" },
{ "name": "obo_audience", "value": "https://api.example.com" }
]
},
"severity_id": 1,
"time": 1781540126644,
"type_uid": 300201,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 1,
"message": "issued JWT-SVID-OBO token",
"user": { "uid": "auth0|64f1c2d9a8b7c6e5d4f3a2b1", "type": "User" },
"service": { "name": "example.com" },
"auth_protocol": "SPIFFE JWT-SVID-OBO",
"auth_protocol_id": 99,
"actor": {
"user": {
"uid": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"name": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"type": "Service"
}
},
"api": { "operation": "/oauth/token" }
}
Example Success Event (Transaction Token for the workload itself)
The workload exchanged its own access token, so there is no human principal and user is the Unknown placeholder. The tags record the trust domain the token is valid in and the transaction's scope.
{
"activity_id": 1,
"category_uid": 3,
"class_uid": 3002,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" },
{ "name": "txn_audience", "value": "example.com" },
{ "name": "txn_scope", "value": "orders:read" }
]
},
"severity_id": 1,
"time": 1781540126644,
"type_uid": 300201,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 1,
"message": "issued transaction token",
"user": { "name": "unknown", "type": "Unknown", "type_id": 0 },
"service": { "name": "example.com" },
"auth_protocol": "SPIFFE Transaction-Token",
"auth_protocol_id": 99,
"actor": {
"user": {
"uid": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"name": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"type": "Service"
}
},
"api": { "operation": "/oauth/token" }
}
An OAuth access-token issuance through the jwt-bearer grant has the same shape. Its auth_protocol is "SPIFFE OAuth-Access-Token", its message is "issued OAuth access token", and its tags are oauth_issuer, oauth_audience, and oauth_scope.
Example Failure Event (Transaction Token refused for another principal's access token)
A workload presented a user's access token from an allowlisted identity provider and requested a Transaction Token. Only a workload's own access token is exchanged, so the request was refused. The subject token had already been verified, so the user and the txn_subject_issuer tag are recorded, and status_code names the outcome.
{
"activity_id": 1,
"category_uid": 3,
"class_uid": 3002,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" },
{ "name": "txn_audience", "value": "example.com" },
{ "name": "txn_scope", "value": "orders:read" },
{ "name": "txn_subject_issuer", "value": "https://login.example.com" }
]
},
"severity_id": 3,
"time": 1781540126644,
"type_uid": 300201,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 2,
"message": "transaction token issuance failed",
"status_code": "authorization_denied",
"status_detail": "invalid_request: authorization denied: authorization denied (authorization_denied): a token for a subject other than the requesting workload requires an authorization policy rule allowing the exchange",
"user": { "uid": "auth0|64f1c2d9a8b7c6e5d4f3a2b1", "type": "User" },
"service": { "name": "example.com" },
"auth_protocol": "SPIFFE Transaction-Token",
"auth_protocol_id": 99,
"actor": {
"user": {
"uid": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"name": "spiffe://example.com/production/us-west-2/ns/payments/sa/checkout",
"type": "Service"
}
},
"api": { "operation": "/oauth/token" }
}
Entity Management — signing key lifecycle
Records a change to a signing key set. The affected key set is the entity. The activity_id, type_uid, and message vary by operation:
| Operation | activity_id | type_uid | message (success) |
|---|---|---|---|
| Prepared | Create (1) | 300401 | "prepared signing key set" |
| Activated | Activate (10) | 300410 | "activated signing key set" |
| Tainted | Update (3) | 300403 | "tainted signing key set" |
| Untainted | Update (3) | 300403 | "untainted signing key set" |
| Removed | Delete (4) | 300404 | "removed signing key set" |
| Field | Description |
|---|---|
class_uid | The Entity Management class identifier — 3004. |
category_uid | The category identifier — 3 (Identity & Access Management). |
activity_id | The normalized activity for the operation (see the table above). |
type_uid | The event type identifier — class_uid * 100 + activity_id (see the table above). |
entity | The managed entity acted upon — the signing key set, identified by its key set ID (uid) and trust domain (name). The uid may be empty if a Prepare failed before a key set was generated. |
Activate, taint, untaint, and remove are idempotent. When such an operation succeeds but the key set was already in the requested state, the event records status_id 1 with status_code Unchanged and a distinct message (for example, "signing key set was already absent"), so a consumer can tell a real change from a no-op without parsing the message.
Success
All lifecycle successes share this shape; only activity_id, type_uid, and message change per operation (see the table above). The Activate event below is representative.
{
"activity_id": 10,
"category_uid": 3,
"class_uid": 3004,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" }
]
},
"severity_id": 1,
"time": 1781540534363,
"type_uid": 300410,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 1,
"message": "activated signing key set",
"entity": {
"uid": "ks_3FBFPCV2TJlodtTLg97Splmq331",
"name": "example.com",
"type": "Signing Key Set"
}
}
Failure — Taint (key set still active)
A taint was rejected because the key set is still the active signing key. The event carries the resolved key set as the entity, status_id 2, severity_id 3, and the failure reason.
{
"activity_id": 3,
"category_uid": 3,
"class_uid": 3004,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" }
]
},
"severity_id": 3,
"time": 1781540531702,
"type_uid": 300403,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 2,
"message": "signing key set taint failed",
"status_code": "FailedPrecondition",
"status_detail": "cannot taint active key set \"ks_3FBEEV8pFwANfqEWPOzGZ8RGAD5\"",
"entity": {
"uid": "ks_3FBEEV8pFwANfqEWPOzGZ8RGAD5",
"name": "example.com",
"type": "Signing Key Set"
}
}
Success — Remove (no-op, already absent)
The key set was already gone, so the operation changed nothing. It succeeds with status_code Unchanged and the no-op message.
{
"activity_id": 4,
"category_uid": 3,
"class_uid": 3004,
"metadata": {
"version": "1.8.0",
"product": {
"vendor_name": "Defakto",
"name": "spirl-server",
"version": "0.40.0"
},
"tags": [
{ "name": "trust_domain_id", "value": "td-kscgxiupol" },
{ "name": "trust_domain_deployment_id", "value": "tdd-0c9009b3ed" }
]
},
"severity_id": 1,
"time": 1781540866002,
"type_uid": 300404,
"cloud": {
"provider": "AWS",
"region": "us-west-2",
"account": { "uid": "123456789012" }
},
"osint": [],
"status_id": 1,
"message": "signing key set was already absent",
"status_code": "Unchanged",
"entity": {
"uid": "ks_3FBEEV8pFwANfqEWPOzGZ8RGAD5",
"name": "example.com",
"type": "Signing Key Set"
}
}
Routing to a SIEM
The server only writes events to stdout. To get them into your audit pipeline, pick the events up with the log collector you already run in your cluster and forward them to your OCSF-compatible destination.
Because each event is already valid OCSF 1.8.0, no field mapping or transformation is required. Use the metadata.tags, cloud, and class_uid fields to route or partition events by source and type.
For how the server's other logs are structured, see Grouping Logs by Request. For broader monitoring guidance, see the Operations section.