@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.
- package/LICENSE +215 -0
- package/README.md +647 -0
- package/dist/conformance/src/cli.d.ts +20 -0
- package/dist/conformance/src/cli.js +560 -0
- package/dist/conformance/src/cli.js.map +1 -0
- package/dist/conformance/src/format-errors.d.ts +20 -0
- package/dist/conformance/src/format-errors.js +116 -0
- package/dist/conformance/src/format-errors.js.map +1 -0
- package/dist/conformance/src/integrity/canonicalize.d.ts +25 -0
- package/dist/conformance/src/integrity/canonicalize.js +100 -0
- package/dist/conformance/src/integrity/canonicalize.js.map +1 -0
- package/dist/conformance/src/integrity/digest.d.ts +54 -0
- package/dist/conformance/src/integrity/digest.js +126 -0
- package/dist/conformance/src/integrity/digest.js.map +1 -0
- package/dist/conformance/src/integrity/types.d.ts +86 -0
- package/dist/conformance/src/integrity/types.js +36 -0
- package/dist/conformance/src/integrity/types.js.map +1 -0
- package/dist/conformance/src/integrity/verify-chain.d.ts +23 -0
- package/dist/conformance/src/integrity/verify-chain.js +235 -0
- package/dist/conformance/src/integrity/verify-chain.js.map +1 -0
- package/dist/conformance/src/integrity/verify-event.d.ts +29 -0
- package/dist/conformance/src/integrity/verify-event.js +140 -0
- package/dist/conformance/src/integrity/verify-event.js.map +1 -0
- package/dist/conformance/src/privacy/entropy.d.ts +14 -0
- package/dist/conformance/src/privacy/entropy.js +86 -0
- package/dist/conformance/src/privacy/entropy.js.map +1 -0
- package/dist/conformance/src/privacy/field-names.d.ts +36 -0
- package/dist/conformance/src/privacy/field-names.js +121 -0
- package/dist/conformance/src/privacy/field-names.js.map +1 -0
- package/dist/conformance/src/privacy/lint-event.d.ts +18 -0
- package/dist/conformance/src/privacy/lint-event.js +272 -0
- package/dist/conformance/src/privacy/lint-event.js.map +1 -0
- package/dist/conformance/src/privacy/rules.d.ts +44 -0
- package/dist/conformance/src/privacy/rules.js +161 -0
- package/dist/conformance/src/privacy/rules.js.map +1 -0
- package/dist/conformance/src/privacy/safe-formats.d.ts +32 -0
- package/dist/conformance/src/privacy/safe-formats.js +118 -0
- package/dist/conformance/src/privacy/safe-formats.js.map +1 -0
- package/dist/conformance/src/privacy/size-analysis.d.ts +36 -0
- package/dist/conformance/src/privacy/size-analysis.js +87 -0
- package/dist/conformance/src/privacy/size-analysis.js.map +1 -0
- package/dist/conformance/src/privacy/token-patterns.d.ts +24 -0
- package/dist/conformance/src/privacy/token-patterns.js +126 -0
- package/dist/conformance/src/privacy/token-patterns.js.map +1 -0
- package/dist/conformance/src/privacy/traverse.d.ts +33 -0
- package/dist/conformance/src/privacy/traverse.js +61 -0
- package/dist/conformance/src/privacy/traverse.js.map +1 -0
- package/dist/conformance/src/privacy/types.d.ts +58 -0
- package/dist/conformance/src/privacy/types.js +32 -0
- package/dist/conformance/src/privacy/types.js.map +1 -0
- package/dist/conformance/src/privacy/url-analysis.d.ts +40 -0
- package/dist/conformance/src/privacy/url-analysis.js +148 -0
- package/dist/conformance/src/privacy/url-analysis.js.map +1 -0
- package/dist/conformance/src/profiles/check-profile.d.ts +22 -0
- package/dist/conformance/src/profiles/check-profile.js +72 -0
- package/dist/conformance/src/profiles/check-profile.js.map +1 -0
- package/dist/conformance/src/profiles/evaluate-rule.d.ts +27 -0
- package/dist/conformance/src/profiles/evaluate-rule.js +87 -0
- package/dist/conformance/src/profiles/evaluate-rule.js.map +1 -0
- package/dist/conformance/src/profiles/load-profile.d.ts +30 -0
- package/dist/conformance/src/profiles/load-profile.js +95 -0
- package/dist/conformance/src/profiles/load-profile.js.map +1 -0
- package/dist/conformance/src/profiles/resolve-pointer.d.ts +44 -0
- package/dist/conformance/src/profiles/resolve-pointer.js +123 -0
- package/dist/conformance/src/profiles/resolve-pointer.js.map +1 -0
- package/dist/conformance/src/profiles/select-rules.d.ts +13 -0
- package/dist/conformance/src/profiles/select-rules.js +42 -0
- package/dist/conformance/src/profiles/select-rules.js.map +1 -0
- package/dist/conformance/src/profiles/types.d.ts +101 -0
- package/dist/conformance/src/profiles/types.js +31 -0
- package/dist/conformance/src/profiles/types.js.map +1 -0
- package/dist/conformance/src/profiles/validate-profile-definition.d.ts +12 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js +47 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js.map +1 -0
- package/dist/conformance/src/sources.d.ts +53 -0
- package/dist/conformance/src/sources.js +153 -0
- package/dist/conformance/src/sources.js.map +1 -0
- package/dist/conformance/src/validate-core.d.ts +30 -0
- package/dist/conformance/src/validate-core.js +62 -0
- package/dist/conformance/src/validate-core.js.map +1 -0
- package/dist/conformance/src/validate.d.ts +34 -0
- package/dist/conformance/src/validate.js +62 -0
- package/dist/conformance/src/validate.js.map +1 -0
- package/dist/conformance/src/validator-interface.d.ts +35 -0
- package/dist/conformance/src/validator-interface.js +19 -0
- package/dist/conformance/src/validator-interface.js.map +1 -0
- package/package.json +85 -0
- package/profiles/README.md +201 -0
- package/profiles/api-and-integration-management/README.md +271 -0
- package/profiles/api-and-integration-management/profile.json +258 -0
- package/profiles/backup-and-recovery/README.md +318 -0
- package/profiles/backup-and-recovery/profile.json +178 -0
- package/profiles/customer-and-account-management/README.md +340 -0
- package/profiles/customer-and-account-management/profile.json +237 -0
- package/profiles/deployment-and-change-management/README.md +359 -0
- package/profiles/deployment-and-change-management/profile.json +298 -0
- package/profiles/document-management/README.md +120 -0
- package/profiles/document-management/profile.json +170 -0
- package/profiles/financial-transaction-management/README.md +339 -0
- package/profiles/financial-transaction-management/profile.json +247 -0
- package/profiles/identity-and-access-management/README.md +112 -0
- package/profiles/identity-and-access-management/profile.json +120 -0
- package/profiles/incident-management/README.md +338 -0
- package/profiles/incident-management/profile.json +256 -0
- package/profiles/message-broker-management/README.md +344 -0
- package/profiles/message-broker-management/profile.json +399 -0
- package/profiles/profile-definition.schema.json +213 -0
- package/profiles/secrets-and-key-management/README.md +331 -0
- package/profiles/secrets-and-key-management/profile.json +219 -0
- package/schemas/v0.1/audit-event.schema.json +911 -0
- package/semantic-conventions/README.md +52 -0
- package/semantic-conventions/authentication.md +111 -0
- package/semantic-conventions/configuration-and-change.md +122 -0
- package/semantic-conventions/correlation-and-tracing.md +387 -0
- package/semantic-conventions/data-access.md +115 -0
- package/semantic-conventions/event-naming.md +114 -0
- package/semantic-conventions/identity-and-access.md +112 -0
- package/semantic-conventions/privileged-operations.md +110 -0
- package/semantic-conventions/workflow-and-approval.md +109 -0
- package/specification/actor-model.md +135 -0
- package/specification/approval-and-delegation.md +150 -0
- package/specification/authentication.md +116 -0
- package/specification/authorization.md +117 -0
- package/specification/change-model.md +134 -0
- package/specification/delivery.md +136 -0
- package/specification/design-principles.md +137 -0
- package/specification/event-model.md +387 -0
- package/specification/evidence-model.md +109 -0
- package/specification/extension-model.md +159 -0
- package/specification/integrity.md +314 -0
- package/specification/overview.md +172 -0
- package/specification/privacy.md +386 -0
- package/specification/resource-model.md +116 -0
- package/specification/terminology.md +135 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
{
|
|
2
|
+
"profileVersion": "0.1",
|
|
3
|
+
"name": "incident-management",
|
|
4
|
+
"version": "0.1",
|
|
5
|
+
"status": "experimental",
|
|
6
|
+
"coreVersions": ["0.1"],
|
|
7
|
+
"title": "Incident Management Profile",
|
|
8
|
+
"description": "Additional conformance requirements for the lifecycle of incidents, problems and corrective actions: raising, reprioritising, assigning, escalating, resolving, closing, cancelling and reopening a case, recording a root cause analysis, and opening and verifying a corrective action. Every requirement adds to the OpenAuditModel Core Specification; none relaxes it. High-volume monitoring, timeline and read events such as monitoring.alert.raise, incident.note.create and incident.case.view are deliberately not governed.",
|
|
9
|
+
"rules": [
|
|
10
|
+
{
|
|
11
|
+
"id": "INC-CORE-001",
|
|
12
|
+
"description": "Every governed incident, problem and corrective-action operation records the authorization decision that permitted it and the lifecycle state the record was left in.",
|
|
13
|
+
"rationale": "A case record is rewritten by many hands over days or weeks, and the trail is read long after everyone involved has moved on. Without a recorded decision nothing distinguishes a transition policy allowed from one an operator forced through a console. Without the resulting state, a reviewer replaying the events cannot say what condition the case was actually in at any point, because the profile cannot look inside `/change/after` and a consumer would have to parse a free-form object to find out. Recording the state in one typed, namespaced field is what makes the sequence readable and indexable at all.",
|
|
14
|
+
"severity": "error",
|
|
15
|
+
"events": [
|
|
16
|
+
"corrective-action.close",
|
|
17
|
+
"corrective-action.open",
|
|
18
|
+
"corrective-action.verify",
|
|
19
|
+
"incident.assignment.change",
|
|
20
|
+
"incident.case.cancel",
|
|
21
|
+
"incident.case.close",
|
|
22
|
+
"incident.case.create",
|
|
23
|
+
"incident.case.reopen",
|
|
24
|
+
"incident.case.resolve",
|
|
25
|
+
"incident.major.declare",
|
|
26
|
+
"incident.priority.change",
|
|
27
|
+
"incident.rca.approve",
|
|
28
|
+
"incident.rca.create",
|
|
29
|
+
"incident.rca.update",
|
|
30
|
+
"problem.case.close",
|
|
31
|
+
"problem.case.create"
|
|
32
|
+
],
|
|
33
|
+
"requiredPaths": ["/authorization"],
|
|
34
|
+
"requiredMetadata": [
|
|
35
|
+
{
|
|
36
|
+
"path": "/incident/status",
|
|
37
|
+
"type": "string"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "INC-CORE-002",
|
|
43
|
+
"description": "A governed incident operation should carry the correlation identifier shared by the rest of the handling and name the resources the case is about.",
|
|
44
|
+
"rationale": "Incident handling is the clearest example in this specification of an operation that is not one event. Raising, escalating, resolving, analysing, remediating and closing are separate events emitted hours or weeks apart, often by different applications and principals, and they are assembled by correlation rather than by timestamp. These are recommended and not required because a conforming producer may have no correlation facility at all, and because an incident about a process rather than a system legitimately has no affected resource to name. A missing correlation identifier should prompt a question, not fail a build.",
|
|
45
|
+
"severity": "warning",
|
|
46
|
+
"events": [
|
|
47
|
+
"corrective-action.close",
|
|
48
|
+
"corrective-action.open",
|
|
49
|
+
"corrective-action.verify",
|
|
50
|
+
"incident.assignment.change",
|
|
51
|
+
"incident.case.cancel",
|
|
52
|
+
"incident.case.close",
|
|
53
|
+
"incident.case.create",
|
|
54
|
+
"incident.case.reopen",
|
|
55
|
+
"incident.case.resolve",
|
|
56
|
+
"incident.major.declare",
|
|
57
|
+
"incident.priority.change",
|
|
58
|
+
"incident.rca.approve",
|
|
59
|
+
"incident.rca.create",
|
|
60
|
+
"incident.rca.update",
|
|
61
|
+
"incident.sla.breach",
|
|
62
|
+
"problem.case.close",
|
|
63
|
+
"problem.case.create"
|
|
64
|
+
],
|
|
65
|
+
"recommendedPaths": ["/request/correlationId", "/relatedResources"]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "INC-CREATE-001",
|
|
69
|
+
"description": "Raising a case should record how the condition was noticed, when it began, and how badly it hurt.",
|
|
70
|
+
"rationale": "Detection time and business impact are known accurately only at the moment a case is opened, and are reconstructed badly or not at all afterwards. They are recommended rather than required because impact and urgency scales differ between organizations and some cases are raised by a customer with no measurable detection point, and a requirement that could not be met honestly would be satisfied with a placeholder instead.",
|
|
71
|
+
"severity": "warning",
|
|
72
|
+
"events": ["incident.case.create", "problem.case.create"],
|
|
73
|
+
"recommendedPaths": [
|
|
74
|
+
"/reason",
|
|
75
|
+
"/metadata/incident/detectedAt",
|
|
76
|
+
"/metadata/incident/impact",
|
|
77
|
+
"/metadata/incident/urgency"
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "INC-STATE-001",
|
|
82
|
+
"description": "An operation that moves a case between lifecycle states records the transition as a before and after.",
|
|
83
|
+
"rationale": "The state a case ends in is not the same fact as the state it came from. Only the transition shows that a case went from open to closed rather than from cancelled to closed, and only the transition makes an impossible sequence visible — a closure of something never resolved, a resolution of something already closed. An event that records the destination alone is a snapshot, and a trail of snapshots cannot be replayed.",
|
|
84
|
+
"severity": "error",
|
|
85
|
+
"events": [
|
|
86
|
+
"incident.priority.change",
|
|
87
|
+
"incident.assignment.change",
|
|
88
|
+
"incident.major.declare",
|
|
89
|
+
"incident.case.resolve",
|
|
90
|
+
"incident.case.close",
|
|
91
|
+
"incident.case.cancel",
|
|
92
|
+
"problem.case.close",
|
|
93
|
+
"corrective-action.close"
|
|
94
|
+
],
|
|
95
|
+
"requiredPaths": ["/change"]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "INC-STATE-002",
|
|
99
|
+
"description": "A transition that reflects a judgement — reprioritisation, escalation to major, closure or cancellation — records why it was made.",
|
|
100
|
+
"rationale": "These are the transitions where the record and the decision can diverge. Downgrading a priority, declaring a major incident, closing a case or abandoning one are all defensible and all abusable, and the difference is the justification. A case closed with no stated reason is indistinguishable from a case closed to make a queue look shorter, and that ambiguity is precisely what an incident trail exists to remove. Resolution and reassignment are excluded because they carry their own structured explanation.",
|
|
101
|
+
"severity": "error",
|
|
102
|
+
"events": [
|
|
103
|
+
"incident.priority.change",
|
|
104
|
+
"incident.major.declare",
|
|
105
|
+
"incident.case.close",
|
|
106
|
+
"incident.case.cancel",
|
|
107
|
+
"problem.case.close",
|
|
108
|
+
"corrective-action.close"
|
|
109
|
+
],
|
|
110
|
+
"requiredPaths": ["/reason"]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "INC-PRIORITY-001",
|
|
114
|
+
"description": "Raising a case, reassessing its priority, escalating it to major and reporting a missed commitment all record the priority in force.",
|
|
115
|
+
"rationale": "Priority is the field every downstream obligation hangs from: who is paged, how fast a response is owed, whether an escalation was warranted and whether a service commitment was met. Recorded only at creation, it cannot answer any question about the middle of the incident. The profile requires the field and deliberately does not close its vocabulary, because priority scales are an organizational choice and a profile that imposed one would be describing a single service desk rather than a domain.",
|
|
116
|
+
"severity": "error",
|
|
117
|
+
"events": [
|
|
118
|
+
"incident.case.create",
|
|
119
|
+
"problem.case.create",
|
|
120
|
+
"incident.priority.change",
|
|
121
|
+
"incident.major.declare",
|
|
122
|
+
"incident.sla.breach"
|
|
123
|
+
],
|
|
124
|
+
"requiredMetadata": [
|
|
125
|
+
{
|
|
126
|
+
"path": "/incident/priority",
|
|
127
|
+
"type": "string"
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"id": "INC-ASSIGN-001",
|
|
133
|
+
"description": "Handing a case or a corrective action to someone names the principal who became accountable for it.",
|
|
134
|
+
"rationale": "Accountability that is not recorded is not accountability. A reassignment or a corrective action opened without naming who now owns it produces a trail that shows work being moved and never shows who was asked to do it, which is the one question a follow-up review always asks.The principal is recorded in namespaced metadata rather than in `/subject`, because [actor-model.md](../../specification/actor-model.md) §5 defines `subject` as the principal *on whose behalf* the actor acted, and forbids its use as a generic target. A grantee or assignee is neither. This follows the identity profile, which records a role in `/metadata/role/id` for the same reason.",
|
|
135
|
+
"severity": "error",
|
|
136
|
+
"events": ["incident.assignment.change", "corrective-action.open"],
|
|
137
|
+
"requiredMetadata": [
|
|
138
|
+
{
|
|
139
|
+
"path": "/incident/assigneeId",
|
|
140
|
+
"type": "string"
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"recommendedPaths": ["/reason"]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"id": "INC-RESOLVE-001",
|
|
147
|
+
"description": "Resolving a case records what kind of resolution it was.",
|
|
148
|
+
"rationale": "A workaround, a permanent fix, a duplicate and a condition that disappeared on its own are four different outcomes with four different follow-up obligations, and a status of `resolved` collapses all of them into one word. The distinction is what tells a later reader whether the underlying fault is still present in production. The recommended fields point at the rest of the story: when the service was actually restored, and which corrective action carries the remaining work.",
|
|
149
|
+
"severity": "error",
|
|
150
|
+
"events": ["incident.case.resolve"],
|
|
151
|
+
"requiredMetadata": [
|
|
152
|
+
{
|
|
153
|
+
"path": "/incident/resolutionType",
|
|
154
|
+
"type": "string"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"recommendedPaths": [
|
|
158
|
+
"/reason",
|
|
159
|
+
"/metadata/incident/resolvedAt",
|
|
160
|
+
"/metadata/incident/correctiveAction/id"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"id": "INC-CLOSE-001",
|
|
165
|
+
"description": "When the producer has declared that closing this case required approval, the closure records the approval decision.",
|
|
166
|
+
"rationale": "Whether a closure needs a second pair of eyes is an organizational policy, not a fact this specification can decide: a service desk closing thousands of routine tickets a week and a safety board closing a reportable incident are both conforming, and requiring approval for every closure would describe one of them and be switched off by the other. The producer declares the obligation and the profile enforces the consequence. The rule requires the decision to be recorded, not to be favourable: an event whose approval is still pending or was rejected and whose outcome is success describes a control bypass, and the trail must be able to say so.",
|
|
167
|
+
"severity": "error",
|
|
168
|
+
"events": [
|
|
169
|
+
"incident.case.close",
|
|
170
|
+
"incident.case.cancel",
|
|
171
|
+
"problem.case.close",
|
|
172
|
+
"corrective-action.close"
|
|
173
|
+
],
|
|
174
|
+
"when": {
|
|
175
|
+
"path": "/metadata/incident/approvalRequired",
|
|
176
|
+
"equals": true
|
|
177
|
+
},
|
|
178
|
+
"requiredPaths": ["/approval/status"],
|
|
179
|
+
"recommendedPaths": ["/approval/approvers", "/approval/approvedAt"]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "INC-REOPEN-001",
|
|
183
|
+
"description": "Reopening a closed case is a new lifecycle transition, recorded with the transition it made and the reason it was made.",
|
|
184
|
+
"rationale": "A reopen is the strongest evidence an incident trail can carry that a resolution was wrong, so it is the transition most worth recording well. It is treated as a new auditable step and nothing more: the earlier closure and any approval attached to it remain valid historical records of what was decided at that time, and this profile does not assert that a reopen retracts them or that a fresh approval is owed, because that is an organizational process this specification does not make. Reusing the original correlation identifier, recommended by INC-CORE-002, is what keeps the whole handling readable as one story rather than two unrelated cases.",
|
|
185
|
+
"severity": "error",
|
|
186
|
+
"events": ["incident.case.reopen"],
|
|
187
|
+
"requiredPaths": ["/change", "/reason"],
|
|
188
|
+
"recommendedPaths": ["/evidence"]
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": "INC-RCA-001",
|
|
192
|
+
"description": "A root cause analysis event records the method the analysis was performed with.",
|
|
193
|
+
"rationale": "An analysis is trustworthy in proportion to how it was conducted, and the method is the only part of that a machine-readable trail can carry. Two organizations using different techniques still both know which one they used, so the field is universal even though its vocabulary is not, and it is left open for that reason. The profile does not attempt to require the analysis document itself: the rule language cannot inspect the contents of `/evidence`, so a supporting record is recommended by INC-EVIDENCE-001 rather than dishonestly claimed to be enforced.",
|
|
194
|
+
"severity": "error",
|
|
195
|
+
"events": ["incident.rca.approve", "incident.rca.create", "incident.rca.update"],
|
|
196
|
+
"requiredMetadata": [
|
|
197
|
+
{
|
|
198
|
+
"path": "/incident/rca/method",
|
|
199
|
+
"type": "string"
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"id": "INC-RCA-002",
|
|
205
|
+
"description": "Approving a root cause analysis records the approval decision.",
|
|
206
|
+
"rationale": "This event is an approval, so requiring the approval state is not an added process obligation but a requirement that the event describe itself. An approval event with no recorded decision states that an analysis was reviewed without stating what the review concluded. The status is required rather than required to be `approved`, so that a rejection or a lapsed request stays recordable and visible.",
|
|
207
|
+
"severity": "error",
|
|
208
|
+
"events": ["incident.rca.approve"],
|
|
209
|
+
"requiredPaths": ["/approval/status"],
|
|
210
|
+
"recommendedPaths": ["/approval/approvers", "/approval/approvedAt"]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"id": "INC-CAPA-001",
|
|
214
|
+
"description": "Verifying a corrective action records how the verification was carried out.",
|
|
215
|
+
"rationale": "Verification is the step that turns a promise into evidence, and an unqualified assertion that an action was verified is worth nothing: reading a ticket, watching production for a week and re-running the failure are all called verification and are not comparable. Recording the method is what lets a later reader judge whether the fix was actually demonstrated. The vocabulary is open because verification techniques are domain-specific.",
|
|
216
|
+
"severity": "error",
|
|
217
|
+
"events": ["corrective-action.verify"],
|
|
218
|
+
"requiredMetadata": [
|
|
219
|
+
{
|
|
220
|
+
"path": "/incident/correctiveAction/verificationMethod",
|
|
221
|
+
"type": "string"
|
|
222
|
+
}
|
|
223
|
+
],
|
|
224
|
+
"recommendedPaths": ["/metadata/incident/correctiveAction/verifiedAt"]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"id": "INC-SLA-001",
|
|
228
|
+
"description": "A missed service commitment names the commitment that was missed.",
|
|
229
|
+
"rationale": "A breach event that does not say what was breached records only that someone was unhappy. The commitment identifier is what lets the breach be tied to the obligation it violated and counted against it later, when the commitment itself may have been renegotiated. The profile requires the identifier and asserts nothing about durations or thresholds: the v0.1 rule language compares values for equality and cannot evaluate a numeric range, so a target is recorded as a name rather than checked as a bound. This event is excluded from INC-CORE-001 because a breach is a system observation rather than an authorized human action, and demanding an authorization decision for it would force producers to invent one.",
|
|
230
|
+
"severity": "error",
|
|
231
|
+
"events": ["incident.sla.breach"],
|
|
232
|
+
"requiredMetadata": [
|
|
233
|
+
{
|
|
234
|
+
"path": "/incident/sla/target",
|
|
235
|
+
"type": "string"
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
"recommendedPaths": ["/metadata/incident/sla/breachedAt"]
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"id": "INC-EVIDENCE-001",
|
|
242
|
+
"description": "Root-cause and corrective-action events should reference the material that supports them.",
|
|
243
|
+
"rationale": "The analysis document, the change record and the verification report are what make an incident conclusion checkable rather than merely stated, and `/evidence` is where the core model puts references to them. This is a recommendation and not a requirement for a reason worth stating plainly: the v0.1 rule language can test that `/evidence` is present and non-empty, and cannot test that it contains an entry of any particular type. A rule that required `/evidence` would be satisfied by an unrelated reference, so the profile asks for evidence rather than pretending to verify it.",
|
|
244
|
+
"severity": "warning",
|
|
245
|
+
"events": [
|
|
246
|
+
"corrective-action.close",
|
|
247
|
+
"corrective-action.open",
|
|
248
|
+
"corrective-action.verify",
|
|
249
|
+
"incident.rca.approve",
|
|
250
|
+
"incident.rca.create",
|
|
251
|
+
"incident.rca.update"
|
|
252
|
+
],
|
|
253
|
+
"recommendedPaths": ["/evidence"]
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Message Broker Management Profile
|
|
2
|
+
|
|
3
|
+
**Status: Experimental. Implemented in v0.1, 12 rules (11 enforceable).**
|
|
4
|
+
|
|
5
|
+
Scope: the **control plane** of message brokers, queues, streams and event logs — the administrative
|
|
6
|
+
operations performed by operators, platform teams and reconciliation tooling on clusters, topics,
|
|
7
|
+
queues, exchanges, streams, consumer groups, access control lists, quotas and broker configuration,
|
|
8
|
+
plus the two operational interventions that move data without changing it: offset resets and message
|
|
9
|
+
replay.
|
|
10
|
+
|
|
11
|
+
The profile is vendor-neutral. It describes operations that any broker performs, and assumes no
|
|
12
|
+
particular protocol, storage engine, management API or deployment model. It applies equally to a
|
|
13
|
+
log-structured event platform, a work-queue broker, a stream store and a hosted messaging service.
|
|
14
|
+
|
|
15
|
+
The enforceable rules are in [profile.json](profile.json).
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
auditmodel check-profile examples/profiles/message-broker-management/valid --profile message-broker-management
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Purpose
|
|
22
|
+
|
|
23
|
+
A broker is where an organization's data is in motion, and its control plane is the smallest set of
|
|
24
|
+
operations that can lose all of it. Deleting a topic, purging a queue, shortening a retention window
|
|
25
|
+
or rewinding a consumer group destroys or duplicates messages that no other system holds a copy of,
|
|
26
|
+
and the audit event is usually the only surviving record that the operation happened at all. Broker
|
|
27
|
+
access control is also where the confidentiality of every topic on a cluster is actually decided,
|
|
28
|
+
which is why this profile governs ACL administration rather than deferring it to the identity
|
|
29
|
+
domain.
|
|
30
|
+
|
|
31
|
+
## What this profile governs
|
|
32
|
+
|
|
33
|
+
| Event family | Governed |
|
|
34
|
+
| --------------------------------------------------------------------- | -------------------- |
|
|
35
|
+
| `broker.cluster.create`, `.scale`, `.upgrade`, `.failover`, `.delete` | yes, by exact name |
|
|
36
|
+
| `broker.topic.*` | yes |
|
|
37
|
+
| `broker.queue.*` | yes |
|
|
38
|
+
| `broker.exchange.*` | yes |
|
|
39
|
+
| `broker.stream.*` | yes |
|
|
40
|
+
| `broker.acl.*`, `broker.permission.*` | yes |
|
|
41
|
+
| `broker.quota.*` | yes |
|
|
42
|
+
| `broker.configuration.*` | yes |
|
|
43
|
+
| `broker.consumer-group.create`, `.update`, `.delete` | yes, by exact name |
|
|
44
|
+
| `broker.offset.reset` | yes, by exact name |
|
|
45
|
+
| `broker.message.replay` | yes, by exact name |
|
|
46
|
+
| `broker.message.publish`, `.consume`, `.acknowledge`, `.deliver` | **no** — data plane |
|
|
47
|
+
| `broker.consumer-group.rebalance`, `.lag-report` | **no** — automatic |
|
|
48
|
+
| `broker.cluster.health-check` and other liveness or telemetry events | **no** — operational |
|
|
49
|
+
|
|
50
|
+
### Explicit exclusions, and why
|
|
51
|
+
|
|
52
|
+
**The data plane.** A broker emits a publish or consume event for every message it carries. Requiring
|
|
53
|
+
an authorization decision, a destructiveness declaration and a cluster identifier on each of them
|
|
54
|
+
would add cost to the highest-volume event in the entire system in exchange for almost no review
|
|
55
|
+
value, and the requirement would be switched off rather than met.
|
|
56
|
+
|
|
57
|
+
The exclusion is **structural**, not a matter of discipline: every governed event is selected by
|
|
58
|
+
**exact name**, so an operation is governed because it appears in the profile, never because it
|
|
59
|
+
happens to share a family with one that does.
|
|
60
|
+
|
|
61
|
+
That matters more than it first appears. An earlier draft selected the administrative families by
|
|
62
|
+
prefix — `broker.topic.`, `broker.queue.`, `broker.stream.` — which reads as safe until you notice
|
|
63
|
+
that [event-naming.md](../../semantic-conventions/event-naming.md) tells producers to put the
|
|
64
|
+
**resource** in the middle segment. A publish to a topic is then plausibly `broker.topic.publish`,
|
|
65
|
+
and the profile's heaviest requirements would have landed on the highest-volume event in the estate.
|
|
66
|
+
Naming a data-plane operation `broker.message.publish` avoided it, but that was the producer's
|
|
67
|
+
discipline doing the work, not the profile's. Exact names remove the assumption.
|
|
68
|
+
|
|
69
|
+
Consumer-group and cluster administration are selected by exact name for the same reason, with an
|
|
70
|
+
additional one: `broker.consumer-group.rebalance` is emitted by the broker itself, has no human
|
|
71
|
+
actor and no authorization decision, and can fire many times a minute during a deployment.
|
|
72
|
+
|
|
73
|
+
The same reasoning applies to `broker.cluster.*`: cluster administration is selected by five exact
|
|
74
|
+
names, so a producer that emits `broker.cluster.health-check` from a liveness probe is not dragged
|
|
75
|
+
into scope. Tests assert each of these, because widening one prefix later would silently start
|
|
76
|
+
governing every message a broker delivers.
|
|
77
|
+
|
|
78
|
+
Excluded does not mean unaudited. A publish event is still a conforming OpenAuditModel event, and
|
|
79
|
+
[data-access.md](../../semantic-conventions/data-access.md) covers recording data-plane activity.
|
|
80
|
+
|
|
81
|
+
**Message payloads.** They are never recorded, by any rule in this profile, in any circumstance. See
|
|
82
|
+
[Privacy considerations](#privacy-considerations).
|
|
83
|
+
|
|
84
|
+
## Rules
|
|
85
|
+
|
|
86
|
+
| Rule | Applies to | Requires |
|
|
87
|
+
| ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
|
|
88
|
+
| `BROKER-CORE-001` | every governed event | `/authorization`, `/metadata/broker/system`, `.../clusterId` |
|
|
89
|
+
| `BROKER-CORE-002` | every governed event | _recommends_ reason, authentication, parent, correlation ID |
|
|
90
|
+
| `BROKER-RISK-001` | every governed event | `/metadata/broker/operation/destructive` as a boolean |
|
|
91
|
+
| `BROKER-RISK-002` | declared-destructive operations | `/reason`; recommends `/approval` |
|
|
92
|
+
| `BROKER-RISK-003` | operations declared to need approval | `/approval` |
|
|
93
|
+
| `BROKER-FAIL-001` | governed events with outcome failure | `/event/error/type`; recommends `/event/error/retryable` |
|
|
94
|
+
| `BROKER-LIFECYCLE-001` | topic, queue, stream, exchange create | `/resource/classification`; recommends `/resource/ownerId` |
|
|
95
|
+
| `BROKER-CHANGE-001` | configuration, ACL, quota, resource and consumer-group `.update`, offset reset | `/change`; recommends `/change/changedFields` |
|
|
96
|
+
| `BROKER-ACL-001` | `broker.acl.*`, `broker.permission.*` | `/metadata/broker/acl/principalId`, `/metadata/broker/acl/permission` |
|
|
97
|
+
| `BROKER-QUOTA-001` | `broker.quota.*` | `/metadata/broker/quota/dimension` |
|
|
98
|
+
| `BROKER-OFFSET-001` | `broker.offset.reset` | `/reason`, `/metadata/broker/offset/previous` and `.../target` |
|
|
99
|
+
| `BROKER-REPLAY-001` | `broker.message.replay` | `/reason`, `/metadata/broker/replay/scope` |
|
|
100
|
+
|
|
101
|
+
`BROKER-CORE-002` is the only rule with severity `warning`; the other eleven fail conformance. Each
|
|
102
|
+
rule's full text and rationale is in [profile.json](profile.json).
|
|
103
|
+
|
|
104
|
+
`BROKER-CHANGE-001` deliberately does not select `broker.cluster.scale` or `broker.cluster.upgrade`.
|
|
105
|
+
A cluster operation is frequently driven by a release pipeline that already records the transition in
|
|
106
|
+
its own deployment event, and requiring `/change` here would push producers into duplicating it under
|
|
107
|
+
two names — the coupling described under [cross-profile overlaps](#cross-profile-overlaps). The
|
|
108
|
+
`cluster-upgrade` fixture records `/change` anyway, because a producer that holds the version
|
|
109
|
+
transition should write it down.
|
|
110
|
+
|
|
111
|
+
## Metadata namespace
|
|
112
|
+
|
|
113
|
+
Every metadata requirement in this profile lives under `/metadata/broker/`.
|
|
114
|
+
|
|
115
|
+
| Path | Type | Required by | Meaning |
|
|
116
|
+
| ---------------------------------------- | ------- | ------------------- | ------------------------------------------------------- |
|
|
117
|
+
| `/metadata/broker/system` | string | `BROKER-CORE-001` | Broker family or protocol the operation targeted |
|
|
118
|
+
| `/metadata/broker/clusterId` | string | `BROKER-CORE-001` | Broker deployment the operation acted on |
|
|
119
|
+
| `/metadata/broker/operation/destructive` | boolean | `BROKER-RISK-001` | Whether the operation discards data or withdraws access |
|
|
120
|
+
| `/metadata/broker/approvalRequired` | boolean | — | Producer's declaration that approval policy applied |
|
|
121
|
+
| `/metadata/broker/acl/permission` | string | `BROKER-ACL-001` | Permission granted, changed or withdrawn |
|
|
122
|
+
| `/metadata/broker/acl/effect` | string | _recommended_ | Whether the rule allows or denies |
|
|
123
|
+
| `/metadata/broker/quota/dimension` | string | `BROKER-QUOTA-001` | Dimension of consumption the quota limits |
|
|
124
|
+
| `/metadata/broker/offset/previous` | string | `BROKER-OFFSET-001` | Consumer position before the reset |
|
|
125
|
+
| `/metadata/broker/offset/target` | string | `BROKER-OFFSET-001` | Consumer position after the reset |
|
|
126
|
+
| `/metadata/broker/offset/strategy` | string | _recommended_ | How the target position was chosen |
|
|
127
|
+
| `/metadata/broker/replay/scope` | string | `BROKER-REPLAY-001` | How much was replayed |
|
|
128
|
+
| `/metadata/broker/replay/messageCount` | number | _recommended_ | How many messages the replay covered |
|
|
129
|
+
|
|
130
|
+
All values are open vocabularies. The profile does not enumerate broker families, quota dimensions,
|
|
131
|
+
reset strategies or replay scopes, because each is a moving target across products and none can be
|
|
132
|
+
closed without excluding a broker somebody runs. Nested, namespaced keys are used throughout so that
|
|
133
|
+
two profiles governing the same event cannot assign different meanings to one key — an `expiresAt`
|
|
134
|
+
on a share and a `scope` on a replay are not the same fact as anything else called `expiresAt` or
|
|
135
|
+
`scope`.
|
|
136
|
+
|
|
137
|
+
### Deliberate non-requirements in metadata
|
|
138
|
+
|
|
139
|
+
- **The managed resource type is not duplicated into metadata.** `resource.type` is already REQUIRED
|
|
140
|
+
by the core model and already carries `topic`, `queue`, `stream`, `exchange`, `consumer-group` or
|
|
141
|
+
`cluster`. A `/metadata/broker/resourceType` alongside it would be a second copy of a core field,
|
|
142
|
+
which [event-model.md](../../specification/event-model.md) prohibits, and two copies eventually
|
|
143
|
+
disagree.
|
|
144
|
+
- **The resource name is not required.** `resource.id` is REQUIRED by the core model, and topic and
|
|
145
|
+
queue names routinely encode customer, tenant or product information. `resource.name` remains
|
|
146
|
+
available where the producer judges it safe.
|
|
147
|
+
- **Partition, node and broker identifiers are not required.** They are meaningful on some broker
|
|
148
|
+
families and meaningless on others. `/metadata/broker/offset/partition` appears in the fixtures as
|
|
149
|
+
an illustration, not as a requirement.
|
|
150
|
+
|
|
151
|
+
## Conditional-policy fields
|
|
152
|
+
|
|
153
|
+
The v0.1 rule language offers exactly one conditional mechanism: one path compared for equality
|
|
154
|
+
against one scalar. This profile uses it three times, and each condition is a fact the **producer**
|
|
155
|
+
declares, because none of the three can be inferred from an event name.
|
|
156
|
+
|
|
157
|
+
| Condition | Rule | Consequence |
|
|
158
|
+
| -------------------------------------------------- | ----------------- | ----------------------------------------------- |
|
|
159
|
+
| `/metadata/broker/operation/destructive` is `true` | `BROKER-RISK-002` | `/reason` becomes required, `/approval` advised |
|
|
160
|
+
| `/metadata/broker/approvalRequired` is `true` | `BROKER-RISK-003` | `/approval` becomes required |
|
|
161
|
+
| `/event/outcome` is `"failure"` | `BROKER-FAIL-001` | `/event/error/type` becomes required |
|
|
162
|
+
|
|
163
|
+
`destructive` and `approvalRequired` are deliberately separate facts. Destructiveness is a property
|
|
164
|
+
of the **operation** — does it discard messages, positions or access? Approval need is a property of
|
|
165
|
+
the **organization's policy** for that operation, and differs by cluster, environment and team. Only
|
|
166
|
+
destructiveness is required to be recorded, because only it is knowable by the producing system
|
|
167
|
+
without reference to a policy the audit model does not hold.
|
|
168
|
+
|
|
169
|
+
When a condition's path is absent the condition does **not** hold and the rule contributes nothing.
|
|
170
|
+
That is why `BROKER-RISK-001` requires the destructiveness flag unconditionally: without it,
|
|
171
|
+
`BROKER-RISK-002` would quietly stop applying to every producer that omitted the field.
|
|
172
|
+
|
|
173
|
+
## Approval model
|
|
174
|
+
|
|
175
|
+
**Approval is never required unconditionally.** A very large share of legitimate broker
|
|
176
|
+
administration is unilateral and should be — clearing a development queue, creating a topic for a
|
|
177
|
+
team's own service, raising a quota the same operator lowered an hour earlier. A rule that demanded a
|
|
178
|
+
second signature for all of it would describe one organization's change process and be ignored
|
|
179
|
+
everywhere else.
|
|
180
|
+
|
|
181
|
+
Instead:
|
|
182
|
+
|
|
183
|
+
- `BROKER-RISK-002` **recommends** `/approval` for any operation the producer declared destructive,
|
|
184
|
+
so an unapproved destructive operation produces a warning and a reviewable signal.
|
|
185
|
+
- `BROKER-RISK-003` **requires** `/approval` only when the producer declared that approval policy
|
|
186
|
+
applied to this operation.
|
|
187
|
+
|
|
188
|
+
`BROKER-RISK-003` requires the approval object to be **present**, not to be granted. A rejected,
|
|
189
|
+
expired or pending approval satisfies it. An audit trail that could only record approved changes
|
|
190
|
+
would be unable to record the most interesting event in the domain: the change a control stopped.
|
|
191
|
+
|
|
192
|
+
## Privacy considerations
|
|
193
|
+
|
|
194
|
+
**Message payloads MUST NOT be captured.** This is a core rule
|
|
195
|
+
([privacy.md](../../specification/privacy.md) §2), restated here because this is the domain where it
|
|
196
|
+
is most often violated: a broker console has the payload in hand and recording it is one line of
|
|
197
|
+
code. No rule in this profile requires message content, and no fixture contains any.
|
|
198
|
+
|
|
199
|
+
- An offset reset records two **positions**, never the messages between them.
|
|
200
|
+
- A replay records a **scope and a count**, never what was replayed.
|
|
201
|
+
- A purge records **how many** messages were discarded, never which.
|
|
202
|
+
- An ACL change records a **permission and a principal identifier**, never a credential, connection
|
|
203
|
+
string or bootstrap address.
|
|
204
|
+
|
|
205
|
+
Where an operator has a genuine need to preserve message content — a dispute over what was published
|
|
206
|
+
— that content is **evidence**, stored under its own access controls and referenced from the event.
|
|
207
|
+
See [evidence-model.md](../../specification/evidence-model.md).
|
|
208
|
+
|
|
209
|
+
Broker identifiers are also a disclosure surface. `clusterId`, topic names and consumer-group names
|
|
210
|
+
should be stable, opaque, lower-case identifiers. Host names, ports, bootstrap servers and
|
|
211
|
+
connection strings expose internal infrastructure and are rejected by `auditmodel lint-privacy`;
|
|
212
|
+
none appears in any fixture.
|
|
213
|
+
|
|
214
|
+
## Known rule-language limitations
|
|
215
|
+
|
|
216
|
+
The v0.1 rule language checks presence, JSON type and scalar equality. These requirements are real
|
|
217
|
+
and are documented here rather than pretended into rules:
|
|
218
|
+
|
|
219
|
+
- **Positions cannot be compared.** `BROKER-OFFSET-001` requires both the previous and the target
|
|
220
|
+
position, but nothing can assert that they differ, that the target is earlier, or how many messages
|
|
221
|
+
lie between them. A reviewer reads both values; the engine only confirms they were recorded.
|
|
222
|
+
- **Numeric ranges cannot be checked.** A quota change records its dimension; the engine cannot say
|
|
223
|
+
whether the new limit is an increase, a decrease or a hundredfold jump, and a limit is only
|
|
224
|
+
meaningful against a deployment's own baseline anyway.
|
|
225
|
+
- **Positions are typed as strings.** Broker position types are irreconcilable — a 64-bit log offset,
|
|
226
|
+
a stream entry identifier, a composite message identifier — and the rule language has no union
|
|
227
|
+
type. A string carries all of them; a number would exclude most brokers.
|
|
228
|
+
- **Cross-field consistency cannot be expressed.** The profile cannot require that an event whose
|
|
229
|
+
authorization decision is `deny` also has outcome `failure`, because that needs two conditions.
|
|
230
|
+
- **Array contents are never inspected.** The profile cannot assert that `relatedResources` contains
|
|
231
|
+
the affected cluster, or that `/approval/approvers` holds someone other than the actor.
|
|
232
|
+
- **Only one condition per rule.** "Destructive **and** in production" is not expressible; a producer
|
|
233
|
+
that wants environment-sensitive strictness must express it in its own policy engine.
|
|
234
|
+
- **A prefix cannot exclude a verb.** Seven families are selected by prefix, so a read-shaped name
|
|
235
|
+
inside a governed family — `broker.acl.list`, `broker.topic.describe` — is selected too, and
|
|
236
|
+
`BROKER-ACL-001` or `BROKER-CHANGE-001` then asks it for a `/subject` or a `/change` that a listing
|
|
237
|
+
does not have. There is no negative selector in v0.1, and enumerating every mutating verb every
|
|
238
|
+
broker exposes would exclude the next one. This profile therefore assumes the prefixed families
|
|
239
|
+
carry operations that change something; a producer that audits administrative reads as well should
|
|
240
|
+
give them their own domain segment rather than a governed prefix. The same shape appears in
|
|
241
|
+
`document-management`, where `document.permission.` requires `/subject`, so it is a property of the
|
|
242
|
+
rule language rather than of this profile. The exclusions that matter — the data plane, automatic
|
|
243
|
+
rebalancing and liveness probes — are held by exact-name selection instead, which a prefix cannot
|
|
244
|
+
undo.
|
|
245
|
+
|
|
246
|
+
## Cross-profile overlaps
|
|
247
|
+
|
|
248
|
+
- **Identity and access management.** Broker ACL administration is governed here, not by
|
|
249
|
+
`identity.*`. A broker ACL is scoped to a broker resource, expressed in a broker's permission
|
|
250
|
+
vocabulary and administered by the platform team that runs the cluster; routing it into the
|
|
251
|
+
identity domain would require that profile to understand topic patterns and permission types it
|
|
252
|
+
has no reason to model. The event may still carry `/subject`, `/authorization` and identity-shaped
|
|
253
|
+
resources, and an organization that reviews all access changes together can select on
|
|
254
|
+
`event.type` (`grant`, `revoke`) across both profiles.
|
|
255
|
+
- **Deployment and change management.** A broker upgrade is both a cluster operation and a release.
|
|
256
|
+
`broker.cluster.upgrade` is governed here; a pipeline that also emits a deployment event should
|
|
257
|
+
correlate the two through `/request/correlationId` or `/change/deploymentId` rather than
|
|
258
|
+
duplicating one event under two names.
|
|
259
|
+
- **Incident management.** Offset resets, purges and replays are usually performed during an
|
|
260
|
+
incident. This profile requires the justification; the incident record itself belongs to the
|
|
261
|
+
incident domain, linked through `/change/incidentId` and `/reason/reference`.
|
|
262
|
+
- **Backup and recovery.** Replaying from a dead-letter queue is a broker operation; restoring a
|
|
263
|
+
cluster from a snapshot is a recovery operation. The dividing line is whether the data came back
|
|
264
|
+
from the broker or from a backup.
|
|
265
|
+
|
|
266
|
+
## Fixture matrix
|
|
267
|
+
|
|
268
|
+
[examples/profiles/message-broker-management/](../../examples/profiles/message-broker-management/) —
|
|
269
|
+
twelve valid, twelve invalid, three not-applicable.
|
|
270
|
+
|
|
271
|
+
| Family | Valid fixture | Negative fixture |
|
|
272
|
+
| --------------------- | ------------------------------ | --------------------------------------------------------------------------------- |
|
|
273
|
+
| Topic lifecycle | `topic-create`, `topic-delete` | missing authorization, cluster, classification, approval |
|
|
274
|
+
| Topic configuration | `topic-retention-reduce` | — |
|
|
275
|
+
| Failed administration | `topic-delete-denied` | missing error type |
|
|
276
|
+
| Queue administration | `queue-purge` | missing destructive flag, missing reason |
|
|
277
|
+
| Access control | `acl-grant` | missing subject |
|
|
278
|
+
| Quota | `quota-update` | missing dimension |
|
|
279
|
+
| Broker configuration | `configuration-update` | missing change |
|
|
280
|
+
| Consumer group | `consumer-group-delete` | — |
|
|
281
|
+
| Cluster | `cluster-upgrade` | — |
|
|
282
|
+
| Offset | `offset-reset` | missing target position |
|
|
283
|
+
| Replay | `message-replay` | missing scope |
|
|
284
|
+
| Data plane | — | `message-publish`, `message-consume`, `consumer-group-rebalance` (not applicable) |
|
|
285
|
+
|
|
286
|
+
Every fixture — valid, invalid and not-applicable alike — is core-conforming and privacy-clean. Every
|
|
287
|
+
invalid fixture is core-**valid** and fails exactly one profile rule, at one pointer.
|
|
288
|
+
|
|
289
|
+
## Not-applicable rationale
|
|
290
|
+
|
|
291
|
+
Three fixtures exist to hold the exclusions in place rather than to demonstrate a requirement:
|
|
292
|
+
|
|
293
|
+
- `message-publish.json` and `message-consume.json` are ordinary data-plane events. If a future edit
|
|
294
|
+
widened any selector to a bare `broker.` or `broker.message.` prefix, they would start conforming
|
|
295
|
+
instead of being skipped, and the test would fail — which is the point, because that edit would
|
|
296
|
+
impose an authorization requirement on every message a broker carries.
|
|
297
|
+
- `consumer-group-rebalance.json` is emitted by the broker itself, with a `system` actor and no
|
|
298
|
+
authorization decision. It holds the decision to select consumer-group administration by exact
|
|
299
|
+
name.
|
|
300
|
+
|
|
301
|
+
`check-profile` reports all three as not applicable and exits `3`. **Not applicable is not
|
|
302
|
+
conformance**: it means the profile said nothing about the event, not that the event satisfied it.
|
|
303
|
+
|
|
304
|
+
## Relationship to the existing `kafka.*` examples
|
|
305
|
+
|
|
306
|
+
The repository already ships
|
|
307
|
+
[examples/valid/kafka-consumer-offset-reset.json](../../examples/valid/kafka-consumer-offset-reset.json),
|
|
308
|
+
which follows the family-scoped naming that
|
|
309
|
+
[privileged-operations.md](../../semantic-conventions/privileged-operations.md) §6 suggests
|
|
310
|
+
(`kafka.consumer.offset-reset`, `rabbitmq.queue.purge`, `redis.key.delete`). This profile governs the
|
|
311
|
+
vendor-neutral `broker.*` vocabulary only, and does **not** select family-scoped names. Two reasons:
|
|
312
|
+
|
|
313
|
+
1. A family-scoped prefix such as `kafka.` cannot be used as a selector without sweeping in
|
|
314
|
+
`kafka.message.publish`, which reintroduces exactly the data-plane problem the profile is
|
|
315
|
+
structured to avoid — and enumerating one family's admin names invites doing it for every broker
|
|
316
|
+
anyone runs.
|
|
317
|
+
2. The broker family is carried as data, in `/metadata/broker/system`, where it can be filtered,
|
|
318
|
+
aggregated and extended without a new event name and without a new rule.
|
|
319
|
+
|
|
320
|
+
The consequence is recorded honestly rather than hidden: `kafka.consumer.offset-reset` is
|
|
321
|
+
**not applicable** to this profile.
|
|
322
|
+
|
|
323
|
+
It is a real inconsistency, not a curiosity. §6 recommends family-scoped names **and** links to this
|
|
324
|
+
profile in the same list, so a producer that follows the convention literally arrives at a profile
|
|
325
|
+
that governs none of its events. The two documents have to be reconciled, and there are only two
|
|
326
|
+
ways to do it: §6 starts recommending the vendor-neutral `broker.*` names, or this profile gains
|
|
327
|
+
family-scoped exact-name selectors for the operations §6 enumerates — exact names, never a `kafka.`
|
|
328
|
+
prefix. Either edit reaches outside this directory, which is why it is written down here rather than
|
|
329
|
+
left to be discovered by the first producer to run `check-profile` and be told its offset reset is
|
|
330
|
+
not applicable.
|
|
331
|
+
|
|
332
|
+
## Open questions
|
|
333
|
+
|
|
334
|
+
- Should a multi-partition or multi-topic offset reset be one event or one event per partition? The
|
|
335
|
+
profile accepts either; `resource` holds the consumer group and `relatedResources` the topics.
|
|
336
|
+
- Cache and key-space operations (`redis.key.delete` and its equivalents) can affect an unbounded
|
|
337
|
+
number of keys. A scope declaration would be the right requirement, but there is not yet enough
|
|
338
|
+
adoption evidence to say whether it should be a pattern, a count or a named key space.
|
|
339
|
+
- Should `broker.subscription.*` be governed? A durable subscription is administrative on some
|
|
340
|
+
platforms and a per-client, per-session operation on others, where governing it would recreate the
|
|
341
|
+
data-plane problem. It is currently out of scope, and subscriptions that are genuinely
|
|
342
|
+
administrative should be recorded as queues or consumer groups.
|
|
343
|
+
- Is `destructive` the right discriminator, or should the profile distinguish "discards data" from
|
|
344
|
+
"withdraws access"? A single flag is currently enough to make the reviewable set small.
|