@openauditmodel/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/LICENSE +215 -0
  2. package/README.md +647 -0
  3. package/dist/conformance/src/cli.d.ts +20 -0
  4. package/dist/conformance/src/cli.js +560 -0
  5. package/dist/conformance/src/cli.js.map +1 -0
  6. package/dist/conformance/src/format-errors.d.ts +20 -0
  7. package/dist/conformance/src/format-errors.js +116 -0
  8. package/dist/conformance/src/format-errors.js.map +1 -0
  9. package/dist/conformance/src/integrity/canonicalize.d.ts +25 -0
  10. package/dist/conformance/src/integrity/canonicalize.js +100 -0
  11. package/dist/conformance/src/integrity/canonicalize.js.map +1 -0
  12. package/dist/conformance/src/integrity/digest.d.ts +54 -0
  13. package/dist/conformance/src/integrity/digest.js +126 -0
  14. package/dist/conformance/src/integrity/digest.js.map +1 -0
  15. package/dist/conformance/src/integrity/types.d.ts +86 -0
  16. package/dist/conformance/src/integrity/types.js +36 -0
  17. package/dist/conformance/src/integrity/types.js.map +1 -0
  18. package/dist/conformance/src/integrity/verify-chain.d.ts +23 -0
  19. package/dist/conformance/src/integrity/verify-chain.js +235 -0
  20. package/dist/conformance/src/integrity/verify-chain.js.map +1 -0
  21. package/dist/conformance/src/integrity/verify-event.d.ts +29 -0
  22. package/dist/conformance/src/integrity/verify-event.js +140 -0
  23. package/dist/conformance/src/integrity/verify-event.js.map +1 -0
  24. package/dist/conformance/src/privacy/entropy.d.ts +14 -0
  25. package/dist/conformance/src/privacy/entropy.js +86 -0
  26. package/dist/conformance/src/privacy/entropy.js.map +1 -0
  27. package/dist/conformance/src/privacy/field-names.d.ts +36 -0
  28. package/dist/conformance/src/privacy/field-names.js +121 -0
  29. package/dist/conformance/src/privacy/field-names.js.map +1 -0
  30. package/dist/conformance/src/privacy/lint-event.d.ts +18 -0
  31. package/dist/conformance/src/privacy/lint-event.js +272 -0
  32. package/dist/conformance/src/privacy/lint-event.js.map +1 -0
  33. package/dist/conformance/src/privacy/rules.d.ts +44 -0
  34. package/dist/conformance/src/privacy/rules.js +161 -0
  35. package/dist/conformance/src/privacy/rules.js.map +1 -0
  36. package/dist/conformance/src/privacy/safe-formats.d.ts +32 -0
  37. package/dist/conformance/src/privacy/safe-formats.js +118 -0
  38. package/dist/conformance/src/privacy/safe-formats.js.map +1 -0
  39. package/dist/conformance/src/privacy/size-analysis.d.ts +36 -0
  40. package/dist/conformance/src/privacy/size-analysis.js +87 -0
  41. package/dist/conformance/src/privacy/size-analysis.js.map +1 -0
  42. package/dist/conformance/src/privacy/token-patterns.d.ts +24 -0
  43. package/dist/conformance/src/privacy/token-patterns.js +126 -0
  44. package/dist/conformance/src/privacy/token-patterns.js.map +1 -0
  45. package/dist/conformance/src/privacy/traverse.d.ts +33 -0
  46. package/dist/conformance/src/privacy/traverse.js +61 -0
  47. package/dist/conformance/src/privacy/traverse.js.map +1 -0
  48. package/dist/conformance/src/privacy/types.d.ts +58 -0
  49. package/dist/conformance/src/privacy/types.js +32 -0
  50. package/dist/conformance/src/privacy/types.js.map +1 -0
  51. package/dist/conformance/src/privacy/url-analysis.d.ts +40 -0
  52. package/dist/conformance/src/privacy/url-analysis.js +148 -0
  53. package/dist/conformance/src/privacy/url-analysis.js.map +1 -0
  54. package/dist/conformance/src/profiles/check-profile.d.ts +22 -0
  55. package/dist/conformance/src/profiles/check-profile.js +72 -0
  56. package/dist/conformance/src/profiles/check-profile.js.map +1 -0
  57. package/dist/conformance/src/profiles/evaluate-rule.d.ts +27 -0
  58. package/dist/conformance/src/profiles/evaluate-rule.js +87 -0
  59. package/dist/conformance/src/profiles/evaluate-rule.js.map +1 -0
  60. package/dist/conformance/src/profiles/load-profile.d.ts +30 -0
  61. package/dist/conformance/src/profiles/load-profile.js +95 -0
  62. package/dist/conformance/src/profiles/load-profile.js.map +1 -0
  63. package/dist/conformance/src/profiles/resolve-pointer.d.ts +44 -0
  64. package/dist/conformance/src/profiles/resolve-pointer.js +123 -0
  65. package/dist/conformance/src/profiles/resolve-pointer.js.map +1 -0
  66. package/dist/conformance/src/profiles/select-rules.d.ts +13 -0
  67. package/dist/conformance/src/profiles/select-rules.js +42 -0
  68. package/dist/conformance/src/profiles/select-rules.js.map +1 -0
  69. package/dist/conformance/src/profiles/types.d.ts +101 -0
  70. package/dist/conformance/src/profiles/types.js +31 -0
  71. package/dist/conformance/src/profiles/types.js.map +1 -0
  72. package/dist/conformance/src/profiles/validate-profile-definition.d.ts +12 -0
  73. package/dist/conformance/src/profiles/validate-profile-definition.js +47 -0
  74. package/dist/conformance/src/profiles/validate-profile-definition.js.map +1 -0
  75. package/dist/conformance/src/sources.d.ts +53 -0
  76. package/dist/conformance/src/sources.js +153 -0
  77. package/dist/conformance/src/sources.js.map +1 -0
  78. package/dist/conformance/src/validate-core.d.ts +30 -0
  79. package/dist/conformance/src/validate-core.js +62 -0
  80. package/dist/conformance/src/validate-core.js.map +1 -0
  81. package/dist/conformance/src/validate.d.ts +34 -0
  82. package/dist/conformance/src/validate.js +62 -0
  83. package/dist/conformance/src/validate.js.map +1 -0
  84. package/dist/conformance/src/validator-interface.d.ts +35 -0
  85. package/dist/conformance/src/validator-interface.js +19 -0
  86. package/dist/conformance/src/validator-interface.js.map +1 -0
  87. package/package.json +85 -0
  88. package/profiles/README.md +201 -0
  89. package/profiles/api-and-integration-management/README.md +271 -0
  90. package/profiles/api-and-integration-management/profile.json +258 -0
  91. package/profiles/backup-and-recovery/README.md +318 -0
  92. package/profiles/backup-and-recovery/profile.json +178 -0
  93. package/profiles/customer-and-account-management/README.md +340 -0
  94. package/profiles/customer-and-account-management/profile.json +237 -0
  95. package/profiles/deployment-and-change-management/README.md +359 -0
  96. package/profiles/deployment-and-change-management/profile.json +298 -0
  97. package/profiles/document-management/README.md +120 -0
  98. package/profiles/document-management/profile.json +170 -0
  99. package/profiles/financial-transaction-management/README.md +339 -0
  100. package/profiles/financial-transaction-management/profile.json +247 -0
  101. package/profiles/identity-and-access-management/README.md +112 -0
  102. package/profiles/identity-and-access-management/profile.json +120 -0
  103. package/profiles/incident-management/README.md +338 -0
  104. package/profiles/incident-management/profile.json +256 -0
  105. package/profiles/message-broker-management/README.md +344 -0
  106. package/profiles/message-broker-management/profile.json +399 -0
  107. package/profiles/profile-definition.schema.json +213 -0
  108. package/profiles/secrets-and-key-management/README.md +331 -0
  109. package/profiles/secrets-and-key-management/profile.json +219 -0
  110. package/schemas/v0.1/audit-event.schema.json +911 -0
  111. package/semantic-conventions/README.md +52 -0
  112. package/semantic-conventions/authentication.md +111 -0
  113. package/semantic-conventions/configuration-and-change.md +122 -0
  114. package/semantic-conventions/correlation-and-tracing.md +387 -0
  115. package/semantic-conventions/data-access.md +115 -0
  116. package/semantic-conventions/event-naming.md +114 -0
  117. package/semantic-conventions/identity-and-access.md +112 -0
  118. package/semantic-conventions/privileged-operations.md +110 -0
  119. package/semantic-conventions/workflow-and-approval.md +109 -0
  120. package/specification/actor-model.md +135 -0
  121. package/specification/approval-and-delegation.md +150 -0
  122. package/specification/authentication.md +116 -0
  123. package/specification/authorization.md +117 -0
  124. package/specification/change-model.md +134 -0
  125. package/specification/delivery.md +136 -0
  126. package/specification/design-principles.md +137 -0
  127. package/specification/event-model.md +387 -0
  128. package/specification/evidence-model.md +109 -0
  129. package/specification/extension-model.md +159 -0
  130. package/specification/integrity.md +314 -0
  131. package/specification/overview.md +172 -0
  132. package/specification/privacy.md +386 -0
  133. package/specification/resource-model.md +116 -0
  134. package/specification/terminology.md +135 -0
@@ -0,0 +1,52 @@
1
+ # Semantic Conventions
2
+
3
+ **Specification version: 0.1 · Status: Experimental**
4
+
5
+ Semantic conventions are the layer between the schema and a useful audit trail. The schema says
6
+ `event.name` must be a lower-case dotted name; the conventions say that a sign-in is
7
+ `authentication.login` everywhere, so that two applications written by two teams produce data that
8
+ can be read together.
9
+
10
+ ## Status of these documents
11
+
12
+ Conventions are **SHOULD-level guidance** unless a section explicitly states otherwise.
13
+
14
+ A producer that uses a name outside these conventions is still conforming. It is simply not
15
+ comparable with other producers, which is the entire benefit on offer. Where an application's domain
16
+ is not covered here, producers SHOULD follow the naming rule in
17
+ [event-naming.md](event-naming.md) and propose a convention.
18
+
19
+ The vocabularies here are **open**. The core schema constrains their form, not their membership. See
20
+ [design-principles.md](../specification/design-principles.md).
21
+
22
+ ## Documents
23
+
24
+ | Document | Covers |
25
+ | ---------------------------------------------------------- | ------------------------------------------------- |
26
+ | [event-naming.md](event-naming.md) | The naming rule, categories, activity types |
27
+ | [authentication.md](authentication.md) | Sign-in, sign-out, sessions, credentials, factors |
28
+ | [identity-and-access.md](identity-and-access.md) | Users, roles, permissions, service accounts |
29
+ | [data-access.md](data-access.md) | Reading, exporting, sharing and modifying data |
30
+ | [configuration-and-change.md](configuration-and-change.md) | Settings, secrets, deployments, releases |
31
+ | [workflow-and-approval.md](workflow-and-approval.md) | Requests, approvals, workflow state, incidents |
32
+ | [privileged-operations.md](privileged-operations.md) | Administrative and break-glass operations |
33
+ | [correlation-and-tracing.md](correlation-and-tracing.md) | Request, trace, span and correlation identifiers |
34
+
35
+ ## Relationship to profiles
36
+
37
+ Conventions are horizontal: they describe how to name and categorise the operations that appear in
38
+ almost every application. [Profiles](../profiles/) are vertical: they add stricter requirements for a
39
+ specific domain, and may require fields the core model leaves optional.
40
+
41
+ A convention never adds a requirement. A profile may.
42
+
43
+ ## Proposing a convention
44
+
45
+ Open a specification change issue describing:
46
+
47
+ 1. The operations the convention covers.
48
+ 2. The proposed names, and why they follow the naming rule.
49
+ 3. At least two independent applications that would emit them.
50
+ 4. What breaks if the convention is not adopted.
51
+
52
+ See [CONTRIBUTING.md](../CONTRIBUTING.md).
@@ -0,0 +1,111 @@
1
+ # Authentication Events
2
+
3
+ **Specification version: 0.1 · Status: Experimental**
4
+
5
+ Category: `authentication`
6
+
7
+ ## 1. Recommended event names
8
+
9
+ | Name | Operation |
10
+ | ---------------------------------- | ----------------------------------------------------------- |
11
+ | `authentication.login` | A principal established an authenticated session |
12
+ | `authentication.logout` | A principal ended a session deliberately |
13
+ | `authentication.session.expire` | A session ended by policy rather than by request |
14
+ | `authentication.session.revoke` | A session was terminated by an administrator or by policy |
15
+ | `authentication.factor.enroll` | A principal registered an additional authentication factor |
16
+ | `authentication.factor.remove` | A factor was removed |
17
+ | `authentication.factor.challenge` | An additional factor was requested and answered |
18
+ | `authentication.credential.create` | A credential was issued |
19
+ | `authentication.credential.rotate` | A credential was replaced |
20
+ | `authentication.credential.revoke` | A credential was invalidated |
21
+ | `authentication.password.change` | A principal changed its own password |
22
+ | `authentication.password.reset` | A password was reset, typically by an administrator or flow |
23
+ | `authentication.lockout.apply` | An account was locked after repeated failures |
24
+ | `authentication.lockout.release` | A lock was lifted |
25
+
26
+ Failed sign-ins use `authentication.login` with `outcome: failure`, never a separate name.
27
+
28
+ ## 2. Context to populate
29
+
30
+ | Field | Guidance |
31
+ | -------------------------------- | ------------------------------------------------------------------------------------------ |
32
+ | `actor` | The principal that authenticated. For a failed attempt where identity is unproven, see §4. |
33
+ | `resource` | The session, credential or account that the operation concerned |
34
+ | `authentication.method` | The primary factor used |
35
+ | `authentication.mfa` | Whether a second factor was satisfied |
36
+ | `authentication.provider` | The identity provider that asserted the identity |
37
+ | `authentication.sessionId` | A correlation identifier, never a usable token |
38
+ | `request.ipAddress`, `userAgent` | Only where the audit purpose requires it; both are personal data in context |
39
+ | `controlCategories` | `authentication-logging` |
40
+
41
+ ## 3. Outcomes and errors
42
+
43
+ A failed authentication MUST carry `event.error`. Recommended codes:
44
+
45
+ ```text
46
+ invalid-credentials unknown-principal account-locked
47
+ account-disabled factor-required factor-invalid
48
+ session-expired provider-unavailable policy-denied
49
+ ```
50
+
51
+ `event.error.message` MUST NOT disclose which part of a credential was wrong, whether an account
52
+ exists, or any value the principal submitted. `invalid-credentials` is the correct code for both a
53
+ wrong password and an unknown user, unless the operator has decided otherwise for their threat model.
54
+
55
+ ## 4. Identifying the actor on a failed sign-in
56
+
57
+ A failed sign-in has no proven identity. Recommended handling:
58
+
59
+ - Where the submitted identifier resolves to a known principal, record that principal with the
60
+ identifier the system already uses. Do not record the submitted string.
61
+ - Where it does not resolve, record `actor` as `{"type": "unknown", "id": "..."}` with a producer-chosen
62
+ non-personal placeholder, such as a hash of the attempt or an attempt identifier.
63
+ - Never record the submitted username verbatim when it may be an email address, and never record the
64
+ submitted password in any form. See [privacy.md](../specification/privacy.md).
65
+
66
+ ## 5. Credentials
67
+
68
+ Credential events record the **fact** of a credential operation. They MUST NOT record the credential.
69
+
70
+ ```json
71
+ {
72
+ "event": {
73
+ "name": "authentication.credential.rotate",
74
+ "category": "authentication",
75
+ "outcome": "success"
76
+ },
77
+ "actor": { "type": "service", "id": "service-account-rotation-worker" },
78
+ "resource": { "type": "api-key", "id": "api-key-4471" },
79
+ "change": { "type": "update", "changedFields": ["secret"], "ticketId": "change-8812" },
80
+ "controlCategories": ["authentication-logging", "privileged-access"]
81
+ }
82
+ ```
83
+
84
+ ## 6. Absence of authentication context
85
+
86
+ Background jobs, system operations and imported events legitimately have no `authentication` object.
87
+ Omitting it is correct; `method: anonymous` means something different. See
88
+ [authentication.md](../specification/authentication.md) §4.
89
+
90
+ ## 7. Example
91
+
92
+ ```json
93
+ {
94
+ "specVersion": "0.1",
95
+ "id": "018f1c40-1111-7222-8333-444455556666",
96
+ "time": "2026-03-20T08:12:04Z",
97
+ "event": {
98
+ "name": "authentication.login",
99
+ "category": "authentication",
100
+ "type": "login",
101
+ "outcome": "failure",
102
+ "severity": "medium",
103
+ "error": { "code": "factor-required", "type": "authentication", "retryable": true }
104
+ },
105
+ "actor": { "type": "user", "id": "user-2211" },
106
+ "resource": { "type": "session", "id": "session-attempt-88f1" },
107
+ "application": { "name": "identity-service", "environment": "production" },
108
+ "authentication": { "method": "password", "provider": "corporate-idp", "mfa": false },
109
+ "controlCategories": ["authentication-logging"]
110
+ }
111
+ ```
@@ -0,0 +1,122 @@
1
+ # Configuration and Change Events
2
+
3
+ **Specification version: 0.1 · Status: Experimental**
4
+
5
+ Categories: `configuration`, `deployment`
6
+
7
+ ## 1. Recommended event names
8
+
9
+ ### Configuration
10
+
11
+ | Name | Operation |
12
+ | -------------------------------- | -------------------------------- |
13
+ | `configuration.setting.create` | A setting was introduced |
14
+ | `configuration.setting.update` | A setting was changed |
15
+ | `configuration.setting.delete` | A setting was removed |
16
+ | `configuration.secret.rotate` | A secret was replaced |
17
+ | `configuration.secret.access` | A secret was read by a principal |
18
+ | `configuration.feature.toggle` | A feature flag was switched |
19
+ | `configuration.policy.update` | A policy definition was changed |
20
+ | `configuration.retention.update` | A retention rule was changed |
21
+
22
+ ### Deployment and release
23
+
24
+ | Name | Operation |
25
+ | --------------------------------- | ------------------------------------- |
26
+ | `deployment.release.create` | A release was prepared |
27
+ | `deployment.release.approve` | A release was approved for deployment |
28
+ | `deployment.release.deploy` | A release was deployed |
29
+ | `deployment.release.rollback` | A deployment was reverted |
30
+ | `deployment.infrastructure.apply` | An infrastructure change was applied |
31
+
32
+ ### Change management
33
+
34
+ | Name | Operation |
35
+ | ------------------------ | ----------------------------- |
36
+ | `change.request.create` | A change request was raised |
37
+ | `change.request.approve` | A change request was approved |
38
+ | `change.request.reject` | A change request was refused |
39
+ | `change.request.close` | A change request was closed |
40
+
41
+ ## 2. Configuration changes are high-value audit events
42
+
43
+ Configuration is where controls are switched off. A change to session lifetime, a retention rule, an
44
+ audit destination or a policy version can undo every other control in the system, and it usually
45
+ leaves no trace in business data.
46
+
47
+ Producers SHOULD:
48
+
49
+ - Record every change to security-relevant configuration, including failed attempts.
50
+ - Set `event.severity` to `high` or `critical` for production security configuration.
51
+ - Include `configuration-integrity` in `controlCategories`, and `change-approval` where approval
52
+ applies.
53
+ - Record `application.environment` accurately. The same change is routine in `development` and
54
+ critical in `production`.
55
+
56
+ ## 3. Before and after
57
+
58
+ Configuration is the case where before and after values are usually both safe and necessary — a
59
+ reviewer needs to know that session lifetime went from 720 minutes to 60, not merely that it changed.
60
+
61
+ Nonetheless:
62
+
63
+ - Record only the settings that changed, named in `change.changedFields`.
64
+ - MUST NOT record secret values. A secret rotation records `changedFields: ["secret"]` and nothing
65
+ more. See [change-model.md](../specification/change-model.md) §5.
66
+ - Where a setting's value is itself sensitive — an allowlist of addresses, an internal endpoint —
67
+ record a hash or omit the value.
68
+
69
+ ## 4. Correlating change to authorization
70
+
71
+ The three questions matter most here:
72
+
73
+ | Field | Records |
74
+ | ----------------- | --------------------------------------------- |
75
+ | `authorization` | The policy decision that permitted the change |
76
+ | `approval` | The human decision that sanctioned it |
77
+ | `change.ticketId` | The change record it was performed under |
78
+ | `reason` | Why it was needed |
79
+
80
+ An emergency change with `approval.status` of `pending` and a successful outcome is a control bypass.
81
+ Producers MUST record it accurately rather than suppressing it, and SHOULD record the justification in
82
+ `reason`. That combination is exactly what a post-incident review looks for.
83
+
84
+ ## 5. Deployments
85
+
86
+ `deployment.release.deploy` describes what was deployed, where, and from which source:
87
+
88
+ ```json
89
+ {
90
+ "resource": { "type": "deployment", "id": "deployment-2026-03-16-3" },
91
+ "relatedResources": [{ "type": "service", "id": "service-checkout" }],
92
+ "application": { "name": "delivery-service", "environment": "production" },
93
+ "change": {
94
+ "type": "execute",
95
+ "deploymentId": "deployment-2026-03-16-3",
96
+ "ticketId": "change-9910"
97
+ },
98
+ "metadata": { "sourceRevision": "9f2a4c1", "releaseVersion": "9.0.3", "strategy": "rolling" }
99
+ }
100
+ ```
101
+
102
+ `application` describes the system that **performed** the deployment. The deployed service is a
103
+ resource. Producers routinely get this backwards.
104
+
105
+ Rollbacks SHOULD reference the deployment being reverted in `metadata`, so that the pair can be found
106
+ together.
107
+
108
+ ## 6. Secret access
109
+
110
+ `configuration.secret.access` records that a principal read a secret. It MUST NOT record the secret,
111
+ any part of it, or a hash of it that would allow verification against a guess.
112
+
113
+ Where secret access is routine for a workload, producers SHOULD consider whether recording every read
114
+ is useful, or whether recording grants, rotations and out-of-pattern access is more valuable. Volume
115
+ that nobody reviews is not a control.
116
+
117
+ ## 7. Example
118
+
119
+ See
120
+ [examples/valid/privileged-configuration-change.json](../examples/valid/privileged-configuration-change.json)
121
+ for a complete privileged configuration change with approval, sanitized before and after values, and
122
+ an integrity chain entry.
@@ -0,0 +1,387 @@
1
+ # Semantic Conventions: Correlation and Tracing
2
+
3
+ **Specification version: 0.1 · Status: Experimental · This document: Informative**
4
+
5
+ > How to populate the identifiers that let an operator find every audit event, application log and
6
+ > error belonging to one operation — across services, messages and trace boundaries.
7
+
8
+ The normative rules referenced here live in
9
+ [specification/event-model.md §10](../specification/event-model.md). This document explains which
10
+ identifier to use, and why the distinctions matter.
11
+
12
+ **OpenAuditModel is not a tracing system.** It records no spans, builds no trace tree and stores no
13
+ timing. It carries the identifiers a tracing system already produced, so that an audit event can be
14
+ joined to whatever the operator already runs — and so that correlation still works when nothing is
15
+ running at all.
16
+
17
+ ## 1. The five identifiers
18
+
19
+ | Field | Identifies | Stable across services? | Stable across messages? |
20
+ | ------------------------ | --------------------------------- | ----------------------- | ----------------------- |
21
+ | `/id` | one audit event | no — unique per event | no |
22
+ | `/request/requestId` | one inbound request | usually no | not applicable |
23
+ | `/request/traceId` | one distributed execution | **yes** | yes, if continued |
24
+ | `/request/spanId` | one operation inside that trace | no — new per operation | no |
25
+ | `/request/correlationId` | one logical or business operation | **yes** | **yes** |
26
+
27
+ ### 1.1 `/id` — the audit event identifier
28
+
29
+ Unique to a single audit record. Two events never share it, and it is the field a consumer
30
+ deduplicates on ([delivery.md](../specification/delivery.md)). It identifies the _record_, not the
31
+ operation the record describes.
32
+
33
+ ### 1.2 `/request/requestId` — the current inbound request
34
+
35
+ The request currently being served. Scoped to one service handling one call: a downstream service
36
+ generally has its own, and a background job or message consumer has none at all.
37
+
38
+ `requestId` SHOULD NOT be propagated into asynchronous messages. A message consumer is not serving
39
+ the request that produced the message, and copying the value there makes two unrelated units of work
40
+ look like one.
41
+
42
+ Where the value comes from a caller-supplied header such as `X-Request-ID`, see §5.3.
43
+
44
+ ### 1.3 `/request/traceId` — the distributed technical execution
45
+
46
+ The end-to-end technical execution, in the W3C Trace Context sense. It stays the same across every
47
+ service that participates in one execution, which is what makes it the primary join key between an
48
+ audit event and application logs.
49
+
50
+ A trace ends when the execution ends. It does not span a business process that pauses for an
51
+ overnight batch or a human approval — that is what `correlationId` is for.
52
+
53
+ ### 1.4 `/request/spanId` — the active operation
54
+
55
+ The specific operation within the trace that produced this event. New for every operation, so it is a
56
+ locator inside a trace rather than a grouping key.
57
+
58
+ `spanId` SHOULD NOT be recorded without `traceId`. A span identifier alone cannot be resolved: there
59
+ is nothing to look it up in. This matches the OpenTelemetry log data model, which states that if
60
+ SpanId is present TraceId should be too. The schema does not enforce it, because a producer that has
61
+ only half the context is better served by recording what it has than by dropping the event.
62
+
63
+ ### 1.5 `/request/correlationId` — the logical operation
64
+
65
+ The logical operation, workflow, job, conversation or business process the event belongs to. It may
66
+ span services, messages, trace boundaries and time.
67
+
68
+ This is the field that survives when nothing else does. When a consumer starts a fresh trace, the
69
+ `traceId` changes and the `requestId` is absent; `correlationId` is what still says _this belongs to
70
+ order 2026-004418_. A system with no tracing at all can populate `correlationId` alone and still get
71
+ useful correlation.
72
+
73
+ Because it is the identifier most likely to be set from business data, it is also the one most likely
74
+ to leak personal data. See §5.4.
75
+
76
+ ## 2. Where the values come from
77
+
78
+ Obtain `traceId` and `spanId` from the **active trace context**, not by generating them for the audit
79
+ event. An identifier minted for the audit record correlates the record with nothing: it is
80
+ well-formed, passes validation, and matches no span in any backend.
81
+
82
+ | Situation | `traceId` | `spanId` |
83
+ | ---------------------------------- | ------------------- | ---------------- |
84
+ | Tracing active | from active context | from active span |
85
+ | Trace context received, not active | from `traceparent` | omit |
86
+ | No tracing | omit | omit |
87
+
88
+ Omitting a field is always correct. Inventing one is not.
89
+
90
+ ### 2.1 Do not store the raw headers
91
+
92
+ Parse `traceparent`, extract the trace and span identifiers, and discard the rest. The version byte
93
+ and the sampled flag describe the tracing system's own decisions, not the audited operation.
94
+
95
+ `tracestate` SHOULD NOT be stored at all. It is vendor-specific, may carry tenant or account
96
+ identifiers, and is the only unbounded free-text value in the set. W3C Trace Context forbids putting
97
+ personal data in it, but that obligation binds the system that _writes_ the header — an audit
98
+ producer reading it cannot assume it was honoured.
99
+
100
+ The schema deliberately provides nowhere to put either header.
101
+
102
+ ## 3. Correlation without tracing
103
+
104
+ None of this requires OpenTelemetry, or any tracing at all.
105
+
106
+ A system with no tracer populates `correlationId` — a job run identifier, a workflow instance, an
107
+ order number's non-reversible surrogate — and omits `traceId` and `spanId`. Correlation across
108
+ services still works, because `correlationId` is propagated by the application rather than by a
109
+ tracing library.
110
+
111
+ Where even that is unavailable, `/application` still identifies the producing service and
112
+ `/organization` its tenant, which is the same resource-level fallback OpenTelemetry uses for logs
113
+ with no trace context.
114
+
115
+ ## 4. Scenarios
116
+
117
+ ### 4.1 HTTP request
118
+
119
+ ```json
120
+ {
121
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A80",
122
+ "request": {
123
+ "requestId": "req-8f2c41",
124
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
125
+ "spanId": "00f067aa0ba902b7"
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### 4.2 HTTP request that publishes a message
131
+
132
+ The same trace continues into the producer; `correlationId` is established here and travels with the
133
+ message.
134
+
135
+ ```json
136
+ {
137
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A81",
138
+ "request": {
139
+ "requestId": "req-8f2c41",
140
+ "correlationId": "order-2026-004418",
141
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
142
+ "spanId": "1a2b3c4d5e6f7081"
143
+ }
144
+ }
145
+ ```
146
+
147
+ Propagate `traceparent` and the business `correlationId` in the message headers. Do not propagate
148
+ `requestId`.
149
+
150
+ ### 4.3 Consumer that continues the trace
151
+
152
+ The trace and the correlation both survive. There is no `requestId`: no request is being served.
153
+
154
+ ```json
155
+ {
156
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A82",
157
+ "request": {
158
+ "correlationId": "order-2026-004418",
159
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
160
+ "spanId": "2b3c4d5e6f708192"
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### 4.4 Consumer that starts a new trace
166
+
167
+ Some consumers deliberately begin a new trace — long-running processors, or systems where the
168
+ producing trace has already been closed. The technical execution is new; the business operation is
169
+ not.
170
+
171
+ ```json
172
+ {
173
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A83",
174
+ "request": {
175
+ "correlationId": "order-2026-004418",
176
+ "traceId": "9f8e7d6c5b4a39281706f5e4d3c2b1a0",
177
+ "spanId": "3c4d5e6f708192a3"
178
+ }
179
+ }
180
+ ```
181
+
182
+ `correlationId` is the only identifier shared with §4.2. Without it, the business operation cannot be
183
+ reconstructed at all — which is the reason the field exists separately from `traceId`.
184
+
185
+ ### 4.5 Workflow spanning several services and days
186
+
187
+ An approval that waits for a human outlives any trace. `correlationId` carries the process;
188
+ `/approval/workflowId` identifies the workflow definition or instance.
189
+
190
+ ```json
191
+ {
192
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A84",
193
+ "request": {
194
+ "requestId": "req-c41f09",
195
+ "correlationId": "access-request-2026-0912",
196
+ "traceId": "5c6d7e8f90a1b2c3d4e5f60718293a4b",
197
+ "spanId": "4d5e6f7081920304"
198
+ },
199
+ "approval": {
200
+ "workflowId": "wf-access-review-v3",
201
+ "requestId": "ar-2026-0912"
202
+ }
203
+ }
204
+ ```
205
+
206
+ Both `requestId` fields are correct and mean different things: `/request/requestId` is the HTTP call
207
+ being served, `/approval/requestId` is the approval record. See §6.1.
208
+
209
+ ### 4.6 Scheduled background job
210
+
211
+ No request, so no `requestId`. The job run is the logical operation.
212
+
213
+ ```json
214
+ {
215
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A85",
216
+ "request": {
217
+ "correlationId": "nightly-reconciliation-2026-07-30"
218
+ },
219
+ "metadata": {
220
+ "recordsProcessed": 4187
221
+ }
222
+ }
223
+ ```
224
+
225
+ Every event emitted by one job run SHOULD share that `correlationId`. Do not use
226
+ `/integrity/batchId` for this; see §6.2.
227
+
228
+ ### 4.7 Failed downstream operation
229
+
230
+ A failure belongs to the same execution as the work that failed, so the trace context is preserved.
231
+
232
+ ```json
233
+ {
234
+ "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A87",
235
+ "event": {
236
+ "outcome": "failure",
237
+ "error": { "code": "upstream-timeout" }
238
+ },
239
+ "request": {
240
+ "correlationId": "order-2026-004418",
241
+ "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
242
+ "spanId": "6f70819203040506"
243
+ }
244
+ }
245
+ ```
246
+
247
+ ## 5. Trust and privacy
248
+
249
+ ### 5.1 Correlation is not authorization
250
+
251
+ Correlation identifiers are **observational metadata**. They MUST NOT be treated as proof of
252
+ identity, authorization, authenticity, integrity or tenant isolation
253
+ ([event-model.md §10.3](../specification/event-model.md)).
254
+
255
+ Every one of them can be supplied by a caller, guessed, replayed or copied between tenants. The model
256
+ has dedicated fields that do carry those guarantees — `/authentication`, `/authorization` and
257
+ `/integrity` — and a correlation identifier is not a substitute for any of them. Two events sharing a
258
+ `traceId` is evidence that someone propagated a header, not that they belong to the same principal.
259
+
260
+ ### 5.2 Trace context is not causality
261
+
262
+ A shared `traceId` means two events belong to the same execution. It does not say which caused which,
263
+ in what order, or through what path. Ordering within a producer comes from `/sequence`; causal
264
+ relationships between messages are not modelled in the core schema at all (§7).
265
+
266
+ ### 5.3 Inbound identifiers are untrusted input
267
+
268
+ A `correlationId` or `requestId` taken from a request header is caller-controlled. A producer SHOULD
269
+ either generate its own value, or record the supplied one while never relying on it — an attacker who
270
+ can set `X-Correlation-ID` can attach their own activity to another operation's identifier.
271
+
272
+ The schema bounds the damage: `identifier` caps at 256 characters and rejects leading and trailing
273
+ whitespace, so an identifier cannot become a log-injection payload or an unbounded index key.
274
+
275
+ ### 5.4 Do not put sensitive values in identifiers
276
+
277
+ Personal data, credentials, account numbers and sensitive business values SHOULD NOT be used as
278
+ correlation identifiers. An email address used as a `correlationId` is personal data in the audit
279
+ store, in every application log that echoes it, and in every index built over it.
280
+
281
+ Use a non-reversible surrogate — a random identifier, or a keyed hash of the business key — and keep
282
+ the business value in the field that was designed for it, where it can be redacted independently.
283
+
284
+ The privacy linter does **not** inspect `/request/correlationId` or `/request/requestId`
285
+ ([privacy.md §6.6](../specification/privacy.md)). These fields hold high-entropy values by design and
286
+ scanning them would report a finding on every well-formed event. Nothing will warn a producer that
287
+ put an email address there.
288
+
289
+ ### 5.5 Cardinality
290
+
291
+ Every distinct correlation identifier is an index entry. Deriving one per record in a large batch, or
292
+ per retry, produces indexes that cost more than the queries they serve. Prefer one identifier per
293
+ logical operation.
294
+
295
+ ## 6. Identifiers that are not correlation identifiers
296
+
297
+ ### 6.1 `/approval/requestId` is not `/request/requestId`
298
+
299
+ `/request/requestId` identifies the inbound call being served. `/approval/requestId` identifies an
300
+ approval record that may exist for days and be referenced by many calls. An event may carry both; see
301
+ §4.5.
302
+
303
+ ### 6.2 `/integrity/batchId` is not a job or processing batch
304
+
305
+ `/integrity/batchId` identifies an **integrity sealing or verification batch** — the group of events
306
+ sealed together when digests were computed. It does **not** identify a job run, a processing batch, an
307
+ import batch or any business operation, and its boundaries are set by the sealing process rather than
308
+ by the work.
309
+
310
+ Use `/request/correlationId` for job runs, processing batches and imports (§4.6).
311
+
312
+ Likewise `/integrity/chainId` identifies a tamper-evidence chain, and `/sequence` orders events within
313
+ one producer, partition or chain. Neither is a correlation identifier.
314
+
315
+ ### 6.3 `/authentication/sessionId`
316
+
317
+ A session correlation identifier, scoped to one authenticated session. It is not an operation
318
+ identifier, and it must never be a usable session token or cookie value.
319
+
320
+ ## 7. Experimental: messaging causation
321
+
322
+ **Experimental. Not a stable core field, not a required convention, and not part of the v0.1
323
+ conformance surface.** Nothing validates it, and it may change or be withdrawn.
324
+
325
+ The core schema deliberately has no `causationId`. A shared `traceId` groups events; it does not say
326
+ which event caused which. Where that relationship must be recorded, producers MAY use the reserved
327
+ extension:
328
+
329
+ ```json
330
+ {
331
+ "extensions": {
332
+ "org.openauditmodel.correlation": {
333
+ "causes": [{ "type": "audit-event", "id": "01J8ZC7Q2E4N6R8T0V2X4Z6A81" }]
334
+ }
335
+ }
336
+ }
337
+ ```
338
+
339
+ `causes` is an **array** because a single scalar cannot express fan-in — an operation triggered by
340
+ several messages has several causes, and OpenTelemetry reached the same conclusion when it chose span
341
+ links over a single parent for messaging. `type` names the kind of cause (`audit-event`, `message`);
342
+ `id` is the identifier of that cause.
343
+
344
+ It stays an extension rather than a core field for two reasons. There is not enough production
345
+ adoption evidence to freeze its shape, and a field whose shape later has to change would be a
346
+ breaking change to a published identifier — which the project does not permit. An extension can
347
+ evolve; `/request` cannot.
348
+
349
+ Extension keys are inspected by the privacy linter, so ordinary care about their contents applies.
350
+
351
+ ## 8. Logging interoperability
352
+
353
+ The point of all of this is that one query returns everything. For that, application logs must carry
354
+ the **same values** under whatever names their stack already uses.
355
+
356
+ | OpenAuditModel | OpenTelemetry | ECS |
357
+ | ------------------------ | ------------- | ---------- |
358
+ | `/request/traceId` | `trace_id` | `trace.id` |
359
+ | `/request/spanId` | `span_id` | `span.id` |
360
+ | `/request/correlationId` | attribute | label |
361
+
362
+ The values are byte-identical across all three, so a log backend needs a field alias, not a
363
+ transformation. Emit `traceId` and `spanId` into application logs, error logs, producer logs and
364
+ consumer logs, and a search for one trace identifier returns the audit events alongside them.
365
+
366
+ OpenAuditModel keeps its own `camelCase` naming rather than adopting `trace.id`, because that naming
367
+ is used consistently throughout the schema and correlation is not a good place to make it
368
+ inconsistent. See [mappings/](../mappings/) for the full informative mappings.
369
+
370
+ ## 9. Sampling independence
371
+
372
+ **Audit event generation MUST NOT depend on trace sampling**
373
+ ([event-model.md §10.3](../specification/event-model.md)).
374
+
375
+ Tracing is a sampled diagnostic; auditing is a complete record. An audit event emitted only when a
376
+ span is recording is a sampled audit log, and the gap is invisible until the missing event is the one
377
+ that matters.
378
+
379
+ | Situation | Behaviour |
380
+ | -------------------------------------- | ------------------------------------------------ |
381
+ | Trace sampled and stored | Record the identifiers; they resolve. |
382
+ | Trace exists but is not sampled | Record the identifiers anyway. |
383
+ | No tracing backend retains the trace | Record them anyway. The audit event is complete. |
384
+ | Identifier resolves to no stored trace | Expected. Not an error, and not a defect. |
385
+
386
+ A `traceId` that leads nowhere is a normal outcome of sampling, and it still correlates the audit
387
+ event with the application logs of the same execution.