@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,359 @@
1
+ # Deployment and Change Management Profile
2
+
3
+ **Status: Experimental. Implemented in v0.1, 13 rules, 11 of them enforceable.**
4
+
5
+ Scope: applications that build, approve, deploy, revert and reconfigure running systems — delivery
6
+ pipelines, release management, infrastructure automation, configuration management and change
7
+ management.
8
+
9
+ The profile is vendor-neutral. It describes operations that any delivery system performs, not the
10
+ feature list of any product, and it names no pipeline tool, cloud, orchestrator or version control
11
+ system. It assumes no branching model, no environment naming scheme, no versioning scheme and no
12
+ approval workflow engine.
13
+
14
+ The enforceable rules are in [profile.json](profile.json).
15
+
16
+ ```bash
17
+ auditmodel check-profile examples/profiles/deployment-and-change-management/valid --profile deployment-and-change-management
18
+ ```
19
+
20
+ ## Purpose
21
+
22
+ A deployment trail answers three questions that nothing else in a system can answer: what is running
23
+ where, who decided it should be, and what happened when it was put there. Those answers are cheap to
24
+ record at the moment of the change and expensive to reconstruct afterwards, because the pipeline that
25
+ knew them has already moved on and the environment now shows only its current state.
26
+
27
+ This profile requires the fields that make those answers survive. It does not require a delivery
28
+ process, and it does not decide which changes are risky enough to need a person.
29
+
30
+ ## Scope
31
+
32
+ | Event family | Governed |
33
+ | ----------------------------------------------------------------------------------------------- | -------- |
34
+ | `deployment.release.*` — `create`, `approve`, `deploy`, `promote`, `rollback`, `cancel` | yes |
35
+ | `deployment.infrastructure.*` — `apply` and its siblings | yes |
36
+ | `configuration.setting.create`, `.update`, `.delete` | yes |
37
+ | `configuration.secret.rotate` | yes |
38
+ | `configuration.feature.toggle`, `configuration.policy.update`, `configuration.retention.update` | yes |
39
+ | `change.request.*` — `create`, `approve`, `reject`, `close` | yes |
40
+ | `configuration.secret.access` | **no** |
41
+ | `deployment.pipeline.*`, `deployment.build.*` and other pipeline telemetry | **no** |
42
+
43
+ The vocabulary comes from
44
+ [configuration-and-change.md](../../semantic-conventions/configuration-and-change.md). This profile
45
+ invents no parallel names. It additionally governs `deployment.release.cancel` and
46
+ `deployment.release.promote`, which follow the same pattern and are reached through the
47
+ `deployment.release.` prefix; producers that do not emit them are unaffected.
48
+
49
+ ## Explicit exclusions
50
+
51
+ **Pipeline telemetry.** A delivery platform emits far more events about itself than about change:
52
+ runners poll for work, build steps append log lines, health probes report, agents send heartbeats.
53
+ Requiring a change identifier, a target environment and an approval-policy flag on each of them would
54
+ add cost to the highest-volume events in the system for no review value, and the requirement would be
55
+ switched off rather than met.
56
+
57
+ **Secret reads.** `configuration.secret.access` records that a workload read a credential it needs to
58
+ start. In most deployments that happens on every process start, and
59
+ [configuration-and-change.md §6](../../semantic-conventions/configuration-and-change.md) already
60
+ warns that recording every read may be less useful than recording grants, rotations and
61
+ out-of-pattern access. Rotation is governed; reading is not.
62
+
63
+ **Builds and tests.** A build produces an artifact; it does not change a running system. Build
64
+ events are legitimate audit events, and this profile has nothing to add to them.
65
+
66
+ The exclusions are structural, not a matter of discipline. **No selector in this profile uses a bare
67
+ `deployment.`, `configuration.` or `change.` prefix**, and the configuration family is selected by
68
+ exact event name rather than by a `configuration.setting.` prefix, so a future
69
+ `configuration.setting.read` cannot be swept in by accident either. Three not-applicable fixtures and
70
+ a test hold that boundary, because widening one prefix later would silently impose requirements on
71
+ every poll in every pipeline. A further test requires every prefix to name an **object within a
72
+ category** rather than a whole category, so `deployment.` cannot be reintroduced by editing a list of
73
+ names.
74
+
75
+ The boundary is deliberately not symmetric, and the asymmetry is worth stating plainly. Exactly two
76
+ prefixes remain: `deployment.release.` and `deployment.infrastructure.`. A verb added under either
77
+ later — a `deployment.release.list`, a `deployment.infrastructure.plan` — would be governed without
78
+ anyone deciding that it should be, and would then have to carry a change identifier, a target
79
+ environment and an approval-policy flag.
80
+
81
+ The configuration and change-request families are selected by **exact name** instead.
82
+ `configuration.` already contains a read, and `change.request.` is worse: a change-management tool
83
+ emits comments, watches, views and list operations continuously, and governing
84
+ `change.request.comment` would have put an approval-policy flag on a comment. That is the trade in
85
+ both directions — a new `deployment.release.*` verb is far more likely to be material change than
86
+ telemetry, so governing it by accident costs a failing check rather than an unaudited change, while a
87
+ new `change.request.*` verb is far more likely to be chatter.
88
+
89
+ A producer that needs a read-only verb under either remaining prefix should raise it against this
90
+ profile rather than route around it.
91
+
92
+ Excluded does not mean unaudited. Every excluded event is still a conforming OpenAuditModel event.
93
+
94
+ ## Event families and what each one must carry
95
+
96
+ | Family | Beyond the core requirements |
97
+ | ------------------------------------------------- | -------------------------------------------------------------- |
98
+ | Everything governed | change identifier, target environment, approval-policy flag |
99
+ | `deployment.release.*` | the version concerned |
100
+ | `deployment.infrastructure.*` | the version concerned |
101
+ | Events that change what is running | the version replaced |
102
+ | Rollback and cancellation | authorization decision, justification |
103
+ | Configuration, feature, policy, retention, secret | authorization decision, the names of the settings that changed |
104
+ | Approval decisions | the resulting approval status |
105
+ | Anything executed under a required approval | the approval status it ran under |
106
+ | Anything flagged as bypassing the path | a justification |
107
+ | Anything that failed or partly applied | the state the target was left in |
108
+
109
+ ## Rules
110
+
111
+ | Rule | Severity | Applies to | Requires |
112
+ | ----------------------- | -------- | ----------------------------------------------------- | ------------------------------------------------------------- |
113
+ | `DEPLOY-CORE-001` | error | every governed event | `/metadata/deployment/id`, `/metadata/deployment/environment` |
114
+ | `DEPLOY-CORE-002` | error | every governed event | `/metadata/deployment/approvalRequired` (boolean) |
115
+ | `DEPLOY-CORE-003` | warning | every governed event | _recommends_ `/reason`, correlation ID, pipeline ID |
116
+ | `DEPLOY-RELEASE-001` | error | `deployment.release.*`, `deployment.infrastructure.*` | `/metadata/deployment/version` |
117
+ | `DEPLOY-RELEASE-002` | error | deploy, promote, rollback, infrastructure apply | `/metadata/deployment/previousVersion` |
118
+ | `DEPLOY-REVERT-001` | error | `deployment.release.rollback`, `.cancel` | `/authorization`, `/reason` |
119
+ | `DEPLOY-CONFIG-001` | error | the configuration family | `/authorization`, `/change/changedFields` |
120
+ | `DEPLOY-APPROVAL-001` | error | events that are approval decisions | `/approval/status` |
121
+ | `DEPLOY-APPROVAL-002` | error | executing events, when approval was required | `/approval/status` |
122
+ | `DEPLOY-EMERGENCY-001` | error | every governed event, when flagged emergency | `/reason` |
123
+ | `DEPLOY-FAILURE-001` | error | every governed event, when `outcome: failure` | `/metadata/deployment/resultingState` |
124
+ | `DEPLOY-FAILURE-002` | error | every governed event, when `outcome: partial` | `/metadata/deployment/resultingState` |
125
+ | `DEPLOY-AUTOMATION-001` | warning | executing events performed by a service | _recommends_ `/subject`, `/delegation` |
126
+
127
+ Each rule's full text and rationale is in [profile.json](profile.json).
128
+
129
+ ## Metadata namespace
130
+
131
+ Every metadata requirement lives under `/metadata/deployment/`. Namespacing keeps two profiles from
132
+ assigning different meanings to the same key when an event is governed by both: a `version` on a
133
+ release and a `version` on a document are not the same fact.
134
+
135
+ | Field | Type | Required | Records |
136
+ | ------------------ | ------- | ---------------------------------- | -------------------------------------------------------------- |
137
+ | `id` | string | always | The deployment, release or change record this event belongs to |
138
+ | `environment` | string | always | The environment the change was applied **to** |
139
+ | `approvalRequired` | boolean | always | Whether policy required an approval for this change |
140
+ | `version` | string | release and infrastructure events | The version the event concerns |
141
+ | `previousVersion` | string | events that change what is running | The version replaced |
142
+ | `resultingState` | string | on `failure` and `partial` | The state the target was left in |
143
+ | `emergency` | boolean | never; conditional trigger | Whether the normal change path was bypassed |
144
+ | `pipelineId` | string | recommended | The pipeline or automation that produced the change |
145
+
146
+ Producers may add further fields under the namespace. The fixtures show `strategy`,
147
+ `instancesUpdated`, `secretType` and `riskLevel` as examples; none of them is required and none is
148
+ interpreted by this profile.
149
+
150
+ ### `environment` is not `application.environment`
151
+
152
+ `application` describes the system that **performed** the change, not the system that received it.
153
+ A delivery platform runs in one environment and deploys into many, so
154
+ `application.environment: production` and `metadata.deployment.environment: staging` on the same
155
+ event is correct and common — [valid/deploy-automated.json](../../examples/profiles/deployment-and-change-management/valid/deploy-automated.json)
156
+ is exactly that case. Conflating the two makes every environment-scoped review wrong, which is why
157
+ the profile requires the target environment separately rather than reusing a core field that means
158
+ something else.
159
+
160
+ The profile requires the field and says nothing about its values. It does not require `production`
161
+ to exist, or to be spelled that way.
162
+
163
+ ## Conditional-policy fields
164
+
165
+ Three paths turn rules on: two producer-set flags under the profile's own namespace, and the core
166
+ `event.outcome`. Each condition has the same shape, which is the only conditional mechanism v0.1
167
+ offers: one path compared for equality against one scalar.
168
+
169
+ | Flag | When it equals | The profile then requires |
170
+ | --------------------------------------- | -------------- | ----------------------------------------- |
171
+ | `/metadata/deployment/approvalRequired` | `true` | `/approval/status` on the executing event |
172
+ | `/metadata/deployment/emergency` | `true` | `/reason` |
173
+ | `/event/outcome` | `failure` | `/metadata/deployment/resultingState` |
174
+ | `/event/outcome` | `partial` | `/metadata/deployment/resultingState` |
175
+
176
+ `approvalRequired` is itself required by `DEPLOY-CORE-002`. A conditional rule contributes nothing
177
+ when its path is absent, so a flag nobody had to record would make the rule optional in practice:
178
+ silence would read as "no approval was required" when it may mean "nobody recorded whether one was".
179
+ `emergency` is deliberately **not** required — an ordinary change is not obliged to declare that it
180
+ is ordinary — which does mean a producer can avoid `DEPLOY-EMERGENCY-001` by omitting the flag. That
181
+ is a limitation the rule language cannot close, and it is recorded below rather than papered over.
182
+
183
+ ## Approval model
184
+
185
+ **The profile does not decide which deployments need a human.** Continuous delivery deploys to
186
+ production many times a day with no per-change approval; a regulated release process approves every
187
+ one; an outage is repaired by whoever is on call and approved afterwards. All three are legitimate,
188
+ and a profile that mandated approval for every production deployment would describe one of them and
189
+ be switched off by everyone else. The producer declares the policy in `approvalRequired`, and
190
+ `DEPLOY-APPROVAL-002` enforces the consequence.
191
+
192
+ **Approval is required of the executing event, not only of the approving one.** The question a review
193
+ asks is not whether an approval exists somewhere but whether the change that actually reached the
194
+ environment was covered by one.
195
+
196
+ **The status is required; `approved` is not.** A change executed while its approval was still pending
197
+ is a control bypass, and
198
+ [configuration-and-change.md §4](../../semantic-conventions/configuration-and-change.md) requires
199
+ producers to record that honestly rather than suppress it. A rule that accepted only `approved` would
200
+ make the single most important event in the trail unrepresentable, and would be met by dropping the
201
+ flag rather than by improving the process.
202
+ [valid/rollback-emergency.json](../../examples/profiles/deployment-and-change-management/valid/rollback-emergency.json)
203
+ is that event: a successful production rollback with `approval.status: pending`, recorded as it
204
+ happened.
205
+
206
+ **`not-required` is an answer.** Where policy required no approval, `approval.status: not-required`
207
+ records that a decision was reached rather than skipped, and satisfies the recommendation in
208
+ `DEPLOY-REVERT-001`.
209
+
210
+ ## Automation and the human behind it
211
+
212
+ Automation performs most material change, so the `actor` on a deployment event is usually a service
213
+ and the person who decided the change should happen is not in the event at all. The core model can
214
+ express the link in two ways, and this profile picks one:
215
+
216
+ - The service that carried the change out is the `actor`.
217
+ - The principal it acted for is the `subject`.
218
+ - `delegation.type` says how authority moved between them; `on-behalf-of` is the usual answer.
219
+
220
+ Recording the author under `metadata` instead would work for one product and be unreadable across
221
+ two. `DEPLOY-AUTOMATION-001` is a **warning** rather than a requirement because unattended
222
+ automation — drift correction, a scheduled reconciliation, an autoscaler — genuinely has no human
223
+ author, and in that case the warning is the correct signal to a reviewer rather than a defect to fix.
224
+
225
+ ## Privacy considerations
226
+
227
+ - **A secret rotation records that the secret changed, never the secret.**
228
+ `change.changedFields: ["secret"]` is the whole disclosure, and it is why this profile requires
229
+ field **names** rather than before and after values.
230
+ - **`change.before` and `change.after` are never required.** They are useful for configuration and
231
+ dangerous for credentials, so the choice stays with the producer. Where a setting's value is itself
232
+ sensitive, record a hash or omit it; see
233
+ [change-model.md](../../specification/change-model.md) §5.
234
+ - **No source control identity is required.** Commit authors, branch names and repository URLs are
235
+ personal data or internal topology in many organizations. The profile asks for a `subject`
236
+ identifier, which can be opaque.
237
+ - **Nothing in this profile asks for a token, key, connection string or endpoint.** A pipeline
238
+ credential has no place in an audit event, and `auditmodel lint-privacy` runs against every fixture
239
+ here by test.
240
+
241
+ ## Known rule-language limitations
242
+
243
+ - **No disjunction.** `DEPLOY-FAILURE-002` is a duplicate of `DEPLOY-FAILURE-001` with a different
244
+ condition, because a rule may carry exactly one equality condition. Stating the requirement twice
245
+ is better than leaving the `partial` case ungoverned, but it is duplication and it should collapse
246
+ into one rule if the language ever gains an `in` operator.
247
+ - **A rule cannot require a flag it is conditioned on.** `emergency` is optional, so a producer that
248
+ never sets it never triggers `DEPLOY-EMERGENCY-001`. Requiring `emergency` on every governed event
249
+ was considered and rejected: it would force every routine deployment to declare `false`, and the
250
+ value would be copied from a template rather than derived.
251
+ - **No cross-field comparison.** The profile cannot check that `version` differs from
252
+ `previousVersion`, that `resultingState` is consistent with `outcome`, that a rollback's target
253
+ version was ever deployed, or that `approvedAt` precedes `time`.
254
+ - **No numeric ranges.** `requiredMetadata` checks JSON type only. A profile can require
255
+ `receivedApprovals` to be an integer; it cannot require it to be at least `requiredApprovals`.
256
+ - **No array-content predicates.** The profile cannot require that `relatedResources` contains a
257
+ service, or that `changedFields` excludes a particular name.
258
+ - **No vocabulary constraint on open fields.** `environment`, `version` and `resultingState` are
259
+ required to be present strings and nothing more, because deployment environments, versioning
260
+ schemes and what counts as partially applied are all organization decisions.
261
+
262
+ ## Cross-profile overlaps
263
+
264
+ - **`identity-and-access-management`.** `identity.credential.rotate` and
265
+ `configuration.secret.rotate` are different operations on different objects: a principal's
266
+ credential versus a system's configured secret. Neither profile governs the other's event, and the
267
+ metadata namespaces (`/metadata/credential/`, `/metadata/deployment/`) do not collide.
268
+ - **`document-management`.** No overlap. `configuration.retention.update` changes a retention
269
+ **rule**; `document.retention.*` changes a document's retention state.
270
+ - **`incident-management`.** A rollback performed during an incident is governed here, and the
271
+ incident it responded to is referenced through the core `change.incidentId` field rather than
272
+ through a metadata key this profile defines. Incident lifecycle events themselves are out of scope.
273
+ - **`message-broker-management`.** A broker topic or ACL change made through a delivery pipeline
274
+ produces **two events**, not one governed twice: a `broker.*` event governed by the broker profile
275
+ and a `deployment.*` or `change.*` event governed by this one. An event has exactly one
276
+ `event.name`, and neither profile selects the other's names, so no single event is ever checked
277
+ against both. `/request/correlationId` is what joins them.
278
+
279
+ ## Not required, and why
280
+
281
+ - **A change ticket on every change.** `change.ticketId` is recommended where an approval was
282
+ required and never required outright. Teams that deliver continuously have no ticket, and a rule
283
+ that demanded one would be satisfied with a generated placeholder.
284
+ - **Trace identifiers.** Distributed tracing may not exist in the system that performs a change, and
285
+ a deployment is frequently not a request at all. `request.correlationId` is recommended;
286
+ `traceId` and `spanId` are not mentioned.
287
+ - **`request.protocol`, `request.method`, `request.route`.** A change applied by a reconciler that
288
+ polls a repository has no request to describe.
289
+ - **A source revision.** The earlier placeholder proposed requiring `metadata.sourceRevision`. It was
290
+ dropped: not every change comes from source control, a revision identifier is meaningless without
291
+ the repository it belongs to, and naming the repository is exactly the kind of internal topology the
292
+ privacy guidance discourages. Producers that have one should record it under the namespace.
293
+ - **`application.component`.** What was deployed is a `resource`, and the systems it changed belong in
294
+ `relatedResources`, which `DEPLOY-RELEASE-001` recommends. Requiring a component name as well would
295
+ force a single-target deployment to duplicate its own resource.
296
+ - **An authorization decision on a deployment.** `DEPLOY-CONFIG-001` and `DEPLOY-REVERT-001` require
297
+ `/authorization`; deploy, promote and infrastructure apply do not. The asymmetry is intentional. A
298
+ configuration change and a revert are performed **against** the normal path, usually by a person
299
+ under time pressure, and the policy decision is the evidence that they were allowed to be. A
300
+ pipeline that delivers what was merged frequently has no policy engine in the path at all, and a
301
+ rule demanding a decision that was never made would be satisfied with a synthesised
302
+ `decision: allow` — which is worse evidence than an honest absence.
303
+ [valid/deploy-automated.json](../../examples/profiles/deployment-and-change-management/valid/deploy-automated.json)
304
+ is that case. Where a deployment does pass a gate, recording it is right; the profile simply does
305
+ not make silence a failure.
306
+ - **Approval on every production deployment.** See the approval model above. This is the decision the
307
+ profile exists to get right.
308
+
309
+ ## Fixture matrix
310
+
311
+ [examples/profiles/deployment-and-change-management/](../../examples/profiles/deployment-and-change-management/)
312
+ — twelve valid, fourteen invalid, three not-applicable.
313
+
314
+ | Rule | Valid fixture | Invalid fixture |
315
+ | ----------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------- |
316
+ | `DEPLOY-CORE-001` | all twelve | `deploy-missing-deployment-id.json`, `deploy-missing-environment.json` |
317
+ | `DEPLOY-CORE-002` | all twelve | `deploy-missing-approval-required.json` |
318
+ | `DEPLOY-CORE-003` | all twelve | none; warnings never fail |
319
+ | `DEPLOY-RELEASE-001` | `release-create.json` | `deploy-missing-version.json` |
320
+ | `DEPLOY-RELEASE-002` | `deploy-automated.json` | `deploy-missing-previous-version.json` |
321
+ | `DEPLOY-REVERT-001` | `release-cancel.json` | `cancel-missing-authorization.json`, `cancel-missing-reason.json` |
322
+ | `DEPLOY-CONFIG-001` | `configuration-update.json` | `configuration-update-missing-authorization.json`, `secret-rotate-missing-changed-fields.json` |
323
+ | `DEPLOY-APPROVAL-001` | `change-request-approve.json` | `change-request-approve-missing-approval.json` |
324
+ | `DEPLOY-APPROVAL-002` | `deploy-approved.json` | `deploy-approved-missing-approval.json` |
325
+ | `DEPLOY-EMERGENCY-001` | `secret-rotate-emergency.json` | `secret-rotate-missing-reason.json` |
326
+ | `DEPLOY-FAILURE-001` | `deploy-failed.json` | `deploy-failed-missing-resulting-state.json` |
327
+ | `DEPLOY-FAILURE-002` | `deploy-partial.json` | `deploy-partial-missing-resulting-state.json` |
328
+ | `DEPLOY-AUTOMATION-001` | `infrastructure-apply.json` | none; warnings never fail |
329
+
330
+ Every fixture — valid, invalid and not-applicable alike — is core-conforming and privacy-clean. The
331
+ ones under `invalid/` fail the profile, not the core schema, and each fails for exactly one reason.
332
+
333
+ ## Not-applicable rationale
334
+
335
+ `deployment.pipeline.poll`, `deployment.build.log-append` and `configuration.secret.access` are
336
+ perfectly good audit events that this profile deliberately does not govern. `check-profile` reports
337
+ each of them as not applicable with exit code 3, which is not conformance: it says the profile had
338
+ nothing to say.
339
+
340
+ They exist to hold the exclusions in place. If a future edit widened a selector to a bare
341
+ `deployment.` or `configuration.secret.` prefix, these fixtures would start being checked instead of
342
+ skipped and the test would fail — which is the point, because that edit would silently impose a
343
+ change identifier, a target environment and an approval-policy flag on every poll, every build log
344
+ line and every process start in a deployment.
345
+
346
+ ## Open questions
347
+
348
+ - Should `resultingState` have a recommended vocabulary? The fixtures use `unchanged`,
349
+ `partially-applied` and `rolled-back`. Three values are not evidence of a convention, and fixing one
350
+ too early would be worse than leaving the field open.
351
+ - Is `emergency` the right name for a flag that also covers a break-glass repair and an overridden
352
+ pipeline gate? They are the same fact from an audit point of view — the normal path was not
353
+ followed — but producers may not recognize their case in the word.
354
+ - Should a promotion between environments record the environment it came from as well as the one it
355
+ went to? `previousVersion` answers the version question but not the provenance question, and there
356
+ is no adoption evidence yet for what the field should be called.
357
+ - A change request and the change that executes it are two events with two resources. This profile
358
+ ties them through `/metadata/deployment/id`. Whether that is better than
359
+ `relatedResources` for the general case is unresolved.