@openauditmodel/cli 0.2.1 → 0.4.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 (41) hide show
  1. package/README.md +75 -9
  2. package/dist/conformance/src/cli.js +149 -10
  3. package/dist/conformance/src/cli.js.map +1 -1
  4. package/dist/conformance/src/integrity/canonicalize.d.ts +6 -2
  5. package/dist/conformance/src/integrity/canonicalize.js +7 -2
  6. package/dist/conformance/src/integrity/canonicalize.js.map +1 -1
  7. package/dist/conformance/src/integrity/verify-event.d.ts +4 -3
  8. package/dist/conformance/src/integrity/verify-event.js +25 -6
  9. package/dist/conformance/src/integrity/verify-event.js.map +1 -1
  10. package/dist/conformance/src/privacy/size-analysis.js +3 -1
  11. package/dist/conformance/src/privacy/size-analysis.js.map +1 -1
  12. package/dist/conformance/src/privacy/token-patterns.js +17 -2
  13. package/dist/conformance/src/privacy/token-patterns.js.map +1 -1
  14. package/dist/conformance/src/privacy/types.d.ts +19 -0
  15. package/dist/conformance/src/privacy/types.js +8 -0
  16. package/dist/conformance/src/privacy/types.js.map +1 -1
  17. package/dist/conformance/src/profiles/coverage.d.ts +62 -0
  18. package/dist/conformance/src/profiles/coverage.js +114 -0
  19. package/dist/conformance/src/profiles/coverage.js.map +1 -0
  20. package/package.json +15 -2
  21. package/profiles/README.md +42 -0
  22. package/profiles/api-and-integration-management/0.1/profile.json +258 -0
  23. package/profiles/backup-and-recovery/0.1/profile.json +178 -0
  24. package/profiles/customer-and-account-management/0.1/profile.json +237 -0
  25. package/profiles/deployment-and-change-management/0.1/profile.json +298 -0
  26. package/profiles/document-management/0.1/profile.json +170 -0
  27. package/profiles/financial-transaction-management/0.1/profile.json +247 -0
  28. package/profiles/identity-and-access-management/0.1/profile.json +120 -0
  29. package/profiles/incident-management/0.1/profile.json +256 -0
  30. package/profiles/incident-management/0.2/profile.json +287 -0
  31. package/profiles/incident-management/README.md +39 -21
  32. package/profiles/incident-management/profile.json +37 -6
  33. package/profiles/message-broker-management/0.1/profile.json +399 -0
  34. package/profiles/secrets-and-key-management/0.1/profile.json +219 -0
  35. package/semantic-conventions/README.md +15 -10
  36. package/semantic-conventions/backup-and-recovery.md +124 -0
  37. package/semantic-conventions/customer-and-account.md +133 -0
  38. package/semantic-conventions/financial-transactions.md +128 -0
  39. package/semantic-conventions/message-brokers.md +128 -0
  40. package/semantic-conventions/secrets-and-keys.md +132 -0
  41. package/specification/integrity.md +8 -3
@@ -0,0 +1,219 @@
1
+ {
2
+ "profileVersion": "0.1",
3
+ "name": "secrets-and-key-management",
4
+ "version": "0.1",
5
+ "status": "experimental",
6
+ "coreVersions": ["0.1"],
7
+ "title": "Secrets and Key Management Profile",
8
+ "description": "Additional conformance requirements for the custody of secrets, cryptographic keys and certificates: creation, rotation, revocation, destruction, policy change, and the privileged reveal and export operations that turn protected material into copied material. Every requirement adds to the OpenAuditModel Core Specification; none relaxes it. Routine automated retrieval of a secret and ordinary cryptographic operations against a key are deliberately not governed. An audit event records that a secret operation occurred; it never records the secret.",
9
+ "rules": [
10
+ {
11
+ "id": "SECRET-CORE-001",
12
+ "description": "Every governed secret, key or certificate operation records the authorization decision that permitted it and the classification of the material it acted on.",
13
+ "rationale": "Custody operations are the ones an attacker performs after gaining a foothold and before doing anything visible, so an operation with no recorded decision cannot be separated from one that bypassed policy. Classification is what makes the record triageable: a key protecting settlement records and a key in a sandbox produce identical events otherwise, and a reviewer who cannot tell them apart must treat every rotation as an incident or none of them.",
14
+ "severity": "error",
15
+ "events": [
16
+ "secret.create",
17
+ "secret.update",
18
+ "secret.rotate",
19
+ "secret.revoke",
20
+ "secret.delete",
21
+ "secret.reveal",
22
+ "secret.export",
23
+ "key.generate",
24
+ "key.import",
25
+ "key.rotate",
26
+ "key.enable",
27
+ "key.disable",
28
+ "key.destroy",
29
+ "key.export",
30
+ "certificate.issue",
31
+ "certificate.renew",
32
+ "certificate.revoke",
33
+ "certificate.delete"
34
+ ],
35
+ "eventPrefixes": ["secret.policy.", "key.policy."],
36
+ "requiredPaths": ["/authorization", "/resource/classification"]
37
+ },
38
+ {
39
+ "id": "SECRET-CORE-002",
40
+ "description": "Every governed operation records the kind of protected material it acted on.",
41
+ "rationale": "The kind of material is the blast radius. Replacing a read-only reporting credential and replacing the key that signs every payment instruction are the same event shape and entirely different incidents, and no external reader can recover the difference from an opaque identifier once the material is gone. The type is recorded rather than inferred because only the custodian knows it, and it is known at exactly the moment the event is emitted.",
42
+ "severity": "error",
43
+ "events": [
44
+ "secret.create",
45
+ "secret.update",
46
+ "secret.rotate",
47
+ "secret.revoke",
48
+ "secret.delete",
49
+ "secret.reveal",
50
+ "secret.export",
51
+ "key.generate",
52
+ "key.import",
53
+ "key.rotate",
54
+ "key.enable",
55
+ "key.disable",
56
+ "key.destroy",
57
+ "key.export",
58
+ "certificate.issue",
59
+ "certificate.renew",
60
+ "certificate.revoke",
61
+ "certificate.delete"
62
+ ],
63
+ "eventPrefixes": ["secret.policy.", "key.policy."],
64
+ "requiredMetadata": [{ "path": "/secret/type", "type": "string" }]
65
+ },
66
+ {
67
+ "id": "SECRET-CORE-003",
68
+ "description": "A governed operation should record why it happened, which custody system holds the material, and how it correlates with the wider operation.",
69
+ "rationale": "These are the fields a reviewer reaches for first and a producer omits most often. They are recommended rather than required because a scheduled rotation has no justification beyond the policy that scheduled it, because a deployment may have exactly one custody system and nothing to distinguish, and because correlation identifiers presuppose a tracing or workflow discipline that a conforming producer may not have. A missing answer here should prompt a question, not fail a build.",
70
+ "severity": "warning",
71
+ "events": [
72
+ "secret.create",
73
+ "secret.update",
74
+ "secret.rotate",
75
+ "secret.revoke",
76
+ "secret.delete",
77
+ "secret.reveal",
78
+ "secret.export",
79
+ "key.generate",
80
+ "key.import",
81
+ "key.rotate",
82
+ "key.enable",
83
+ "key.disable",
84
+ "key.destroy",
85
+ "key.export",
86
+ "certificate.issue",
87
+ "certificate.renew",
88
+ "certificate.revoke",
89
+ "certificate.delete"
90
+ ],
91
+ "eventPrefixes": ["secret.policy.", "key.policy."],
92
+ "recommendedPaths": ["/reason", "/request/correlationId", "/metadata/secret/provider"]
93
+ },
94
+ {
95
+ "id": "SECRET-LIFECYCLE-001",
96
+ "description": "Material brought into custody records who owns it.",
97
+ "rationale": "An unowned secret cannot be rotated and cannot be retired, because nobody knows which systems break when it changes. Ownership is knowable at exactly one moment — when the material is created, generated, imported or issued — and every later attempt to reconstruct it is archaeology across deployment manifests. Recording an owning team or service is not personal data and does not require naming an individual.",
98
+ "severity": "error",
99
+ "events": ["secret.create", "key.generate", "key.import", "certificate.issue"],
100
+ "requiredPaths": ["/resource/ownerId"]
101
+ },
102
+ {
103
+ "id": "SECRET-ROTATE-001",
104
+ "description": "Rotation records the transition it performed, naming what changed rather than what the material now is.",
105
+ "rationale": "Rotation is the healthy operation in this domain and the one most often recorded as a bare fact. Without `/change` the trail says something was replaced and nothing about which version succeeded which, so a consumer failing on a stale value cannot be tied to the rotation that caused it and a rotation that silently did nothing is indistinguishable from one that worked. `/change` is the core structure designed to carry that transition without carrying a value: `changedFields` names members, and `before` and `after` carry version identifiers, never key or secret material.",
106
+ "severity": "error",
107
+ "events": ["secret.rotate", "key.rotate", "certificate.renew"],
108
+ "requiredPaths": ["/change"],
109
+ "recommendedPaths": ["/change/changedFields"]
110
+ },
111
+ {
112
+ "id": "SECRET-ACCESS-001",
113
+ "description": "Revealing a stored value or exporting material records how the principal authenticated and why the access was needed.",
114
+ "rationale": "Reveal and export are the two operations that convert protected material into copied material: after them the custodian is no longer the only holder and no later control can undo that. The authentication context is what distinguishes a reviewed operator session from a replayed one, and the justification is the only thing a reviewer has months later, when the actor has changed teams and the copy is still valid. Recording the fact of the access never requires recording any part of the value.",
115
+ "severity": "error",
116
+ "events": ["secret.reveal", "secret.export", "key.export"],
117
+ "requiredPaths": ["/authentication", "/reason"],
118
+ "recommendedPaths": ["/approval"]
119
+ },
120
+ {
121
+ "id": "SECRET-ACCESS-002",
122
+ "description": "Access the producer has declared to be emergency or break-glass access is approved and performed from a multi-factor authenticated session.",
123
+ "rationale": "Break-glass access exists precisely to bypass the controls that normally apply, so it is the one path where the audit record is the only remaining control. `/approval` is required as a present object rather than as an approved decision, because emergencies are frequently approved retrospectively and the model can say so — `status: pending` on a successful access is an accurate record of a control bypass and exactly what a post-incident review looks for. Suppressing it would be the failure this rule exists to prevent. The rule fires only on the producer's own declaration, so the profile never has to guess what counts as an emergency in a given deployment.",
124
+ "severity": "error",
125
+ "events": ["secret.reveal", "secret.export", "key.export"],
126
+ "when": { "path": "/metadata/secret/emergencyAccess", "equals": true },
127
+ "requiredPaths": ["/approval"],
128
+ "requiredValues": [{ "path": "/authentication/mfa", "equals": true }]
129
+ },
130
+ {
131
+ "id": "SECRET-EXPORT-001",
132
+ "description": "An export records the kind of destination the material was moved to.",
133
+ "rationale": "Where exported material went is the single fact that determines the risk of the operation, and it is the fact least likely to survive in anyone's memory. Material copied into a second hardware custodian, written to offline backup media, displayed on an operator console and handed to an external party are four different exposures that produce identical events without this field. The profile requires the kind of destination rather than an address, because an address is often itself sensitive and is never comparable across deployments.",
134
+ "severity": "error",
135
+ "events": ["secret.export", "key.export"],
136
+ "requiredMetadata": [{ "path": "/secret/destinationType", "type": "string" }]
137
+ },
138
+ {
139
+ "id": "SECRET-DESTROY-001",
140
+ "description": "Revoking, disabling, destroying or deleting protected material is justified.",
141
+ "rationale": "These are the operations an audit trail is least able to reconstruct afterwards, because the evidence is what was removed. Destroying an encryption key destroys the readability of everything it protected, and revoking a certificate can take a service offline as effectively as an outage — so the difference between a planned decommissioning, an incident response and a mistake has to be stated at the time, by the only party who knows it. Approval is recommended rather than required because many deployments legitimately let a team retire its own test material, and a rule that forbade that would be switched off rather than met.",
142
+ "severity": "error",
143
+ "events": [
144
+ "secret.revoke",
145
+ "secret.delete",
146
+ "key.disable",
147
+ "key.destroy",
148
+ "certificate.revoke",
149
+ "certificate.delete"
150
+ ],
151
+ "requiredPaths": ["/reason"],
152
+ "recommendedPaths": ["/approval"]
153
+ },
154
+ {
155
+ "id": "SECRET-APPROVAL-001",
156
+ "description": "An operation the producer has declared to require approval records the approval.",
157
+ "rationale": "Approval requirements in this domain are set locally: one organization requires two signatures to destroy a key and another rotates certificates unattended thousands of times a day, and a profile that imposed either model on the other would describe one deployment and be ignored by the rest. So the producer declares the requirement and the profile enforces the consequence — an operation that its own policy says needs approval, and that carries none, is the control gap this rule makes visible. The rule requires the approval object, not an approved status, so that a rejected or still-pending approval on a completed operation is recordable rather than unrepresentable.",
158
+ "severity": "error",
159
+ "events": [
160
+ "secret.create",
161
+ "secret.update",
162
+ "secret.rotate",
163
+ "secret.revoke",
164
+ "secret.delete",
165
+ "secret.reveal",
166
+ "secret.export",
167
+ "key.generate",
168
+ "key.import",
169
+ "key.rotate",
170
+ "key.enable",
171
+ "key.disable",
172
+ "key.destroy",
173
+ "key.export",
174
+ "certificate.issue",
175
+ "certificate.renew",
176
+ "certificate.revoke",
177
+ "certificate.delete"
178
+ ],
179
+ "eventPrefixes": ["secret.policy.", "key.policy."],
180
+ "when": { "path": "/metadata/secret/approvalRequired", "equals": true },
181
+ "requiredPaths": ["/approval"]
182
+ },
183
+ {
184
+ "id": "SECRET-POLICY-001",
185
+ "description": "A change to a secret or key policy records the transition and why it was made.",
186
+ "rationale": "A policy change is where the controls over every piece of material in scope are switched off at once, and it usually leaves no trace anywhere else: lengthening a rotation interval, removing an approval requirement or widening who may reveal a value weakens thousands of secrets in a single event that looks like routine configuration. Recording the before and after state is what turns that event into evidence, and recording the reason is what separates a deliberate policy decision from an accident nobody noticed for a year.",
187
+ "severity": "error",
188
+ "eventPrefixes": ["secret.policy.", "key.policy."],
189
+ "requiredPaths": ["/change", "/reason"],
190
+ "recommendedPaths": ["/approval"]
191
+ },
192
+ {
193
+ "id": "SECRET-CERT-001",
194
+ "description": "Issuing or renewing a certificate records when it stops being valid.",
195
+ "rationale": "A certificate always has an end of validity, it is fixed at the moment of issue, and it is the fact that causes the outage when nobody wrote it down. This profile recommends an expiry everywhere else and requires it here for that reason: expiry is optional for material that may legitimately be indefinite, and mandatory for material that cannot be. The rule constrains presence and JSON type only; the v0.1 rule language cannot compare an expiry against the event time or against a maximum lifetime.",
196
+ "severity": "error",
197
+ "events": ["certificate.issue", "certificate.renew"],
198
+ "requiredMetadata": [{ "path": "/secret/expiresAt", "type": "string" }],
199
+ "recommendedPaths": ["/metadata/secret/algorithm"]
200
+ },
201
+ {
202
+ "id": "SECRET-KEY-001",
203
+ "description": "Importing key material generated outside the custody system is justified.",
204
+ "rationale": "Imported material existed somewhere else before the import, under procedures the custodian did not run and cannot attest to, and it may still exist there afterwards. That is the one property of a key that a custody system cannot establish for itself, so the reason for accepting external material has to be recorded by the party that decided to accept it. Generation inside the custodian carries no such requirement, which is deliberate: the profile should make the safer path the cheaper one.",
205
+ "severity": "error",
206
+ "events": ["key.import"],
207
+ "requiredPaths": ["/reason"],
208
+ "recommendedPaths": ["/approval"]
209
+ },
210
+ {
211
+ "id": "SECRET-KEY-002",
212
+ "description": "Key lifecycle events should record the algorithm the key uses and when it stops being valid.",
213
+ "rationale": "Algorithm and expiry are what make a key inventory answerable — which keys use a deprecated algorithm, which expire this quarter — and neither can be reconstructed after the key is destroyed. They are recommended rather than required because a custodian may legitimately treat the algorithm as an internal detail of a managed key service it does not control, and because a key held for as long as the data it protects has no expiry to record.",
214
+ "severity": "warning",
215
+ "events": ["key.generate", "key.import", "key.rotate"],
216
+ "recommendedPaths": ["/metadata/secret/algorithm", "/metadata/secret/expiresAt"]
217
+ }
218
+ ]
219
+ }
@@ -21,16 +21,21 @@ The vocabularies here are **open**. The core schema constrains their form, not t
21
21
 
22
22
  ## Documents
23
23
 
24
- | Document | Covers |
25
- | ---------------------------------------------------------- | ------------------------------------------------- |
26
- | [event-naming.md](event-naming.md) | The naming rule, categories, activity types |
27
- | [authentication.md](authentication.md) | Sign-in, sign-out, sessions, credentials, factors |
28
- | [identity-and-access.md](identity-and-access.md) | Users, roles, permissions, service accounts |
29
- | [data-access.md](data-access.md) | Reading, exporting, sharing and modifying data |
30
- | [configuration-and-change.md](configuration-and-change.md) | Settings, secrets, deployments, releases |
31
- | [workflow-and-approval.md](workflow-and-approval.md) | Requests, approvals, workflow state, incidents |
32
- | [privileged-operations.md](privileged-operations.md) | Administrative and break-glass operations |
33
- | [correlation-and-tracing.md](correlation-and-tracing.md) | Request, trace, span and correlation identifiers |
24
+ | Document | Covers |
25
+ | ---------------------------------------------------------- | --------------------------------------------------- |
26
+ | [event-naming.md](event-naming.md) | The naming rule, categories, activity types |
27
+ | [authentication.md](authentication.md) | Sign-in, sign-out, sessions, credentials, factors |
28
+ | [identity-and-access.md](identity-and-access.md) | Users, roles, permissions, service accounts |
29
+ | [data-access.md](data-access.md) | Reading, exporting, sharing and modifying data |
30
+ | [configuration-and-change.md](configuration-and-change.md) | Settings, secrets, deployments, releases |
31
+ | [workflow-and-approval.md](workflow-and-approval.md) | Requests, approvals, workflow state, incidents |
32
+ | [privileged-operations.md](privileged-operations.md) | Administrative and break-glass operations |
33
+ | [message-brokers.md](message-brokers.md) | Broker control plane: destinations, access, offsets |
34
+ | [secrets-and-keys.md](secrets-and-keys.md) | Secret, key and certificate lifecycle and access |
35
+ | [financial-transactions.md](financial-transactions.md) | Movements of money, limits and reconciliation |
36
+ | [backup-and-recovery.md](backup-and-recovery.md) | Backup, restore, recovery and retention policy |
37
+ | [customer-and-account.md](customer-and-account.md) | Business parties and business accounts |
38
+ | [correlation-and-tracing.md](correlation-and-tracing.md) | Request, trace, span and correlation identifiers |
34
39
 
35
40
  ## Relationship to profiles
36
41
 
@@ -0,0 +1,124 @@
1
+ # Backup and Recovery Events
2
+
3
+ **Specification version: 0.1 · Status: Experimental**
4
+
5
+ ## 1. Recommended event names
6
+
7
+ Three categories, because taking a backup, deleting one and changing a retention policy are different
8
+ kinds of operation:
9
+
10
+ | Family | `event.category` |
11
+ | --------------------------------------- | ---------------------- |
12
+ | Backup, snapshot and restore operations | `data-infrastructure` |
13
+ | Deletion, expiry and failover | `privileged-operation` |
14
+ | Retention and schedule policy | `configuration` |
15
+
16
+ ### Backups and snapshots
17
+
18
+ | Name | Operation |
19
+ | ----------------- | -------------------------------------------------- |
20
+ | `backup.create` | A backup was started |
21
+ | `backup.complete` | A backup finished, successfully or otherwise |
22
+ | `backup.verify` | A backup was tested for restorability |
23
+ | `backup.expire` | A backup passed its retention date and was removed |
24
+ | `backup.delete` | A backup was deleted before its retention date |
25
+ | `snapshot.create` | A point-in-time snapshot was taken |
26
+ | `snapshot.delete` | A snapshot was removed |
27
+
28
+ ### Restore and recovery
29
+
30
+ | Name | Operation |
31
+ | ------------------- | --------------------------------------------- |
32
+ | `restore.start` | A restore was started |
33
+ | `restore.complete` | A restore finished, successfully or otherwise |
34
+ | `recovery.start` | A recovery procedure was started |
35
+ | `recovery.complete` | A recovery procedure finished |
36
+ | `recovery.failover` | Service was moved to a standby site or system |
37
+ | `recovery.failback` | Service was returned to the primary |
38
+
39
+ ### Policy
40
+
41
+ `backup.policy.update` records a change to a retention period, a schedule or a verification
42
+ requirement. It is the operation that decides whether the rest of this vocabulary will have anything
43
+ to describe.
44
+
45
+ ## 2. These operations come in pairs
46
+
47
+ `backup.create` and `backup.complete`, `restore.start` and `restore.complete`, `recovery.start` and
48
+ `recovery.complete` are two events about one operation, because the operation takes long enough that
49
+ its beginning and its end are separately interesting.
50
+
51
+ **The pair MUST share a `request.correlationId`.** A start with no completion is precisely what an
52
+ auditor is looking for — a backup that never finished, a restore abandoned halfway — and it is only
53
+ findable when the two events can be joined. See
54
+ [correlation-and-tracing.md](correlation-and-tracing.md).
55
+
56
+ The outcome belongs on the completion event, in `event.outcome`, not in the name:
57
+ `backup.complete` with `outcome: failure` rather than a `backup.failed` name. See
58
+ [event-naming.md](event-naming.md) §5.
59
+
60
+ ## 3. Progress events are deliberately ungoverned
61
+
62
+ A running backup emits progress continuously. `restore.progress` and `backup.chunk.write` appear in
63
+ the published fixtures as events the profile does **not** govern, and the reason is the one
64
+ [profiles/backup-and-recovery/README.md](../profiles/backup-and-recovery/README.md) gives: requiring
65
+ an authorization decision and a reason on every chunk written would put the heaviest requirements on
66
+ the highest-volume event, and the requirement would be switched off rather than met.
67
+
68
+ Producers MAY emit progress events. They are operational telemetry, and an audit trail that filled
69
+ with them would be reviewed by nobody.
70
+
71
+ ## 4. What must never be recorded
72
+
73
+ **The backed-up content.** A backup event names a backup; it does not sample it, summarise it or
74
+ quote a record from it. A backup exists precisely because it holds everything, and an audit trail is
75
+ not the place to leak a row of it.
76
+
77
+ **Nothing that identifies the data subjects inside a backup.** A backup of a customer database is
78
+ described by its scope, its size and its source system, never by whose records it contains. A restore
79
+ event is the same: it records that data was restored, from which backup, to where, and by whom.
80
+
81
+ Credentials for the backup target, storage account keys and connection strings are out of bounds for
82
+ the ordinary reason — see [privacy.md](../specification/privacy.md) §6.
83
+
84
+ ## 5. Which principal goes where
85
+
86
+ | Operation | `actor` | `resource` | `subject` |
87
+ | ------------------------------------------------- | ----------------------- | --------------- | --------- |
88
+ | A scheduled job takes a backup | the job's identity | the **backup** | absent |
89
+ | An operator deletes a backup before its retention | the operator | the **backup** | absent |
90
+ | An operator restores a database | the operator | the **restore** | absent |
91
+ | A platform fails service over to a standby | the platform's identity | the **service** | absent |
92
+
93
+ The system that was backed up is a **related resource**, not the primary one: what the event is about
94
+ is the backup or the restore, and that is what changed. `subject` does not appear in this domain — no
95
+ backup operation is performed on another principal's behalf in the sense
96
+ [actor-model.md](../specification/actor-model.md) §5 defines.
97
+
98
+ ## 6. Early deletion is the event that matters
99
+
100
+ `backup.expire` is routine: a retention period elapsed and the platform did what it was configured to
101
+ do. `backup.delete` is not: a principal removed a backup before its retention date, and the recovery
102
+ position of whatever it protected is now different.
103
+
104
+ Where the producer distinguishes the two, the distinction SHOULD be in the name rather than in a
105
+ field, which is why both names exist. An early deletion SHOULD carry its justification in `reason`,
106
+ and where the producer's policy requires a second principal to agree, in `approval`.
107
+
108
+ ## 7. Context to populate
109
+
110
+ | Field | Guidance |
111
+ | ----------------------- | -------------------------------------------------------------------- |
112
+ | `authorization` | The decision that permitted the operation |
113
+ | `request.correlationId` | Shared by the start and completion of one operation |
114
+ | `relatedResources` | The system, database or volume the backup protects |
115
+ | `reason` | Why an early deletion, a failover or an unscheduled restore was done |
116
+ | `approval` | Where deletion or failover requires a second principal's decision |
117
+ | `metadata.backup` | Scope, size, retention class, target and verification state |
118
+
119
+ ## 8. Example
120
+
121
+ See
122
+ [examples/profiles/backup-and-recovery/valid/restore-start.json](../examples/profiles/backup-and-recovery/valid/restore-start.json)
123
+ for the start of a restore with the authorization decision, the correlation identifier that joins it
124
+ to its completion, and the backup it restores from.
@@ -0,0 +1,133 @@
1
+ # Customer and Account Events
2
+
3
+ **Specification version: 0.1 · Status: Experimental**
4
+
5
+ ## 1. Recommended event names
6
+
7
+ Several categories, because opening an account, restricting one and reading a customer record are
8
+ different kinds of operation:
9
+
10
+ | Family | `event.category` |
11
+ | ---------------------------------------- | -------------------- |
12
+ | Customer and account record changes | `data-modification` |
13
+ | Opening, closing and reopening | `resource-lifecycle` |
14
+ | Restriction, freezing and status changes | `security` |
15
+ | Reading a customer or account record | `data-access` |
16
+ | Limits and thresholds | `configuration` |
17
+
18
+ ### Customers
19
+
20
+ | Name | Operation |
21
+ | ------------------- | -------------------------------------------------- |
22
+ | `customer.create` | A customer record was created |
23
+ | `customer.update` | Customer attributes changed |
24
+ | `customer.merge` | Two customer records were combined |
25
+ | `customer.restrict` | A restriction was placed on a customer |
26
+ | `customer.close` | A customer relationship was ended |
27
+ | `customer.delete` | A customer record was removed |
28
+ | `customer.restore` | A closed or deleted customer record was reinstated |
29
+
30
+ ### Accounts
31
+
32
+ | Name | Operation |
33
+ | ------------------ | -------------------------------------- |
34
+ | `account.open` | An account was opened |
35
+ | `account.update` | Account attributes changed |
36
+ | `account.freeze` | An account was frozen |
37
+ | `account.unfreeze` | A freeze was lifted |
38
+ | `account.restrict` | A restriction was placed on an account |
39
+ | `account.close` | An account was closed |
40
+ | `account.reopen` | A closed account was reopened |
41
+
42
+ ### Limits and status
43
+
44
+ Two families are selected by prefix, so any action under them is governed:
45
+
46
+ | Prefix | Covers |
47
+ | ----------------- | --------------------------------------------------- |
48
+ | `account.limit.` | Changes to a limit or threshold on an account |
49
+ | `account.status.` | Changes to an account's standing or lifecycle state |
50
+
51
+ ## 2. This is not the identity vocabulary
52
+
53
+ **A customer is not a user, and an account is not a login.** This document governs business parties
54
+ and business accounts. A user, a role, a permission, a credential or a session belongs to
55
+ [identity-and-access.md](identity-and-access.md), and the two vocabularies never select the same
56
+ event — [profiles/customer-and-account-management/README.md](../profiles/customer-and-account-management/README.md)
57
+ states the boundary and the profile enforces it: `identity.` is not a prefix here, and neither
58
+ `customer.` nor `account.` is a prefix there.
59
+
60
+ This is the most likely misuse of this vocabulary. A person who signs in is an identity; the same
61
+ person as a party the business holds a relationship with is a customer. One human, two objects, two
62
+ vocabularies, and events about them answer different questions.
63
+
64
+ Where one operation genuinely touches both — closing a customer relationship and disabling the login
65
+ that belonged to it — that is two events, one from each vocabulary, joined by
66
+ `request.correlationId`.
67
+
68
+ ## 3. What must never be recorded
69
+
70
+ **The customer record's contents.** Names, addresses, dates of birth, national identifiers, tax
71
+ numbers, contact details and anything else the record holds belong in the system of record. An audit
72
+ event says that a customer record changed, by whom, under what authorization, and **which fields
73
+ moved** — not what they moved to.
74
+
75
+ `change.changedFields` is the recommended shape for exactly this reason: it names the fields without
76
+ carrying the values. Where before and after values genuinely must be recorded — a status transition,
77
+ a limit amount — they SHOULD be recorded only for fields that are not personal data. See
78
+ [change-model.md](../specification/change-model.md) and
79
+ [privacy.md](../specification/privacy.md).
80
+
81
+ An audit trail is retained for years, is read by people with no business need for a customer's
82
+ address, and is frequently exported. It concentrates who-did-what-to-whom, which is what makes it
83
+ valuable and what makes it dangerous.
84
+
85
+ ## 4. Which principal goes where
86
+
87
+ | Operation | `actor` | `resource` | `subject` |
88
+ | -------------------------------------------------- | ------------ | ---------------- | ------------ |
89
+ | An agent creates a customer record | the agent | the **customer** | absent |
90
+ | A customer updates their own details | the customer | the **customer** | absent |
91
+ | An agent updates a customer's details for them | the agent | the **customer** | the customer |
92
+ | An operator freezes an account | the operator | the **account** | absent |
93
+ | An onboarding service opens an account from a form | the service | the **account** | absent |
94
+
95
+ The customer is the `resource` when the customer record is what changed, and the `subject` only when
96
+ the actor acted **on the customer's behalf** — the third row. A customer is not a `subject` merely
97
+ because the operation concerns them:
98
+ [actor-model.md](../specification/actor-model.md) §5.2 calls that the single most common modelling
99
+ mistake the section exists to prevent.
100
+
101
+ The account a customer owns is a **related resource** on a customer event, and the customer is a
102
+ related resource on an account event.
103
+
104
+ ## 5. Restriction and manual override
105
+
106
+ Freezing an account, restricting a customer and overriding a limit are the operations a reviewer
107
+ looks for, because each changes what someone may do without changing the record they would look at
108
+ first.
109
+
110
+ Where the producer distinguishes a manual override from a rule-driven one, the distinction SHOULD
111
+ appear in `metadata` and the justification in `reason`. A restriction with no recorded reason is a
112
+ finding. Where the producer's policy requires a second principal to agree — closing an account,
113
+ lifting a freeze — the decision belongs in `approval`, including when approval was evaluated and
114
+ found not to be required.
115
+
116
+ ## 6. Context to populate
117
+
118
+ | Field | Guidance |
119
+ | ----------------------- | ----------------------------------------------------------------------- |
120
+ | `authorization` | The decision that permitted the change |
121
+ | `change.changedFields` | Which fields moved, in preference to before and after values |
122
+ | `approval` | Where the operation requires a second principal, including not-required |
123
+ | `reason` | Why a restriction, freeze, override or early closure was applied |
124
+ | `relatedResources` | The accounts a customer holds, or the customer an account belongs to |
125
+ | `metadata.customer` | The producer's own identifiers, the record's status and its segment |
126
+ | `request.correlationId` | Shared with the identity events that accompany a closure or an opening |
127
+
128
+ ## 7. Example
129
+
130
+ See
131
+ [examples/profiles/customer-and-account-management/valid/customer-update.json](../examples/profiles/customer-and-account-management/valid/customer-update.json)
132
+ for a customer record change with the authorization decision, the fields that moved, and none of the
133
+ values that moved into them.
@@ -0,0 +1,128 @@
1
+ # Financial Transaction Events
2
+
3
+ **Specification version: 0.1 · Status: Experimental**
4
+
5
+ ## 1. Recommended event names
6
+
7
+ This domain spans several categories, because moving money, reading a statement and changing a limit
8
+ are different kinds of operation:
9
+
10
+ | Family | `event.category` |
11
+ | ------------------------------------ | ------------------- |
12
+ | Movements of money | `data-modification` |
13
+ | Reading a transaction or a statement | `data-access` |
14
+ | Limits and thresholds | `configuration` |
15
+ | An operation awaiting a decision | `workflow` |
16
+
17
+ ### Families
18
+
19
+ Most of this vocabulary is **open by family**. The profile selects by prefix, so any action under a
20
+ family name is governed, and the actions below are the ones the published fixtures carry rather than
21
+ a closed list.
22
+
23
+ | Prefix | Covers |
24
+ | --------------------------- | ------------------------------------------------------ |
25
+ | `financial.transfer.` | Movement between two accounts |
26
+ | `financial.payment.` | Payment to a payee |
27
+ | `financial.payout.` | Disbursement to a beneficiary |
28
+ | `financial.deposit.` | Money received into an account |
29
+ | `financial.withdrawal.` | Money taken out of an account |
30
+ | `financial.refund.` | Return of a previously captured amount |
31
+ | `financial.chargeback.` | A disputed payment reversed by the scheme |
32
+ | `financial.reversal.` | An operation undone by the producer |
33
+ | `financial.settlement.` | Net position settled between parties |
34
+ | `financial.reconciliation.` | Recorded position corrected against an external record |
35
+
36
+ ### Actions the fixtures carry
37
+
38
+ | Name | Operation |
39
+ | --------------------------------- | ----------------------------------------- |
40
+ | `financial.transfer.execute` | A transfer was carried out |
41
+ | `financial.transfer.cancel` | A transfer was cancelled before execution |
42
+ | `financial.payment.capture` | An authorized payment was captured |
43
+ | `financial.payment.reject` | A payment was refused |
44
+ | `financial.payment.cancel` | A payment was cancelled |
45
+ | `financial.payout.execute` | A payout was carried out |
46
+ | `financial.payout.cancel` | A payout was cancelled |
47
+ | `financial.withdrawal.execute` | A withdrawal was carried out |
48
+ | `financial.withdrawal.reject` | A withdrawal was refused |
49
+ | `financial.refund.create` | A refund was raised |
50
+ | `financial.chargeback.open` | A chargeback was opened |
51
+ | `financial.reversal.execute` | An operation was reversed |
52
+ | `financial.settlement.execute` | A settlement run completed |
53
+ | `financial.settlement.cancel` | A settlement run was cancelled |
54
+ | `financial.reconciliation.adjust` | A recorded position was corrected |
55
+
56
+ ### Limits
57
+
58
+ | Name | Operation |
59
+ | ------------------------ | ------------------- |
60
+ | `financial.limit.create` | A limit was defined |
61
+ | `financial.limit.update` | A limit was changed |
62
+ | `financial.limit.delete` | A limit was removed |
63
+
64
+ ## 2. The audit event is not the payment record
65
+
66
+ This is the distinction the domain most often loses. An audit event records that an operation was
67
+ performed and that it was permitted. The payment itself — the instruction, the parties, the
68
+ reference data — lives in the system that made it.
69
+
70
+ [README.md](../README.md) makes the point with the profile's own requirements: the financial profile
71
+ asks for an amount, a currency, a direction and a status, and asks for **no account number, no
72
+ counterparty name and no payment instruction**. That is not an oversight. A profile requires the
73
+ fields that make an operation reviewable, not the business record itself.
74
+
75
+ A reviewer needs to answer: who moved money, how much, in which direction, under what authorization,
76
+ and did anyone approve it. None of those questions needs the payee's address.
77
+
78
+ ## 3. What must never be recorded
79
+
80
+ - **Primary account numbers**, in full or truncated to a length that narrows the search.
81
+ - **IBANs, sort codes and account numbers** wherever an internal identifier would serve. Where a
82
+ counterparty account genuinely must be identifiable, a producer SHOULD record a reference the
83
+ system of record can resolve, not the number itself.
84
+ - **Counterparty personal data** — names, addresses, dates of birth.
85
+ - **Anything that would let a reader reconstruct the payment instruction**, including a free-text
86
+ remittance field copied verbatim.
87
+ - **Authentication and authorization material**: card verification values, one-time codes, tokens.
88
+
89
+ An audit trail is retained for years and read by people who were not party to the transaction. It is
90
+ the wrong place for the data a payment scheme already protects.
91
+
92
+ ## 4. Which principal goes where
93
+
94
+ | Operation | `actor` | `resource` | `subject` |
95
+ | ------------------------------------------------ | ------------ | ---------------- | ------------ |
96
+ | A customer initiates a transfer | the customer | the **transfer** | absent |
97
+ | A payments service executes a scheduled transfer | the service | the **transfer** | absent |
98
+ | An operator reverses a payment | the operator | the **reversal** | absent |
99
+ | An agent acts on a customer's instruction | the agent | the **payment** | the customer |
100
+
101
+ The account is a **related resource**, not the primary one: what changed is the movement of money,
102
+ and the movement is what the event is about. The last row is the only place `subject` belongs —
103
+ someone's authority was borrowed. See [actor-model.md](../specification/actor-model.md) §5.
104
+
105
+ ## 5. Manual intervention
106
+
107
+ An operation performed by hand rather than by the normal path is what an auditor looks for first: a
108
+ manual correction, an override of a limit, a reconciliation adjustment. Where the producer
109
+ distinguishes such operations, the distinction SHOULD appear in `metadata` and the justification in
110
+ `reason`. Where a second principal approved it, the decision belongs in `approval`.
111
+
112
+ ## 6. Context to populate
113
+
114
+ | Field | Guidance |
115
+ | ----------------------- | -------------------------------------------------------------------- |
116
+ | `authorization` | The decision that permitted the movement |
117
+ | `approval` | Where a second principal decided, including when not required |
118
+ | `request.correlationId` | The transaction reference, shared by every event about one movement |
119
+ | `relatedResources` | The accounts involved, by internal identifier |
120
+ | `reason` | Why a manual correction, reversal or adjustment was made |
121
+ | `metadata.financial` | Amount, currency, direction, status and the producer's own reference |
122
+
123
+ ## 7. Example
124
+
125
+ See
126
+ [examples/profiles/financial-transaction-management/valid/transfer-execute.json](../examples/profiles/financial-transaction-management/valid/transfer-execute.json)
127
+ for a completed transfer with the authorization decision, the correlation reference, the amount and
128
+ currency, and no account or counterparty detail.