@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.
- package/README.md +75 -9
- package/dist/conformance/src/cli.js +149 -10
- package/dist/conformance/src/cli.js.map +1 -1
- package/dist/conformance/src/integrity/canonicalize.d.ts +6 -2
- package/dist/conformance/src/integrity/canonicalize.js +7 -2
- package/dist/conformance/src/integrity/canonicalize.js.map +1 -1
- package/dist/conformance/src/integrity/verify-event.d.ts +4 -3
- package/dist/conformance/src/integrity/verify-event.js +25 -6
- package/dist/conformance/src/integrity/verify-event.js.map +1 -1
- package/dist/conformance/src/privacy/size-analysis.js +3 -1
- package/dist/conformance/src/privacy/size-analysis.js.map +1 -1
- package/dist/conformance/src/privacy/token-patterns.js +17 -2
- package/dist/conformance/src/privacy/token-patterns.js.map +1 -1
- package/dist/conformance/src/privacy/types.d.ts +19 -0
- package/dist/conformance/src/privacy/types.js +8 -0
- package/dist/conformance/src/privacy/types.js.map +1 -1
- package/dist/conformance/src/profiles/coverage.d.ts +62 -0
- package/dist/conformance/src/profiles/coverage.js +114 -0
- package/dist/conformance/src/profiles/coverage.js.map +1 -0
- package/package.json +15 -2
- package/profiles/README.md +42 -0
- package/profiles/api-and-integration-management/0.1/profile.json +258 -0
- package/profiles/backup-and-recovery/0.1/profile.json +178 -0
- package/profiles/customer-and-account-management/0.1/profile.json +237 -0
- package/profiles/deployment-and-change-management/0.1/profile.json +298 -0
- package/profiles/document-management/0.1/profile.json +170 -0
- package/profiles/financial-transaction-management/0.1/profile.json +247 -0
- package/profiles/identity-and-access-management/0.1/profile.json +120 -0
- package/profiles/incident-management/0.1/profile.json +256 -0
- package/profiles/incident-management/0.2/profile.json +287 -0
- package/profiles/incident-management/README.md +39 -21
- package/profiles/incident-management/profile.json +37 -6
- package/profiles/message-broker-management/0.1/profile.json +399 -0
- package/profiles/secrets-and-key-management/0.1/profile.json +219 -0
- package/semantic-conventions/README.md +15 -10
- package/semantic-conventions/backup-and-recovery.md +124 -0
- package/semantic-conventions/customer-and-account.md +133 -0
- package/semantic-conventions/financial-transactions.md +128 -0
- package/semantic-conventions/message-brokers.md +128 -0
- package/semantic-conventions/secrets-and-keys.md +132 -0
- 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
|
-
| [
|
|
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.
|