@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,112 @@
1
+ # Identity and Access Management Profile
2
+
3
+ **Profile version: 0.1 · Core versions: 0.1 · Status: Experimental · Implemented**
4
+
5
+ Additional conformance requirements for identity and access management audit events: accounts, roles,
6
+ permissions, service accounts and credential rotation.
7
+
8
+ ```bash
9
+ auditmodel check-profile examples/profiles/identity-and-access-management/valid \
10
+ --profile identity-and-access-management
11
+ ```
12
+
13
+ The rules live in [profile.json](profile.json) and are enforced by the declarative engine described in
14
+ [profiles/README.md](../README.md). Nothing in this profile is implemented in code.
15
+
16
+ ## Why this profile first
17
+
18
+ Access changes are the operations every organization audits, and the fields involved are the same
19
+ everywhere: which role, which permission, at what scope, was it privileged, who approved it, was the
20
+ session multi-factor authenticated. Unlike document sharing or incident priority, none of that
21
+ depends on a product's data model or on an organization's scales. See
22
+ [ADR 0008](../../decisions/0008-declarative-profile-conformance.md) §9.
23
+
24
+ ## Rules
25
+
26
+ | Rule | Applies to | Requires |
27
+ | -------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | `IAM-CORE-001` | `identity.` prefix | `/authorization` |
29
+ | `IAM-CORE-002` | `identity.` prefix | _Recommends_ `/reason`, `/request/correlationId` |
30
+ | `IAM-ROLE-001` | `identity.role.assign`, `identity.role.revoke` | `/authorization`, `/reason`, `/metadata/role/id` string, `/metadata/role/privileged` boolean. _Recommends_ `/approval`, `/request/correlationId` |
31
+ | `IAM-ROLE-002` | the same, **when** `/metadata/role/privileged` is `true` | `/approval`, `/authentication`, and `/authentication/mfa` equal to `true` |
32
+ | `IAM-PERM-001` | `identity.permission.` prefix | `/authorization`, `/reason`, `/metadata/permission/id` string, `/metadata/permission/scope` string, `/metadata/permission/privileged` boolean. _Recommends_ `/approval`, `/request/correlationId` |
33
+ | `IAM-PERM-002` | the same, **when** `/metadata/permission/privileged` is `true` | `/approval`, `/authentication`, and `/authentication/mfa` equal to `true` |
34
+ | `IAM-USER-001` | `identity.user.create`, `.disable`, `.delete` | `/metadata/user/type` string |
35
+ | `IAM-USER-002` | `identity.user.disable`, `identity.user.delete` | `/reason`. _Recommends_ `/approval` |
36
+ | `IAM-SVC-001` | `identity.service-account.create`, `.disable` | `/metadata/serviceAccount/purpose` string, `/metadata/serviceAccount/ownerId` string |
37
+ | `IAM-SVC-002` | `identity.service-account.create` | _Recommends_ `/metadata/serviceAccount/expiresAt` |
38
+ | `IAM-CRED-001` | `identity.credential.rotate` | `/authorization`, `/reason`, `/metadata/credential/type` string. _Recommends_ `/request/correlationId` |
39
+
40
+ Recommendations produce warnings and never fail conformance.
41
+
42
+ ### Why the privileged flag is required rather than inferred
43
+
44
+ `role.privileged` and `permission.privileged` are required booleans because no external reader can
45
+ determine whether `role-4471` is privileged. Recording the answer at the moment of the change is the
46
+ only point at which it is known, and it is what makes `IAM-ROLE-002` and `IAM-PERM-002` able to demand
47
+ approval and multi-factor authentication exactly where they matter.
48
+
49
+ An event that omits the flag fails `IAM-ROLE-001` rather than silently escaping the conditional rule.
50
+ The conditional itself does not fire on a missing flag; the presence requirement is what closes that
51
+ gap.
52
+
53
+ ### Multi-factor authentication
54
+
55
+ `IAM-ROLE-002` and `IAM-PERM-002` require `/authentication/mfa` to **equal `true`**, not merely to be
56
+ present. `mfa: false` is a conforming core event and a profile violation: it records, accurately, that
57
+ a privileged access change was made from a single-factor session.
58
+
59
+ ## Normative requirements this profile cannot check
60
+
61
+ These are requirements of the profile. No tool verifies them, and that is stated rather than
62
+ approximated by a rule that would be wrong.
63
+
64
+ 1. **The primary resource MUST identify the target** — the principal, role, permission or service
65
+ account the operation acted upon. A checker can confirm `/resource/id` is present; it cannot
66
+ confirm it names the right thing. See [actor-model.md](../../specification/actor-model.md): the
67
+ target of an identity operation is a `resource`, and `subject` appears only when someone's
68
+ authority was borrowed.
69
+ 2. **The actor and the target MUST NOT be represented ambiguously.** An administrator disabling an
70
+ account is the `actor`; the account is the `resource`. Recording the account as both is
71
+ structurally valid and semantically wrong.
72
+ 3. **Organization and tenant context is not required globally.** Not every application is
73
+ multi-tenant, and the profile does not assume one.
74
+ 4. **Personally identifiable attributes are never required.** No rule requires a display name, an
75
+ email address or any other direct personal identifier. `/metadata/user/type` records the kind of
76
+ principal — employee, contractor, partner, test — which is what an access review needs, and needs
77
+ no personal data at all.
78
+
79
+ ## Credentials and secrets
80
+
81
+ **Service account secrets, keys and tokens MUST NOT be placed in an audit event.** Neither must the
82
+ old or new value of a rotated credential, private key material, or any token.
83
+
84
+ `identity.credential.rotate` records the _kind_ of credential in `/metadata/credential/type` and
85
+ nothing about its value. `IAM-SVC-001` requires a purpose and an owner, and requires no credential at
86
+ all.
87
+
88
+ This profile does **not** check for secrets. That is
89
+ [`auditmodel lint-privacy`](../../specification/privacy.md), which is a separate, complementary
90
+ command: a profile says which fields must be present, the linter says which values must not. Every
91
+ published fixture in this profile is required by test to pass both, and the credential and service
92
+ account fixtures are additionally checked field by field for secret-shaped members.
93
+
94
+ ## Fixtures
95
+
96
+ [examples/profiles/identity-and-access-management/](../../examples/profiles/identity-and-access-management/)
97
+ holds seven conforming events, seven that violate exactly one rule, and one event the profile does not
98
+ govern.
99
+
100
+ ## Compatibility
101
+
102
+ The profile version is independent of the core specification version. `coreVersions` declares which
103
+ core versions the profile applies to; an event declaring any other `specVersion` is **not applicable**
104
+ rather than in violation.
105
+
106
+ Adding a rule is a breaking change for producers, in the same sense as adding a required core field.
107
+
108
+ ## Not a compliance statement
109
+
110
+ Conformance to this profile means an event carries the fields this profile requires. It is not
111
+ compliance with any law, regulation, standard or contract, and MUST NOT be presented as evidence of
112
+ one.
@@ -0,0 +1,120 @@
1
+ {
2
+ "profileVersion": "0.1",
3
+ "name": "identity-and-access-management",
4
+ "version": "0.1",
5
+ "status": "experimental",
6
+ "coreVersions": ["0.1"],
7
+ "title": "Identity and Access Management Profile",
8
+ "description": "Additional conformance requirements for identity and access management audit events. Every requirement adds to the OpenAuditModel Core Specification; none relaxes it.",
9
+ "rules": [
10
+ {
11
+ "id": "IAM-CORE-001",
12
+ "description": "Every identity operation records the authorization decision that permitted it.",
13
+ "rationale": "An access change without a recorded decision cannot be reviewed: nothing distinguishes a change a policy allowed from one that bypassed policy entirely.",
14
+ "severity": "error",
15
+ "eventPrefixes": ["identity."],
16
+ "requiredPaths": ["/authorization"]
17
+ },
18
+ {
19
+ "id": "IAM-CORE-002",
20
+ "description": "Every identity operation records why it was performed and how it correlates with the request that caused it.",
21
+ "rationale": "Access changes are reviewed long after the fact, by someone who cannot ask the actor what they were doing.",
22
+ "severity": "error",
23
+ "eventPrefixes": ["identity."],
24
+ "recommendedPaths": ["/reason", "/request/correlationId"]
25
+ },
26
+ {
27
+ "id": "IAM-ROLE-001",
28
+ "description": "Role assignment and revocation identify the role and whether it is privileged, and record a justification.",
29
+ "rationale": "The privileged flag is what makes a later reviewer able to separate routine access changes from the ones that matter, without knowing the operator's role catalogue.",
30
+ "severity": "error",
31
+ "events": ["identity.role.assign", "identity.role.revoke"],
32
+ "requiredPaths": ["/authorization", "/reason"],
33
+ "requiredMetadata": [
34
+ { "path": "/role/id", "type": "string" },
35
+ { "path": "/role/privileged", "type": "boolean" }
36
+ ],
37
+ "recommendedPaths": ["/approval", "/request/correlationId"]
38
+ },
39
+ {
40
+ "id": "IAM-ROLE-002",
41
+ "description": "A privileged role change is approved and performed from a multi-factor authenticated session.",
42
+ "rationale": "Privileged access is the control that protects every other control; granting it on the strength of a single factor and one person's judgement is the failure this rule exists to make visible.",
43
+ "severity": "error",
44
+ "events": ["identity.role.assign", "identity.role.revoke"],
45
+ "when": { "path": "/metadata/role/privileged", "equals": true },
46
+ "requiredPaths": ["/approval", "/authentication"],
47
+ "requiredValues": [{ "path": "/authentication/mfa", "equals": true }]
48
+ },
49
+ {
50
+ "id": "IAM-PERM-001",
51
+ "description": "Permission grants and revocations identify the permission, its scope and whether it is privileged.",
52
+ "rationale": "A permission without a scope cannot be assessed: the same permission over one record and over an entire tenant are different operations.",
53
+ "severity": "error",
54
+ "eventPrefixes": ["identity.permission."],
55
+ "requiredPaths": ["/authorization", "/reason"],
56
+ "requiredMetadata": [
57
+ { "path": "/permission/id", "type": "string" },
58
+ { "path": "/permission/scope", "type": "string" },
59
+ { "path": "/permission/privileged", "type": "boolean" }
60
+ ],
61
+ "recommendedPaths": ["/approval", "/request/correlationId"]
62
+ },
63
+ {
64
+ "id": "IAM-PERM-002",
65
+ "description": "A privileged permission change is approved and performed from a multi-factor authenticated session.",
66
+ "rationale": "Direct permission grants bypass the role model, so they warrant the same scrutiny as a privileged role change.",
67
+ "severity": "error",
68
+ "eventPrefixes": ["identity.permission."],
69
+ "when": { "path": "/metadata/permission/privileged", "equals": true },
70
+ "requiredPaths": ["/approval", "/authentication"],
71
+ "requiredValues": [{ "path": "/authentication/mfa", "equals": true }]
72
+ },
73
+ {
74
+ "id": "IAM-USER-001",
75
+ "description": "User lifecycle events record the kind of principal the account represents.",
76
+ "rationale": "Distinguishing an employee account from a contractor, partner or test account is what makes an access review answerable. It requires no personal data.",
77
+ "severity": "error",
78
+ "events": ["identity.user.create", "identity.user.disable", "identity.user.delete"],
79
+ "requiredMetadata": [{ "path": "/user/type", "type": "string" }]
80
+ },
81
+ {
82
+ "id": "IAM-USER-002",
83
+ "description": "Disabling or deleting an account records why.",
84
+ "rationale": "Account removal is indistinguishable from account suppression without a stated reason, and the two have very different implications.",
85
+ "severity": "error",
86
+ "events": ["identity.user.disable", "identity.user.delete"],
87
+ "requiredPaths": ["/reason"],
88
+ "recommendedPaths": ["/approval"]
89
+ },
90
+ {
91
+ "id": "IAM-SVC-001",
92
+ "description": "Service account lifecycle events record the account's purpose and a human owner.",
93
+ "rationale": "Unowned service accounts outlive the systems that needed them. Recording an owner at creation is the only point at which the answer is known.",
94
+ "severity": "error",
95
+ "events": ["identity.service-account.create", "identity.service-account.disable"],
96
+ "requiredMetadata": [
97
+ { "path": "/serviceAccount/purpose", "type": "string" },
98
+ { "path": "/serviceAccount/ownerId", "type": "string" }
99
+ ]
100
+ },
101
+ {
102
+ "id": "IAM-SVC-002",
103
+ "description": "Service account creation records when the account is expected to expire.",
104
+ "rationale": "A recorded expiry turns an indefinite credential into a reviewable one. It is recommended rather than required because not every service account can be time-bound.",
105
+ "severity": "error",
106
+ "events": ["identity.service-account.create"],
107
+ "recommendedPaths": ["/metadata/serviceAccount/expiresAt"]
108
+ },
109
+ {
110
+ "id": "IAM-CRED-001",
111
+ "description": "Credential rotation records the authorization, the justification and the kind of credential rotated.",
112
+ "rationale": "The kind of credential determines the blast radius of the rotation. The credential itself must never appear in the event; see the profile documentation.",
113
+ "severity": "error",
114
+ "events": ["identity.credential.rotate"],
115
+ "requiredPaths": ["/authorization", "/reason"],
116
+ "requiredMetadata": [{ "path": "/credential/type", "type": "string" }],
117
+ "recommendedPaths": ["/request/correlationId"]
118
+ }
119
+ ]
120
+ }
@@ -0,0 +1,338 @@
1
+ # Incident Management Profile
2
+
3
+ **Status: Experimental. Implemented in v0.1, 15 rules — 12 enforceable, 3 advisory.**
4
+
5
+ Scope: applications that manage the lifecycle of incidents, problems and corrective actions —
6
+ operations tooling, IT service management, quality management, safety reporting, regulatory event
7
+ handling.
8
+
9
+ The profile is vendor-neutral. It describes the lifecycle any incident system implements, not the
10
+ data model of any product. It assumes no priority scale, no escalation matrix, no approval process
11
+ and no service commitment framework.
12
+
13
+ The enforceable rules are in [profile.json](profile.json).
14
+
15
+ ```bash
16
+ auditmodel check-profile examples/profiles/incident-management/valid --profile incident-management
17
+ ```
18
+
19
+ ## Purpose
20
+
21
+ An incident trail is not one event. Raising, triaging, escalating, resolving, analysing, remediating
22
+ and closing are separate operations performed hours or weeks apart, often by different applications
23
+ and different principals, and the questions asked afterwards are always about the sequence rather
24
+ than about any single step: who decided this was minor, when did we know, who was accountable, was
25
+ the fix ever verified, why was it closed, and why was it reopened.
26
+
27
+ The core model can carry all of that. This profile requires the parts without which the sequence
28
+ cannot be reconstructed at all.
29
+
30
+ ## Scope
31
+
32
+ ### Event families
33
+
34
+ | Family | Governed | Notes |
35
+ | --------------------------------------------- | -------- | ------------------------------------------------- |
36
+ | `incident.case.create` | yes | Raising a case |
37
+ | `incident.priority.change` | yes | Reassessment of priority |
38
+ | `incident.assignment.change` | yes | Change of accountable owner |
39
+ | `incident.major.declare` | yes | Escalation to major |
40
+ | `incident.case.resolve` | yes | Service restored |
41
+ | `incident.case.close`, `incident.case.cancel` | yes | Terminal transitions |
42
+ | `incident.case.reopen` | yes | A new lifecycle transition, governed on its own |
43
+ | `incident.rca.*` | yes | Root cause analysis, including approval |
44
+ | `incident.sla.breach` | yes | A missed commitment; excluded from `INC-CORE-001` |
45
+ | `problem.case.create`, `problem.case.close` | yes | Problem management, where it is a separate record |
46
+ | `corrective-action.*` | yes | Opening, verifying and closing an action |
47
+ | `monitoring.alert.*` | **no** | Alert observations |
48
+ | `incident.note.*`, `incident.timeline.*` | **no** | Working notes and timeline chatter |
49
+ | `incident.case.view` and other reads | **no** | Reading a case record |
50
+
51
+ The names come from
52
+ [workflow-and-approval.md](../../semantic-conventions/workflow-and-approval.md), which already
53
+ defines the `incident.*` case lifecycle. Two families are added because that document stops at the
54
+ incident: `problem.case.*` for organizations that keep a problem record separate from the incidents
55
+ that revealed it, and `corrective-action.*` for the remediation that outlives both. Both follow the
56
+ core naming rules; `corrective-action.open` uses the two-segment form permitted where a resource
57
+ segment would be artificial, exactly as `authentication.login` does.
58
+
59
+ ### Explicit exclusions
60
+
61
+ **Alert and monitoring observations.** A monitoring system emits alerts continuously, the
62
+ overwhelming majority of which clear on their own and never become a managed case. Requiring an
63
+ authorization decision, a lifecycle state and a priority on each of them would put the profile's
64
+ heaviest requirements on the highest-volume event in the estate, and the requirement would be
65
+ switched off rather than met. An alert that _does_ become a case is recorded by the
66
+ `incident.case.create` that follows it.
67
+
68
+ **Working notes and timeline entries.** During a major incident a case accumulates hundreds of notes,
69
+ status pings and chat-bridge entries. They are worth auditing and they are not lifecycle transitions.
70
+
71
+ **Reads.** Opening a case record in a console is a data-access event, covered by
72
+ [data-access.md](../../semantic-conventions/data-access.md).
73
+
74
+ The exclusion is structural, not a matter of discipline. **No selector in this profile uses a bare
75
+ `incident.`, `incident.case.`, `problem.` or `monitoring.` prefix.** The case lifecycle is selected by
76
+ exact event name precisely so that a future `incident.case.view` or `incident.case.subscribe` cannot
77
+ be swept in by a prefix nobody re-read. Only `incident.rca.` and `corrective-action.` are prefixes,
78
+ and both name low-volume families whose every member should be governed. Tests assert all of this,
79
+ because widening a selector later would silently impose the profile's requirements on every alert in
80
+ a production estate.
81
+
82
+ Excluded does not mean unaudited. Every one of those events is still a conforming OpenAuditModel
83
+ event.
84
+
85
+ ## Rules
86
+
87
+ | Rule | Applies to | Requires |
88
+ | ------------------ | ------------------------------------------------------ | ---------------------------------------------------------- |
89
+ | `INC-CORE-001` | every governed event except `incident.sla.breach` | `/authorization`, `/metadata/incident/status` |
90
+ | `INC-CORE-002` | every governed event | _recommends_ `/request/correlationId`, `/relatedResources` |
91
+ | `INC-CREATE-001` | `incident.case.create`, `problem.case.create` | _recommends_ `/reason`, detection time, impact, urgency |
92
+ | `INC-STATE-001` | every state transition except reopen | `/change` |
93
+ | `INC-STATE-002` | reprioritisation, escalation, closure, cancellation | `/reason` |
94
+ | `INC-PRIORITY-001` | creation, reprioritisation, escalation, breach | `/metadata/incident/priority` |
95
+ | `INC-ASSIGN-001` | `incident.assignment.change`, `corrective-action.open` | `/metadata/incident/assigneeId`; recommends `/reason` |
96
+ | `INC-RESOLVE-001` | `incident.case.resolve` | `/metadata/incident/resolutionType` |
97
+ | `INC-CLOSE-001` | closure or cancellation **declared to need approval** | `/approval/status` |
98
+ | `INC-REOPEN-001` | `incident.case.reopen` | `/change`, `/reason`; recommends `/evidence` |
99
+ | `INC-RCA-001` | `incident.rca.*` | `/metadata/incident/rca/method` |
100
+ | `INC-RCA-002` | `incident.rca.approve` | `/approval/status` |
101
+ | `INC-CAPA-001` | `corrective-action.verify` | `/metadata/incident/correctiveAction/verificationMethod` |
102
+ | `INC-SLA-001` | `incident.sla.breach` | `/metadata/incident/sla/target` |
103
+ | `INC-EVIDENCE-001` | `incident.rca.*`, `corrective-action.*` | _recommends_ `/evidence` |
104
+
105
+ Each rule's full text and rationale is in [profile.json](profile.json).
106
+
107
+ ## Metadata namespace
108
+
109
+ Every metadata requirement lives under **`/metadata/incident/`**, and a test asserts it. Nested,
110
+ domain-namespaced keys keep two profiles from assigning different meanings to the same key when one
111
+ event is governed by both: `status` on an incident, on a share and on a deployment are not the same
112
+ fact, and a root-level `/metadata/status` would make them look like one.
113
+
114
+ | Path | Type | Meaning |
115
+ | -------------------------------------------------------- | ------- | -------------------------------------------------- |
116
+ | `/metadata/incident/status` | string | Lifecycle state the record was left in |
117
+ | `/metadata/incident/priority` | string | Priority in force |
118
+ | `/metadata/incident/impact`, `/urgency` | string | Inputs a priority is usually derived from |
119
+ | `/metadata/incident/detectedAt`, `/resolvedAt` | string | Timeline points the record cannot recompute |
120
+ | `/metadata/incident/resolutionType` | string | Workaround, permanent fix, duplicate, no fault |
121
+ | `/metadata/incident/approvalRequired` | boolean | Producer's declaration that closure needs approval |
122
+ | `/metadata/incident/rca/method` | string | How the analysis was conducted |
123
+ | `/metadata/incident/correctiveAction/verificationMethod` | string | How the action was demonstrated to work |
124
+ | `/metadata/incident/correctiveAction/id`, `/verifiedAt` | string | Links and timing for the remediation |
125
+ | `/metadata/incident/sla/target`, `/breachedAt` | string | The commitment and when it was missed |
126
+
127
+ The **vocabularies of these fields are deliberately open.** Priority, impact, urgency, resolution
128
+ type and verification method mean different things in a service desk, a manufacturing quality system
129
+ and a safety board. The profile requires the field to be recorded and does not tell an organization
130
+ what to put in it; a profile that closed these vocabularies would describe one product rather than a
131
+ domain.
132
+
133
+ `/metadata/incident/status` is required even though a transition also appears in `/change`. They are
134
+ different facts recorded for different readers. `/change` shows the movement; `status` names the
135
+ resulting state in a single typed field that the profile can check and a consumer can index, which
136
+ matters because the rule language cannot look inside `/change/after` and would otherwise have no way
137
+ to tell that a case was left closed rather than left cancelled.
138
+
139
+ `/metadata/incident/status` is also **not** `event.severity`. `event.severity` grades the audit
140
+ significance of the record; the incident's own priority is business data and lives in
141
+ `/metadata/incident/priority`. The profile keeps a single business scale — priority — rather than
142
+ requiring both a priority and a severity, because organizations that use two scales already derive
143
+ one from the other, and an audit trail that carried both would invite them to disagree.
144
+
145
+ ## Conditional-policy fields
146
+
147
+ The profile has exactly one conditional rule, and it fires on one producer-set flag.
148
+
149
+ ```json
150
+ { "when": { "path": "/metadata/incident/approvalRequired", "equals": true } }
151
+ ```
152
+
153
+ Whether closing a case needs a second pair of eyes is an organizational policy, not something this
154
+ specification can decide. A service desk closing thousands of routine tickets a week and a safety
155
+ board closing a reportable event are both conforming. The producer declares the obligation; the
156
+ profile enforces the consequence. This is the same shape as the IAM profile's `role.privileged` flag
157
+ and the document profile's `share.recipientType`, and it is the entire conditional mechanism v0.1
158
+ offers: one path, compared for equality, against one scalar.
159
+
160
+ When the flag is absent the condition does not hold and the rule contributes nothing. Recording
161
+ `approvalRequired: false` explicitly is better practice than omitting it, because it distinguishes
162
+ "approval was not required" from "nobody said".
163
+
164
+ ## Approval model
165
+
166
+ **Approval is never required universally.** Only two rules mention `/approval`, and a test asserts
167
+ that no other rule can quietly acquire one:
168
+
169
+ - `INC-RCA-002` requires it on `incident.rca.approve`, an event that _is_ an approval. Requiring the
170
+ approval state there is not process imposition; it is a requirement that the event describe itself.
171
+ - `INC-CLOSE-001` requires it on a closure **only** where the producer declared it necessary.
172
+
173
+ Both require the approval **status** to be present, not to be favourable. An event whose approval is
174
+ still pending or was rejected and whose outcome is `success` describes a control bypass, and
175
+ [workflow-and-approval.md](../../semantic-conventions/workflow-and-approval.md) §3 says producers
176
+ MUST record that accurately. A rule demanding `status: "approved"` would make the honest record
177
+ non-conforming and quietly reward rewriting it.
178
+
179
+ Approvers and approval timestamps are recommended, never required: an automated approval gate has no
180
+ human approver to name.
181
+
182
+ ### Reopening
183
+
184
+ A reopen is treated as **a new auditable lifecycle transition and nothing more**. `INC-REOPEN-001`
185
+ requires the transition and the reason, and recommends supporting evidence.
186
+
187
+ The profile does **not** assert that reopening invalidates the earlier closure or the approval
188
+ attached to it. That closure remains a true historical record of what was decided, by whom, on the
189
+ evidence available at the time; a trail that retroactively voided it would destroy the very thing a
190
+ reviewer needs in order to ask why the original decision looked right. Whether a fresh approval is
191
+ owed before the case can be closed again is an organizational process, and the profile expresses it
192
+ the only honest way available: through `approvalRequired` on the next closure. A test asserts that no
193
+ rule requires an approval on a reopen.
194
+
195
+ Reusing the **same `request.correlationId`** across the original handling and the reopen is what keeps
196
+ the record readable as one story rather than two unrelated cases. It is recommended by
197
+ `INC-CORE-002` rather than required, because a producer may have no correlation facility at all.
198
+
199
+ ## Privacy considerations
200
+
201
+ Incident records are among the most personal-data-dense artifacts an organization keeps. A case can
202
+ describe a customer complaint, a safety event involving a named individual, an HR investigation or a
203
+ security breach with victims, and the free-text fields are where that leaks.
204
+
205
+ This profile requires **no free text anywhere**. Every requirement is a token, a boolean, a timestamp
206
+ or a structural object. Specifically:
207
+
208
+ - Requirements ask for `reason` as an object, whose `code` is a token. `reason.text` is scanned by the
209
+ privacy linter and should stay a short, factual sentence.
210
+ - Nothing in the profile asks for a reporter's identity, a complainant, an affected customer or any
211
+ contact detail. Principals are referenced by opaque identifier through `actor` and `subject`.
212
+ - `/evidence` references material; it never embeds it. An analysis document stays in the record
213
+ system it belongs to, under that system's access control, and the audit event carries a pointer.
214
+ - `/metadata/incident/rca/*` must not become a place to paste an investigation narrative. The profile
215
+ requires the method, not the findings.
216
+
217
+ Every published fixture is required by test to pass `auditmodel lint-privacy` as well as
218
+ `auditmodel validate`. Nothing here weakens [specification/privacy.md](../../specification/privacy.md);
219
+ a profile-conforming event can still carry a secret, and the linter is what looks for one.
220
+
221
+ ## Known rule-language limitations
222
+
223
+ The v0.1 rule language checks presence, JSON type and strict scalar equality against one condition.
224
+ These consequences are real, and the profile does not paper over them:
225
+
226
+ 1. **Array contents cannot be inspected.** The profile cannot require that `/evidence` contains an
227
+ entry of type `document`, or that `/relatedResources` includes the incident an analysis belongs to.
228
+ `INC-EVIDENCE-001` therefore _recommends_ `/evidence` rather than requiring it and pretending to
229
+ verify it. The placeholder that preceded this profile proposed "an `evidence` entry of type
230
+ `document` REQUIRED on `incident.rca.*`"; that requirement cannot be expressed and is not claimed.
231
+ 2. **Numeric ranges cannot be checked.** `INC-SLA-001` requires the commitment to be _named_. It
232
+ cannot assert that a breach duration exceeded a threshold, that `requiredApprovals` was at least
233
+ two, or that a corrective action was verified before its due date.
234
+ 3. **Fields cannot be compared to each other.** The profile cannot require that the approver differs
235
+ from the actor, that `resolvedAt` is later than `detectedAt`, or that `status` agrees with
236
+ `/change/after`. Separation of duties is recorded as facts and evaluated elsewhere;
237
+ OpenAuditModel is not a policy engine.
238
+ 4. **There is no disjunction.** "Priority or severity" cannot be expressed, which is one reason the
239
+ profile settles on a single required scale.
240
+ 5. **One condition per rule.** "Approval required _and_ the incident is major" needs two rules or one
241
+ producer-set flag. The profile uses the flag.
242
+ 6. **Cross-event invariants are out of reach.** Nothing can require that a closure be preceded by a
243
+ resolution, or that every major incident eventually produce an analysis. A rule sees one event.
244
+
245
+ Where a requirement could not be expressed honestly, it appears above as guidance rather than in
246
+ `profile.json` as a rule that does not do what its name suggests.
247
+
248
+ ## Cross-profile overlaps
249
+
250
+ | Neighbour | Overlap |
251
+ | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
252
+ | `workflow.approval.*` events | An approval decision is its own event; `/approval` on a governed event summarises the outcome. Both should be emitted. This profile requires the summary, never the decision events. |
253
+ | Deployment and change management | A corrective action is frequently delivered as a change. The link belongs in `change.ticketId` / `change.deploymentId` and in `/relatedResources`; this profile does not govern the deployment event and requires no field on it. |
254
+ | Identity and access management | A break-glass access grant during a major incident is an `identity.*` event governed by that profile. Correlating them is what `request.correlationId` is for. |
255
+ | Document management | An analysis document has its own lifecycle under `document.*`. This profile references it through `/evidence` and never governs it. |
256
+ | Message broker / data infrastructure | An incident about a broker names the broker in `/relatedResources`. Nothing here governs data-plane events. |
257
+
258
+ A single event governed by two profiles must satisfy both. Namespaced metadata is what makes that
259
+ possible without collision.
260
+
261
+ ## Fixture matrix
262
+
263
+ [examples/profiles/incident-management/](../../examples/profiles/incident-management/) — thirteen
264
+ valid, fourteen invalid, three not-applicable. Every fixture is core-conforming and privacy-clean;
265
+ every invalid fixture is core-**valid** and fails exactly one profile rule with exactly one error.
266
+ There is one invalid fixture per _requirement_, not per rule, so the two rules that require two
267
+ things each carry two fixtures; a test derives that obligation from `profile.json`.
268
+
269
+ | Rule | Valid fixture | Invalid fixture |
270
+ | ------------------ | ------------------------------------------------------- | -------------------------------------------------------------------------- |
271
+ | `INC-CORE-001` | all governed fixtures | `case-close-missing-authorization.json`, `case-create-missing-status.json` |
272
+ | `INC-CORE-002` | all governed fixtures | advisory, never fails |
273
+ | `INC-CREATE-001` | `case-create.json` | advisory, never fails |
274
+ | `INC-STATE-001` | `assignment-change.json` | `assignment-change-missing-change.json` |
275
+ | `INC-STATE-002` | `case-close.json` | `case-close-missing-reason.json` |
276
+ | `INC-PRIORITY-001` | `priority-change.json` | `priority-change-missing-priority.json` |
277
+ | `INC-ASSIGN-001` | `assignment-change.json`, `corrective-action-open.json` | `assignment-change-missing-assignee.json` |
278
+ | `INC-RESOLVE-001` | `case-resolve.json` | `case-resolve-missing-resolution-type.json` |
279
+ | `INC-CLOSE-001` | `case-close.json` | `case-close-missing-approval.json` |
280
+ | `INC-REOPEN-001` | `case-reopen.json` | `case-reopen-missing-change.json`, `case-reopen-missing-reason.json` |
281
+ | `INC-RCA-001` | `rca-create.json` | `rca-create-missing-method.json` |
282
+ | `INC-RCA-002` | `rca-approve.json` | `rca-approve-missing-approval.json` |
283
+ | `INC-CAPA-001` | `corrective-action-verify.json` | `corrective-action-verify-missing-method.json` |
284
+ | `INC-SLA-001` | `sla-breach.json` | `sla-breach-missing-target.json` |
285
+ | `INC-EVIDENCE-001` | `rca-create.json` | advisory, never fails |
286
+
287
+ `problem-case-close.json` and `major-declare.json` exercise the problem domain and the escalation
288
+ path; `problem-case-close.json` is also the fixture that proves the closure-approval condition stays
289
+ quiet when the producer declares approval was not required.
290
+
291
+ ## Not-applicable rationale
292
+
293
+ Three fixtures under `not-applicable/` prove the exclusions hold:
294
+
295
+ | Fixture | Event | Why it is ungoverned |
296
+ | ----------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------- |
297
+ | `monitoring-alert-raise.json` | `monitoring.alert.raise` | A threshold alert that cleared in 45 seconds and never became a managed case |
298
+ | `note-create.json` | `incident.note.create` | A working note on the incident timeline — high volume, not a lifecycle transition |
299
+ | `case-view.json` | `incident.case.view` | Reading a case record; a data-access event, and the reason the case lifecycle is selected by name |
300
+
301
+ `check-profile` reports each as not applicable with exit code 3. **Not applicable is not
302
+ conformance**: the tool says the profile is silent, never that the profile is satisfied.
303
+
304
+ These fixtures exist to hold the exclusions in place. If a future edit widened a selector to a bare
305
+ `incident.` prefix, they would start conforming instead of being skipped and the test would fail —
306
+ which is the point, because that edit would impose the profile's heaviest requirements on every alert
307
+ and every note in a production estate.
308
+
309
+ ## Not required, and why
310
+
311
+ - **Impact and urgency as requirements.** Recommended on creation only. Many organizations record a
312
+ priority directly, and demanding a derivation they do not perform produces invented values.
313
+ - **`relatedResources` as a requirement on declaration and closure.** The placeholder proposed it.
314
+ An incident about a process, a supplier or a person has no affected service to name, and the rule
315
+ language cannot check what an array contains anyway, so it is recommended by `INC-CORE-002`.
316
+ - **Trace identifiers.** `traceId` and `spanId` presume distributed tracing exists. Correlation is
317
+ recommended; tracing is never required.
318
+ - **`request.protocol`.** A case can be closed from a console, an API, an email gateway or a batch
319
+ reconciliation. The protocol is not the audit fact.
320
+ - **A closure approval for every case.** See the approval model above.
321
+ - **A defined priority or severity scale.** See the metadata namespace above.
322
+ - **An `incident.capa.*` family.** The placeholder proposed it. `corrective-action.*` says the same
323
+ thing without requiring the reader to know that `capa` is quality-management jargon.
324
+
325
+ ## Open questions
326
+
327
+ - Should a corrective action be a resource with its own lifecycle, as modelled here, or metadata on
328
+ the incident? Modelling it as a resource lets it outlive the incident, which is usually what
329
+ happens in practice, at the cost of more events. Adoption evidence is thin either way.
330
+ - Should `incident.sla.breach` be emitted by the incident system or derived by a consumer? The
331
+ profile governs it if it is emitted and says nothing if it is not. Emitting it makes the trail
332
+ self-contained; deriving it avoids duplicating a computation that changes when the commitment does.
333
+ - Is a single `status` field sufficient for organizations that track an incident state and a workflow
334
+ state separately? The profile currently assumes the producer picks the one a reviewer would ask
335
+ about.
336
+ - Would a producer-set `major` flag be a better conditional discriminator than `approvalRequired` for
337
+ the closure rule? It would tie the obligation to the incident's own severity rather than to a
338
+ policy declaration, but it would also let the profile guess at a process it cannot see.