@openauditmodel/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +215 -0
- package/README.md +647 -0
- package/dist/conformance/src/cli.d.ts +20 -0
- package/dist/conformance/src/cli.js +560 -0
- package/dist/conformance/src/cli.js.map +1 -0
- package/dist/conformance/src/format-errors.d.ts +20 -0
- package/dist/conformance/src/format-errors.js +116 -0
- package/dist/conformance/src/format-errors.js.map +1 -0
- package/dist/conformance/src/integrity/canonicalize.d.ts +25 -0
- package/dist/conformance/src/integrity/canonicalize.js +100 -0
- package/dist/conformance/src/integrity/canonicalize.js.map +1 -0
- package/dist/conformance/src/integrity/digest.d.ts +54 -0
- package/dist/conformance/src/integrity/digest.js +126 -0
- package/dist/conformance/src/integrity/digest.js.map +1 -0
- package/dist/conformance/src/integrity/types.d.ts +86 -0
- package/dist/conformance/src/integrity/types.js +36 -0
- package/dist/conformance/src/integrity/types.js.map +1 -0
- package/dist/conformance/src/integrity/verify-chain.d.ts +23 -0
- package/dist/conformance/src/integrity/verify-chain.js +235 -0
- package/dist/conformance/src/integrity/verify-chain.js.map +1 -0
- package/dist/conformance/src/integrity/verify-event.d.ts +29 -0
- package/dist/conformance/src/integrity/verify-event.js +140 -0
- package/dist/conformance/src/integrity/verify-event.js.map +1 -0
- package/dist/conformance/src/privacy/entropy.d.ts +14 -0
- package/dist/conformance/src/privacy/entropy.js +86 -0
- package/dist/conformance/src/privacy/entropy.js.map +1 -0
- package/dist/conformance/src/privacy/field-names.d.ts +36 -0
- package/dist/conformance/src/privacy/field-names.js +121 -0
- package/dist/conformance/src/privacy/field-names.js.map +1 -0
- package/dist/conformance/src/privacy/lint-event.d.ts +18 -0
- package/dist/conformance/src/privacy/lint-event.js +272 -0
- package/dist/conformance/src/privacy/lint-event.js.map +1 -0
- package/dist/conformance/src/privacy/rules.d.ts +44 -0
- package/dist/conformance/src/privacy/rules.js +161 -0
- package/dist/conformance/src/privacy/rules.js.map +1 -0
- package/dist/conformance/src/privacy/safe-formats.d.ts +32 -0
- package/dist/conformance/src/privacy/safe-formats.js +118 -0
- package/dist/conformance/src/privacy/safe-formats.js.map +1 -0
- package/dist/conformance/src/privacy/size-analysis.d.ts +36 -0
- package/dist/conformance/src/privacy/size-analysis.js +87 -0
- package/dist/conformance/src/privacy/size-analysis.js.map +1 -0
- package/dist/conformance/src/privacy/token-patterns.d.ts +24 -0
- package/dist/conformance/src/privacy/token-patterns.js +126 -0
- package/dist/conformance/src/privacy/token-patterns.js.map +1 -0
- package/dist/conformance/src/privacy/traverse.d.ts +33 -0
- package/dist/conformance/src/privacy/traverse.js +61 -0
- package/dist/conformance/src/privacy/traverse.js.map +1 -0
- package/dist/conformance/src/privacy/types.d.ts +58 -0
- package/dist/conformance/src/privacy/types.js +32 -0
- package/dist/conformance/src/privacy/types.js.map +1 -0
- package/dist/conformance/src/privacy/url-analysis.d.ts +40 -0
- package/dist/conformance/src/privacy/url-analysis.js +148 -0
- package/dist/conformance/src/privacy/url-analysis.js.map +1 -0
- package/dist/conformance/src/profiles/check-profile.d.ts +22 -0
- package/dist/conformance/src/profiles/check-profile.js +72 -0
- package/dist/conformance/src/profiles/check-profile.js.map +1 -0
- package/dist/conformance/src/profiles/evaluate-rule.d.ts +27 -0
- package/dist/conformance/src/profiles/evaluate-rule.js +87 -0
- package/dist/conformance/src/profiles/evaluate-rule.js.map +1 -0
- package/dist/conformance/src/profiles/load-profile.d.ts +30 -0
- package/dist/conformance/src/profiles/load-profile.js +95 -0
- package/dist/conformance/src/profiles/load-profile.js.map +1 -0
- package/dist/conformance/src/profiles/resolve-pointer.d.ts +44 -0
- package/dist/conformance/src/profiles/resolve-pointer.js +123 -0
- package/dist/conformance/src/profiles/resolve-pointer.js.map +1 -0
- package/dist/conformance/src/profiles/select-rules.d.ts +13 -0
- package/dist/conformance/src/profiles/select-rules.js +42 -0
- package/dist/conformance/src/profiles/select-rules.js.map +1 -0
- package/dist/conformance/src/profiles/types.d.ts +101 -0
- package/dist/conformance/src/profiles/types.js +31 -0
- package/dist/conformance/src/profiles/types.js.map +1 -0
- package/dist/conformance/src/profiles/validate-profile-definition.d.ts +12 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js +47 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js.map +1 -0
- package/dist/conformance/src/sources.d.ts +53 -0
- package/dist/conformance/src/sources.js +153 -0
- package/dist/conformance/src/sources.js.map +1 -0
- package/dist/conformance/src/validate-core.d.ts +30 -0
- package/dist/conformance/src/validate-core.js +62 -0
- package/dist/conformance/src/validate-core.js.map +1 -0
- package/dist/conformance/src/validate.d.ts +34 -0
- package/dist/conformance/src/validate.js +62 -0
- package/dist/conformance/src/validate.js.map +1 -0
- package/dist/conformance/src/validator-interface.d.ts +35 -0
- package/dist/conformance/src/validator-interface.js +19 -0
- package/dist/conformance/src/validator-interface.js.map +1 -0
- package/package.json +85 -0
- package/profiles/README.md +201 -0
- package/profiles/api-and-integration-management/README.md +271 -0
- package/profiles/api-and-integration-management/profile.json +258 -0
- package/profiles/backup-and-recovery/README.md +318 -0
- package/profiles/backup-and-recovery/profile.json +178 -0
- package/profiles/customer-and-account-management/README.md +340 -0
- package/profiles/customer-and-account-management/profile.json +237 -0
- package/profiles/deployment-and-change-management/README.md +359 -0
- package/profiles/deployment-and-change-management/profile.json +298 -0
- package/profiles/document-management/README.md +120 -0
- package/profiles/document-management/profile.json +170 -0
- package/profiles/financial-transaction-management/README.md +339 -0
- package/profiles/financial-transaction-management/profile.json +247 -0
- package/profiles/identity-and-access-management/README.md +112 -0
- package/profiles/identity-and-access-management/profile.json +120 -0
- package/profiles/incident-management/README.md +338 -0
- package/profiles/incident-management/profile.json +256 -0
- package/profiles/message-broker-management/README.md +344 -0
- package/profiles/message-broker-management/profile.json +399 -0
- package/profiles/profile-definition.schema.json +213 -0
- package/profiles/secrets-and-key-management/README.md +331 -0
- package/profiles/secrets-and-key-management/profile.json +219 -0
- package/schemas/v0.1/audit-event.schema.json +911 -0
- package/semantic-conventions/README.md +52 -0
- package/semantic-conventions/authentication.md +111 -0
- package/semantic-conventions/configuration-and-change.md +122 -0
- package/semantic-conventions/correlation-and-tracing.md +387 -0
- package/semantic-conventions/data-access.md +115 -0
- package/semantic-conventions/event-naming.md +114 -0
- package/semantic-conventions/identity-and-access.md +112 -0
- package/semantic-conventions/privileged-operations.md +110 -0
- package/semantic-conventions/workflow-and-approval.md +109 -0
- package/specification/actor-model.md +135 -0
- package/specification/approval-and-delegation.md +150 -0
- package/specification/authentication.md +116 -0
- package/specification/authorization.md +117 -0
- package/specification/change-model.md +134 -0
- package/specification/delivery.md +136 -0
- package/specification/design-principles.md +137 -0
- package/specification/event-model.md +387 -0
- package/specification/evidence-model.md +109 -0
- package/specification/extension-model.md +159 -0
- package/specification/integrity.md +314 -0
- package/specification/overview.md +172 -0
- package/specification/privacy.md +386 -0
- package/specification/resource-model.md +116 -0
- package/specification/terminology.md +135 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Delivery
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental · This document: Normative**
|
|
4
|
+
|
|
5
|
+
## 1. OpenAuditModel defines no transport
|
|
6
|
+
|
|
7
|
+
The model defines the **event**, not how it moves. There is no OpenAuditModel protocol, wire format,
|
|
8
|
+
endpoint, envelope or delivery guarantee, and none will be introduced.
|
|
9
|
+
|
|
10
|
+
An event is equally valid when it is:
|
|
11
|
+
|
|
12
|
+
- Written to a local file or standard output.
|
|
13
|
+
- Inserted into a table.
|
|
14
|
+
- Published to a topic or queue.
|
|
15
|
+
- Sent as an HTTP request body.
|
|
16
|
+
- Emitted as an OpenTelemetry log record.
|
|
17
|
+
- Wrapped in a CloudEvents envelope.
|
|
18
|
+
- Held in memory in a test.
|
|
19
|
+
|
|
20
|
+
Producers MUST NOT be required to adopt any pipeline in order to conform. See
|
|
21
|
+
[ADR 0003](../decisions/0003-backend-and-transport-independence.md).
|
|
22
|
+
|
|
23
|
+
## 2. Producing
|
|
24
|
+
|
|
25
|
+
A producer:
|
|
26
|
+
|
|
27
|
+
1. MUST emit events that validate against the canonical schema.
|
|
28
|
+
2. SHOULD emit an event for an auditable operation regardless of its outcome. Recording only
|
|
29
|
+
successes produces an audit trail that cannot answer the questions failures raise.
|
|
30
|
+
3. SHOULD emit the event as close to the operation as possible, so that `time` reflects the operation
|
|
31
|
+
rather than a later batch.
|
|
32
|
+
4. SHOULD NOT make the success of the audited operation depend on the success of audit emission,
|
|
33
|
+
unless the operator has explicitly chosen that trade-off. This is a genuine design decision:
|
|
34
|
+
dropping audit records under load and refusing business operations when the audit path is down are
|
|
35
|
+
both defensible, and the specification does not choose for the operator.
|
|
36
|
+
5. MUST record the decision it made, in its own documentation. A consumer cannot tell from the data
|
|
37
|
+
whether missing events mean "nothing happened" or "the buffer overflowed".
|
|
38
|
+
|
|
39
|
+
## 3. Identity and idempotency
|
|
40
|
+
|
|
41
|
+
`id` is the basis of safe delivery.
|
|
42
|
+
|
|
43
|
+
- A producer that retries emission of the same audited operation MUST reuse the same `id`.
|
|
44
|
+
- A producer MUST NOT reuse an `id` for a different operation.
|
|
45
|
+
- A consumer SHOULD deduplicate by `id`.
|
|
46
|
+
- A consumer MUST NOT assume that receiving an event twice means the operation happened twice.
|
|
47
|
+
|
|
48
|
+
This is what makes at-least-once transports safe to use for audit data, and it is why `id` is
|
|
49
|
+
required rather than optional.
|
|
50
|
+
|
|
51
|
+
## 4. Time and observation
|
|
52
|
+
|
|
53
|
+
`time` is when the operation occurred. `observedTime` is when a component in the path observed the
|
|
54
|
+
event.
|
|
55
|
+
|
|
56
|
+
- A producer SHOULD set `time` and SHOULD NOT set `observedTime`.
|
|
57
|
+
- The first component that receives an event MAY set `observedTime`. Later components SHOULD NOT
|
|
58
|
+
overwrite it.
|
|
59
|
+
- A consumer MUST NOT assume the two are equal, and MUST NOT compute pipeline latency from `time`
|
|
60
|
+
alone. Backfilled, queued and replayed events legitimately arrive long after they occurred.
|
|
61
|
+
- Clocks differ. A consumer MUST NOT assume that `time` values from different producers are directly
|
|
62
|
+
comparable at fine resolution, and SHOULD NOT reconstruct causality from timestamps alone.
|
|
63
|
+
`request.traceId` and `request.correlationId` group events that belong to the same execution or
|
|
64
|
+
logical operation; neither establishes which event caused another. See
|
|
65
|
+
[event-model.md §10.3](event-model.md).
|
|
66
|
+
|
|
67
|
+
## 5. Ordering
|
|
68
|
+
|
|
69
|
+
The model provides no global ordering and no total order.
|
|
70
|
+
|
|
71
|
+
- `sequence` orders events **within** one producer, partition or chain. It is meaningless across
|
|
72
|
+
producers.
|
|
73
|
+
- A consumer MUST NOT assume events arrive in `time` order, in `sequence` order, or in the order the
|
|
74
|
+
operations occurred.
|
|
75
|
+
- A consumer that needs ordering MUST establish it from `sequence` within a chain, and MUST tolerate
|
|
76
|
+
gaps. Trace context groups related events but does not order them.
|
|
77
|
+
|
|
78
|
+
## 6. Loss, duplication and gaps
|
|
79
|
+
|
|
80
|
+
Any realistic pipeline will duplicate or drop events. The model responds to this by being explicit
|
|
81
|
+
rather than by promising a guarantee it cannot enforce:
|
|
82
|
+
|
|
83
|
+
| Situation | What the model provides |
|
|
84
|
+
| ------------------ | ----------------------------------------------------- |
|
|
85
|
+
| Duplicate delivery | `id` for deduplication |
|
|
86
|
+
| Reordering | `sequence` within a chain; trace context for grouping |
|
|
87
|
+
| Silent loss | `integrity.previousHash` chains make gaps detectable |
|
|
88
|
+
| Delay | `observedTime` separate from `time` |
|
|
89
|
+
|
|
90
|
+
Detecting a gap requires that something was chained or counted. A pipeline with no chain and no
|
|
91
|
+
counter cannot distinguish a quiet period from a broken exporter, and operators SHOULD monitor
|
|
92
|
+
production rates independently of the audit data itself.
|
|
93
|
+
|
|
94
|
+
## 7. Enrichment
|
|
95
|
+
|
|
96
|
+
Components between producer and consumer MAY enrich events. When they do:
|
|
97
|
+
|
|
98
|
+
1. They MUST NOT alter `event`, `actor`, `subject`, `resource`, `application`, `change`,
|
|
99
|
+
`authorization`, `approval` or `id`. Those describe what the producer observed, and a collector
|
|
100
|
+
was not there.
|
|
101
|
+
2. They MUST NOT set `application` to describe themselves. `application` is the producer. A pipeline
|
|
102
|
+
that rewrites it destroys the provenance of the entire trail.
|
|
103
|
+
3. They MAY set `observedTime` if it is not already present.
|
|
104
|
+
4. They MAY add `extensions` under their own reverse-domain namespace.
|
|
105
|
+
5. They MUST NOT invalidate an event. Any enriched event MUST still validate against the canonical
|
|
106
|
+
schema.
|
|
107
|
+
6. They MUST NOT modify an event that carries `integrity.hash` or `integrity.signature`, because any
|
|
108
|
+
modification invalidates it. Enrichment of a sealed event MUST be carried alongside it, not inside
|
|
109
|
+
it. This includes `observedTime`: every field except the two excluded pointers is part of the
|
|
110
|
+
digest, so adding an observation timestamp to a sealed event breaks its verification. See
|
|
111
|
+
[integrity.md](integrity.md) §4.2.
|
|
112
|
+
|
|
113
|
+
## 8. Batching
|
|
114
|
+
|
|
115
|
+
Batching is a transport concern, and the model defines no batch envelope. Producers that batch:
|
|
116
|
+
|
|
117
|
+
- MUST keep each event individually valid. A batch is a list of complete events, not a shared header
|
|
118
|
+
plus fragments.
|
|
119
|
+
- MAY record `integrity.batchId` to identify the batch an event was sealed with.
|
|
120
|
+
- SHOULD NOT let batching distort `time`.
|
|
121
|
+
|
|
122
|
+
## 9. Optional envelopes
|
|
123
|
+
|
|
124
|
+
Where an envelope is used, the complete audit event SHOULD be carried as the envelope's payload
|
|
125
|
+
without being flattened or restructured, so that it can be validated on arrival exactly as it was
|
|
126
|
+
produced. Conceptual mappings for CloudEvents and OpenTelemetry are described in
|
|
127
|
+
[mappings/](../mappings/), which is informative.
|
|
128
|
+
|
|
129
|
+
Using an envelope MUST NOT change the event. An event that is only valid after being unwrapped and
|
|
130
|
+
transformed is not a conforming event.
|
|
131
|
+
|
|
132
|
+
## 10. Storage
|
|
133
|
+
|
|
134
|
+
Out of scope, deliberately and permanently. The specification says nothing about schemas for storage,
|
|
135
|
+
indexes, partitioning, retention enforcement, compaction or query. It says only that whatever the
|
|
136
|
+
store does, it MUST be able to return the event as it was produced if the trail is to mean anything.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Design Principles
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental · This document: Normative**
|
|
4
|
+
|
|
5
|
+
These principles are the reason the model looks the way it does. They are normative in the sense that
|
|
6
|
+
a proposed change to the core model MUST be evaluated against them, and a change that violates one
|
|
7
|
+
MUST be rejected or must first change the principle.
|
|
8
|
+
|
|
9
|
+
## 1. The core stays small
|
|
10
|
+
|
|
11
|
+
The core model contains only concepts that are meaningful in **every** business application,
|
|
12
|
+
regardless of industry, jurisdiction or architecture.
|
|
13
|
+
|
|
14
|
+
A field belongs in the core only if all of the following hold:
|
|
15
|
+
|
|
16
|
+
1. It is meaningful to a document system, a payment system, a hospital system, a message broker
|
|
17
|
+
console and an internal admin tool alike.
|
|
18
|
+
2. Omitting it would make an audit event ambiguous in a way no other field resolves.
|
|
19
|
+
3. It cannot be expressed as `metadata`, an extension, or a profile requirement without losing
|
|
20
|
+
cross-application comparability.
|
|
21
|
+
|
|
22
|
+
Everything else belongs in `metadata`, `extensions` or a profile. A core model that grows to cover
|
|
23
|
+
every domain stops being a common model.
|
|
24
|
+
|
|
25
|
+
## 2. The core is explainable
|
|
26
|
+
|
|
27
|
+
An engineer MUST be able to read [event-model.md](event-model.md) and instrument an application
|
|
28
|
+
correctly without reading the rest of the specification. Concepts that require a diagram of five
|
|
29
|
+
interacting objects to explain are a sign that the model is wrong, not that the reader is slow.
|
|
30
|
+
|
|
31
|
+
## 3. Composition, not inheritance
|
|
32
|
+
|
|
33
|
+
The model has no type hierarchy, no discriminated event subclasses and no abstract base event.
|
|
34
|
+
An event is a flat set of optional context objects attached to a small required core. A privileged
|
|
35
|
+
configuration change is not a subclass of a change: it is an event with `change`, `approval`,
|
|
36
|
+
`authorization` and `controlCategories` populated.
|
|
37
|
+
|
|
38
|
+
This keeps the schema flat, keeps validation cheap, and keeps profiles additive.
|
|
39
|
+
|
|
40
|
+
## 4. Backend independence
|
|
41
|
+
|
|
42
|
+
The model MUST NOT require, assume or favour any storage system, message broker, telemetry pipeline,
|
|
43
|
+
SIEM, cloud provider or vendor product. An event MUST be equally valid when it is written to a file,
|
|
44
|
+
inserted into a table, published to a topic or held in memory.
|
|
45
|
+
|
|
46
|
+
Consequences that are enforced today:
|
|
47
|
+
|
|
48
|
+
- No field describes a storage location, index, partition key or retention backend.
|
|
49
|
+
- `application` describes the **producer**, never a collector or a store.
|
|
50
|
+
- Schema identifiers never have to be dereferenced: no `$ref` resolves remotely, so validation never requires a network call.
|
|
51
|
+
- Validation is fully offline.
|
|
52
|
+
|
|
53
|
+
See [ADR 0003](../decisions/0003-backend-and-transport-independence.md).
|
|
54
|
+
|
|
55
|
+
## 5. Transport independence
|
|
56
|
+
|
|
57
|
+
The model defines no transport, envelope or delivery guarantee. It MAY be carried by CloudEvents,
|
|
58
|
+
OpenTelemetry, HTTP, a broker, a file or a database write. Delivery concerns — retries, duplicates,
|
|
59
|
+
ordering, batching — are addressed by making the event **idempotent and self-describing**, not by
|
|
60
|
+
defining a protocol. See [delivery.md](delivery.md).
|
|
61
|
+
|
|
62
|
+
## 6. Machine-verifiable by default
|
|
63
|
+
|
|
64
|
+
Every rule that **can** be expressed in JSON Schema Draft 2020-12 **is** expressed there, so that
|
|
65
|
+
conformance is testable rather than aspirational. This includes conditional rules such as "a failure
|
|
66
|
+
carries an error" and "acting for a principal identifies that principal".
|
|
67
|
+
|
|
68
|
+
Where a rule cannot be machine-checked — such as "do not log secrets" — the specification says so
|
|
69
|
+
explicitly rather than pretending validation covers it. See [privacy.md](privacy.md).
|
|
70
|
+
|
|
71
|
+
Schema patterns MUST remain portable across regular expression engines: no look-around and no
|
|
72
|
+
back-references, so that the same schema behaves identically in JavaScript, Python, Go, Java and .NET
|
|
73
|
+
validators.
|
|
74
|
+
|
|
75
|
+
## 7. Strict where it matters, open where it must be
|
|
76
|
+
|
|
77
|
+
Core objects reject unknown properties. This is deliberate: silently accepting an unknown field means
|
|
78
|
+
a producer can believe it is recording something that no consumer will ever read.
|
|
79
|
+
|
|
80
|
+
Vocabularies are treated differently depending on whether the value space is universal:
|
|
81
|
+
|
|
82
|
+
| Kind | Enforcement | Examples |
|
|
83
|
+
| --------------------- | ----------------------------------- | ------------------------------------------------------------------------- |
|
|
84
|
+
| **Closed vocabulary** | `enum` in the schema | `event.outcome`, `event.severity`, `actor.type`, `authorization.decision` |
|
|
85
|
+
| **Open vocabulary** | Form constrained, membership is not | `resource.type`, `resource.classification`, `application.environment` |
|
|
86
|
+
|
|
87
|
+
A vocabulary is closed only when the value set is small, universal, and load-bearing for
|
|
88
|
+
interpretation. A vocabulary is open when organizations legitimately differ — classification schemes
|
|
89
|
+
and environment names differ between companies, and forcing them into a fixed list would produce
|
|
90
|
+
false data rather than comparable data.
|
|
91
|
+
|
|
92
|
+
Both are constrained in **form**: open vocabulary values are lower-case, hyphen-separated tokens, so
|
|
93
|
+
that they remain comparable even when they are not identical.
|
|
94
|
+
|
|
95
|
+
## 8. Privacy is a first-class constraint
|
|
96
|
+
|
|
97
|
+
Audit data is high-risk data: it concentrates who did what to whom. The model therefore:
|
|
98
|
+
|
|
99
|
+
- Prohibits specific values outright, in normative language.
|
|
100
|
+
- Prefers stable identifiers over personal identifiers.
|
|
101
|
+
- Prefers route templates over resolved URLs.
|
|
102
|
+
- Prefers changed field names over changed field values.
|
|
103
|
+
- Prefers references over embedded payloads.
|
|
104
|
+
- Treats capture as an **allowlist**: a producer records the fields it decided to record, never
|
|
105
|
+
"whatever the request contained".
|
|
106
|
+
|
|
107
|
+
See [privacy.md](privacy.md).
|
|
108
|
+
|
|
109
|
+
## 9. Regulation-neutral, industry-neutral, jurisdiction-neutral
|
|
110
|
+
|
|
111
|
+
The core model MUST NOT contain a regulation identifier, an article number, a control identifier, a
|
|
112
|
+
country-specific field or an industry-specific field. Not because compliance does not matter, but
|
|
113
|
+
because a model that encodes one framework becomes unusable everywhere that framework does not apply,
|
|
114
|
+
and becomes wrong when the framework is revised.
|
|
115
|
+
|
|
116
|
+
Regulatory interpretation is a **mapping over** audit data, produced and maintained separately from
|
|
117
|
+
the data model. `controlCategories` exists to carry the neutral half of that relationship.
|
|
118
|
+
|
|
119
|
+
## 10. Stability of meaning
|
|
120
|
+
|
|
121
|
+
An event name, once published, keeps its meaning. A field, once defined, keeps its meaning. If the
|
|
122
|
+
meaning must change, the name changes.
|
|
123
|
+
|
|
124
|
+
Silent redefinition is worse than a breaking change: a breaking change is detected by a validator,
|
|
125
|
+
while a silent redefinition corrupts historical analysis without anyone noticing.
|
|
126
|
+
|
|
127
|
+
## 11. Specification before implementation
|
|
128
|
+
|
|
129
|
+
The normative model, the canonical schema and the conformance tooling come before production SDKs.
|
|
130
|
+
An SDK written first would freeze accidental implementation details into the standard. See
|
|
131
|
+
[ADR 0001](../decisions/0001-specification-first.md).
|
|
132
|
+
|
|
133
|
+
## 12. Do not confuse completeness with quality
|
|
134
|
+
|
|
135
|
+
An event that populates every optional object is not a better audit event than one that populates
|
|
136
|
+
five fields correctly. Optional fields exist so that applications with a real need can express it —
|
|
137
|
+
not as a checklist. Producers SHOULD populate what their audit purpose requires and omit the rest.
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Event Model
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental · This document: Normative**
|
|
4
|
+
|
|
5
|
+
## 1. Structure
|
|
6
|
+
|
|
7
|
+
An audit event is a JSON object with a small required core and a set of optional context objects.
|
|
8
|
+
|
|
9
|
+
### 1.1 Required fields
|
|
10
|
+
|
|
11
|
+
A conforming event MUST contain exactly these seven fields:
|
|
12
|
+
|
|
13
|
+
| Field | Type | Meaning |
|
|
14
|
+
| ------------- | ------ | ----------------------------------------- |
|
|
15
|
+
| `specVersion` | string | Specification version. MUST be `"0.1"`. |
|
|
16
|
+
| `id` | string | Globally unique identifier of this event. |
|
|
17
|
+
| `time` | string | When the audited operation occurred. |
|
|
18
|
+
| `event` | object | What happened and how it ended. |
|
|
19
|
+
| `actor` | object | Who technically performed the operation. |
|
|
20
|
+
| `resource` | object | What the operation acted upon. |
|
|
21
|
+
| `application` | object | Which application produced the event. |
|
|
22
|
+
|
|
23
|
+
### 1.2 Optional fields
|
|
24
|
+
|
|
25
|
+
| Field | Type | Defined in |
|
|
26
|
+
| ------------------- | ------- | -------------------------------------------------------- |
|
|
27
|
+
| `observedTime` | string | This document, §4 |
|
|
28
|
+
| `sequence` | integer | This document, §5 |
|
|
29
|
+
| `subject` | object | [actor-model.md](actor-model.md) |
|
|
30
|
+
| `delegation` | object | [approval-and-delegation.md](approval-and-delegation.md) |
|
|
31
|
+
| `relatedResources` | array | [resource-model.md](resource-model.md) |
|
|
32
|
+
| `organization` | object | This document, §9 |
|
|
33
|
+
| `authentication` | object | [authentication.md](authentication.md) |
|
|
34
|
+
| `authorization` | object | [authorization.md](authorization.md) |
|
|
35
|
+
| `approval` | object | [approval-and-delegation.md](approval-and-delegation.md) |
|
|
36
|
+
| `request` | object | This document, §10 |
|
|
37
|
+
| `change` | object | [change-model.md](change-model.md) |
|
|
38
|
+
| `reason` | object | This document, §11 |
|
|
39
|
+
| `evidence` | array | [evidence-model.md](evidence-model.md) |
|
|
40
|
+
| `integrity` | object | [integrity.md](integrity.md) |
|
|
41
|
+
| `privacy` | object | [privacy.md](privacy.md) |
|
|
42
|
+
| `controlCategories` | array | This document, §12 |
|
|
43
|
+
| `tags` | array | This document, §13 |
|
|
44
|
+
| `metadata` | object | [extension-model.md](extension-model.md) |
|
|
45
|
+
| `extensions` | object | [extension-model.md](extension-model.md) |
|
|
46
|
+
|
|
47
|
+
### 1.3 Strictness
|
|
48
|
+
|
|
49
|
+
The event object and every core object within it MUST reject unknown properties.
|
|
50
|
+
|
|
51
|
+
Domain-specific data MUST NOT be added as a new top-level property. It belongs in `metadata`,
|
|
52
|
+
`extensions`, an object's `attributes`, or a profile.
|
|
53
|
+
|
|
54
|
+
An OPTIONAL object, when present, MUST contain at least one property. An empty object carries no
|
|
55
|
+
information and is rejected, so that "the producer had nothing to say" and "the producer said
|
|
56
|
+
nothing" are not confused.
|
|
57
|
+
|
|
58
|
+
## 2. Event identity
|
|
59
|
+
|
|
60
|
+
`id` MUST be a globally unique, idempotent identifier for the event.
|
|
61
|
+
|
|
62
|
+
- The specification does NOT mandate an identifier technology.
|
|
63
|
+
- UUIDv4, UUIDv7 and ULID are RECOMMENDED. Any collision-resistant identifier is acceptable.
|
|
64
|
+
- A database-generated sequential integer MUST NOT be required by an implementation of this
|
|
65
|
+
specification, because it is not unique across producers and is not available before the event is
|
|
66
|
+
stored.
|
|
67
|
+
- A producer that retries emission of the same audited operation MUST reuse the same `id`, so that
|
|
68
|
+
consumers can deduplicate. See [delivery.md](delivery.md).
|
|
69
|
+
- `id` SHOULD be opaque. It MUST NOT encode personal data.
|
|
70
|
+
|
|
71
|
+
## 3. Event time
|
|
72
|
+
|
|
73
|
+
`time` is when the audited operation **occurred**, as observed by the producer.
|
|
74
|
+
|
|
75
|
+
- MUST be an RFC 3339 compatible date-time string.
|
|
76
|
+
- UTC is RECOMMENDED. A producer that records a local offset MUST include the offset.
|
|
77
|
+
- `time` MUST reflect the operation, not the moment the event object was serialized, when those
|
|
78
|
+
differ meaningfully.
|
|
79
|
+
|
|
80
|
+
## 4. Observed time
|
|
81
|
+
|
|
82
|
+
`observedTime` is when a collector, exporter, gateway or receiving system **observed** the event.
|
|
83
|
+
|
|
84
|
+
- MUST be an RFC 3339 compatible date-time string.
|
|
85
|
+
- MUST NOT be assumed equal to `time`. Queued, batched, retried and backfilled events legitimately
|
|
86
|
+
have an observed time much later than their event time.
|
|
87
|
+
- A producer SHOULD NOT set `observedTime`; it is normally set by the first component that receives
|
|
88
|
+
the event.
|
|
89
|
+
- A consumer that needs to reason about pipeline latency MUST use `observedTime`, and MUST NOT infer
|
|
90
|
+
latency from `time` alone.
|
|
91
|
+
|
|
92
|
+
## 5. Sequence
|
|
93
|
+
|
|
94
|
+
`sequence` is a producer-assigned, monotonically increasing, non-negative integer used to order
|
|
95
|
+
events that share a timestamp.
|
|
96
|
+
|
|
97
|
+
- Ordering is meaningful only **within** a single producer, partition or integrity chain.
|
|
98
|
+
- A consumer MUST NOT assume that sequence numbers are comparable across producers, or that they are
|
|
99
|
+
contiguous.
|
|
100
|
+
|
|
101
|
+
## 6. Event descriptor
|
|
102
|
+
|
|
103
|
+
The `event` object describes what happened.
|
|
104
|
+
|
|
105
|
+
### 6.1 Required
|
|
106
|
+
|
|
107
|
+
| Field | Meaning |
|
|
108
|
+
| ---------- | ---------------------------------- |
|
|
109
|
+
| `name` | Hierarchical event name. See §7. |
|
|
110
|
+
| `category` | Coarse grouping of the event name. |
|
|
111
|
+
| `outcome` | Result of the audited operation. |
|
|
112
|
+
|
|
113
|
+
### 6.2 Optional
|
|
114
|
+
|
|
115
|
+
| Field | Meaning |
|
|
116
|
+
| ---------- | ---------------------------------------------------------------------------- |
|
|
117
|
+
| `type` | Domain-independent activity classifier, such as `create`, `read`, `approve`. |
|
|
118
|
+
| `severity` | Audit significance of the event. |
|
|
119
|
+
| `summary` | Short human-readable description. |
|
|
120
|
+
| `error` | Sanitized failure descriptor. See §6.5. |
|
|
121
|
+
|
|
122
|
+
### 6.3 Outcome
|
|
123
|
+
|
|
124
|
+
`outcome` MUST be one of:
|
|
125
|
+
|
|
126
|
+
| Value | Meaning |
|
|
127
|
+
| --------- | ----------------------------------------------------------------------------------------- |
|
|
128
|
+
| `success` | The operation completed as requested. |
|
|
129
|
+
| `failure` | The operation did not complete. |
|
|
130
|
+
| `partial` | The operation completed for some targets and failed for others. |
|
|
131
|
+
| `unknown` | The producer cannot determine the result, for example after a timeout on a remote system. |
|
|
132
|
+
|
|
133
|
+
A denied authorization is a `failure` outcome with an `authorization.decision` of `deny`. It is not a
|
|
134
|
+
separate outcome value.
|
|
135
|
+
|
|
136
|
+
### 6.4 Severity
|
|
137
|
+
|
|
138
|
+
`severity`, when present, MUST be one of `debug`, `info`, `low`, `medium`, `high`, `critical`.
|
|
139
|
+
|
|
140
|
+
Severity describes the **audit significance** of the operation, not the severity of a software fault.
|
|
141
|
+
A successful privileged configuration change in production is `critical` even though nothing went
|
|
142
|
+
wrong. A failed read of a public resource is `info` even though it failed.
|
|
143
|
+
|
|
144
|
+
The scale is closed in v0.1 because an ordinal scale with producer-defined members cannot be
|
|
145
|
+
compared. Reopening it is an open question for v0.2.
|
|
146
|
+
|
|
147
|
+
### 6.5 Error
|
|
148
|
+
|
|
149
|
+
When `event.outcome` is `failure`, the event MUST contain `event.error`.
|
|
150
|
+
|
|
151
|
+
`event.error` MUST contain `code`, a stable machine-readable failure code defined by the producer.
|
|
152
|
+
It MAY contain `type`, `message` and `retryable`.
|
|
153
|
+
|
|
154
|
+
When `outcome` is `partial`, the event SHOULD contain `event.error` describing the failing portion.
|
|
155
|
+
|
|
156
|
+
`event.error` MUST NOT contain:
|
|
157
|
+
|
|
158
|
+
- Secrets, tokens, credentials or connection strings.
|
|
159
|
+
- Personal data beyond what the audit purpose requires.
|
|
160
|
+
- Full request or response payloads.
|
|
161
|
+
- Internal stack traces, unless explicitly permitted by the operator's policy.
|
|
162
|
+
|
|
163
|
+
Error text is for humans. A consumer MUST NOT parse `message` to make decisions; that is what `code`
|
|
164
|
+
is for.
|
|
165
|
+
|
|
166
|
+
### 6.6 Summary
|
|
167
|
+
|
|
168
|
+
`summary` is optional human-readable text. A consumer MUST NOT parse it. It MUST NOT contain values
|
|
169
|
+
prohibited by [privacy.md](privacy.md).
|
|
170
|
+
|
|
171
|
+
## 7. Event naming
|
|
172
|
+
|
|
173
|
+
### 7.1 Form
|
|
174
|
+
|
|
175
|
+
Event names are hierarchical, lower-case, dot-separated names of the general form:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
domain.resource.action
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
At least two segments are REQUIRED. Segments are lower-case and may contain digits and hyphenated
|
|
182
|
+
words, for example `offset-reset`.
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
authentication.login
|
|
186
|
+
identity.role.assign
|
|
187
|
+
document.share.create
|
|
188
|
+
incident.case.close
|
|
189
|
+
configuration.setting.update
|
|
190
|
+
deployment.release.approve
|
|
191
|
+
queue.consumer.offset-reset
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The core schema MUST NOT enumerate event names. Recommended names are published as
|
|
195
|
+
[semantic conventions](../semantic-conventions/event-naming.md), which producers SHOULD follow.
|
|
196
|
+
|
|
197
|
+
### 7.2 Rules
|
|
198
|
+
|
|
199
|
+
1. Event names MUST be stable across releases.
|
|
200
|
+
2. Event names MUST NOT contain product names.
|
|
201
|
+
3. Event names MUST NOT contain company names.
|
|
202
|
+
4. Event names MUST NOT contain jurisdiction names.
|
|
203
|
+
5. Event names MUST describe the audited operation, not the implementation method. `identity.role.assign`,
|
|
204
|
+
not `identity.role.update-via-graph-api`.
|
|
205
|
+
6. Success and failure of the same operation SHOULD use the **same** event name with different
|
|
206
|
+
`outcome` values. `authentication.login` with `outcome: failure`, not `authentication.login-failed`.
|
|
207
|
+
7. The meaning of an event name MUST NOT change silently between versions. If the meaning changes,
|
|
208
|
+
the name changes.
|
|
209
|
+
|
|
210
|
+
## 8. Application context
|
|
211
|
+
|
|
212
|
+
`application` describes the **producer** of the event.
|
|
213
|
+
|
|
214
|
+
### 8.1 Required
|
|
215
|
+
|
|
216
|
+
| Field | Meaning |
|
|
217
|
+
| ------------- | --------------------------------------------------- |
|
|
218
|
+
| `name` | Stable name of the producing application. |
|
|
219
|
+
| `environment` | Deployment environment the producer was running in. |
|
|
220
|
+
|
|
221
|
+
### 8.2 Optional
|
|
222
|
+
|
|
223
|
+
`version`, `instance`, `service`, `component`, `region`, `zone`, `deploymentId`.
|
|
224
|
+
|
|
225
|
+
### 8.3 Rules
|
|
226
|
+
|
|
227
|
+
1. `application` MUST describe the application that **produced** the event. It MUST NOT describe a
|
|
228
|
+
collector, forwarder, gateway, storage backend or SIEM that later received it. An audit trail in
|
|
229
|
+
which every event claims to come from the log pipeline is not an audit trail.
|
|
230
|
+
2. `application.name` SHOULD be stable across releases and SHOULD be lower-case and hyphenated.
|
|
231
|
+
3. `environment` is an open vocabulary. The RECOMMENDED values are `local`, `development`, `test`,
|
|
232
|
+
`staging`, `production`, `disaster-recovery` and `unknown`. Organizations that use additional
|
|
233
|
+
environment names, such as a user acceptance environment, MAY use their own token rather than
|
|
234
|
+
forcing it into one of the recommended values.
|
|
235
|
+
4. `region` and `zone` are operator-defined identifiers. They MUST NOT be assumed to follow any
|
|
236
|
+
particular provider's naming.
|
|
237
|
+
|
|
238
|
+
## 9. Organization context
|
|
239
|
+
|
|
240
|
+
`organization` is OPTIONAL and MAY contain `tenantId`, `organizationId`, `departmentId`,
|
|
241
|
+
`workspaceId` and `businessUnitId`.
|
|
242
|
+
|
|
243
|
+
Not every application is multi-tenant. The core specification MUST NOT require organization context,
|
|
244
|
+
and a consumer MUST NOT treat its absence as an error.
|
|
245
|
+
|
|
246
|
+
Where a tenant applies to a specific principal or resource rather than to the operation as a whole,
|
|
247
|
+
`actor.tenantId` and `resource.tenantId` are the more precise place to record it.
|
|
248
|
+
|
|
249
|
+
## 10. Request and correlation context
|
|
250
|
+
|
|
251
|
+
`request` is OPTIONAL and describes how the operation was requested.
|
|
252
|
+
|
|
253
|
+
| Field | Notes |
|
|
254
|
+
| --------------------------------- | ----------------------------------------------------------- |
|
|
255
|
+
| `requestId` | The inbound request currently being served. |
|
|
256
|
+
| `correlationId` | The logical operation the event belongs to. See §10.1. |
|
|
257
|
+
| `traceId`, `spanId` | W3C Trace Context compatible. See §10.2. |
|
|
258
|
+
| `ipAddress`, `forwardedFor` | IPv4 or IPv6 literals. Personal data in many jurisdictions. |
|
|
259
|
+
| `userAgent`, `protocol`, `method` | Client and protocol context. |
|
|
260
|
+
| `route` | Route template. See §10.4. |
|
|
261
|
+
|
|
262
|
+
Guidance on choosing between these identifiers is in
|
|
263
|
+
[semantic-conventions/correlation-and-tracing.md](../semantic-conventions/correlation-and-tracing.md),
|
|
264
|
+
which is informative.
|
|
265
|
+
|
|
266
|
+
### 10.1 Correlation identifiers
|
|
267
|
+
|
|
268
|
+
`requestId` identifies the inbound request currently being served. It is scoped to one service
|
|
269
|
+
handling one call, and SHOULD NOT be propagated into asynchronous messages: a message consumer is not
|
|
270
|
+
serving the request that produced the message.
|
|
271
|
+
|
|
272
|
+
`correlationId` identifies the logical operation, workflow, job, conversation or business process the
|
|
273
|
+
event belongs to. It MAY span services, messages, trace boundaries and time, and it is the identifier
|
|
274
|
+
that remains stable when a `traceId` does not.
|
|
275
|
+
|
|
276
|
+
### 10.2 Trace identifiers
|
|
277
|
+
|
|
278
|
+
`traceId` MUST be 32 lower-case hexadecimal characters and `spanId` MUST be 16 lower-case
|
|
279
|
+
hexadecimal characters, matching W3C Trace Context. The all-zero value is invalid for both.
|
|
280
|
+
|
|
281
|
+
This makes audit events correlatable with distributed traces without requiring any particular
|
|
282
|
+
telemetry stack. See [mappings/opentelemetry.md](../mappings/opentelemetry.md), which is informative.
|
|
283
|
+
|
|
284
|
+
Producers SHOULD obtain `traceId` and `spanId` from the active trace context rather than generating
|
|
285
|
+
identifiers specifically for the audit event. An identifier minted for the audit record is
|
|
286
|
+
well-formed and correlates with nothing.
|
|
287
|
+
|
|
288
|
+
`spanId` SHOULD be recorded only together with `traceId`. A span identifier alone cannot be resolved,
|
|
289
|
+
because there is nothing to resolve it against.
|
|
290
|
+
|
|
291
|
+
Raw `traceparent` and `tracestate` values SHOULD NOT be stored. Producers SHOULD extract the trace and
|
|
292
|
+
span identifiers and discard the remainder: the version and flag fields describe the tracing system's
|
|
293
|
+
own decisions, and `tracestate` is vendor-specific and may carry tenant or account information.
|
|
294
|
+
|
|
295
|
+
### 10.3 Trust and independence
|
|
296
|
+
|
|
297
|
+
Correlation identifiers — `requestId`, `correlationId`, `traceId` and `spanId` — are **observational
|
|
298
|
+
metadata**. They MUST NOT be used as proof of identity, authorization, authenticity, integrity or
|
|
299
|
+
tenant isolation. Each can be supplied by a caller, guessed, replayed or copied between tenants.
|
|
300
|
+
`authentication`, `authorization` and `integrity` are the fields that carry those guarantees.
|
|
301
|
+
|
|
302
|
+
A shared `traceId` indicates that two events belong to the same execution. It does NOT establish
|
|
303
|
+
which event caused another, nor the order in which they occurred. Ordering within a producer is
|
|
304
|
+
established by `sequence`; see [delivery.md](delivery.md).
|
|
305
|
+
|
|
306
|
+
Audit event generation MUST NOT depend on trace sampling. An audit event MUST be produced whether or
|
|
307
|
+
not a trace is sampled, recorded or retained, and a `traceId` that resolves to no stored trace is a
|
|
308
|
+
normal outcome rather than an error.
|
|
309
|
+
|
|
310
|
+
Personal data, credentials, account numbers and sensitive business values SHOULD NOT be used as
|
|
311
|
+
correlation identifiers. A non-reversible surrogate SHOULD be used instead; see
|
|
312
|
+
[privacy.md](privacy.md).
|
|
313
|
+
|
|
314
|
+
### 10.4 Route templates
|
|
315
|
+
|
|
316
|
+
`route` SHOULD be the **route template**, not the resolved URL:
|
|
317
|
+
|
|
318
|
+
```text
|
|
319
|
+
/users/{userId}/roles ← RECOMMENDED
|
|
320
|
+
/users/472918/roles ← NOT RECOMMENDED
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The resolved identifier belongs in `resource.id`, where it is structured and can be redacted
|
|
324
|
+
independently.
|
|
325
|
+
|
|
326
|
+
Query strings and fragments are rejected by the schema. Producers MUST NOT capture query strings
|
|
327
|
+
automatically: they routinely carry search terms, filters, tokens and personal identifiers that no
|
|
328
|
+
one decided to audit.
|
|
329
|
+
|
|
330
|
+
## 11. Reason
|
|
331
|
+
|
|
332
|
+
`reason` is OPTIONAL and records **why** the operation was performed: a business justification,
|
|
333
|
+
support request, change ticket, incident response or emergency access explanation.
|
|
334
|
+
|
|
335
|
+
| Field | Meaning |
|
|
336
|
+
| ----------- | ------------------------------------------------------ |
|
|
337
|
+
| `code` | Machine-readable justification code. |
|
|
338
|
+
| `text` | Human-readable justification. |
|
|
339
|
+
| `reference` | Identifier of the record that justifies the operation. |
|
|
340
|
+
|
|
341
|
+
`reason` MUST NOT be used for technical error information. A justification explains intent; an error
|
|
342
|
+
explains failure. An event may legitimately carry both.
|
|
343
|
+
|
|
344
|
+
## 12. Control categories
|
|
345
|
+
|
|
346
|
+
`controlCategories` is an OPTIONAL array of regulation-neutral labels describing the kind of control
|
|
347
|
+
this event provides evidence for:
|
|
348
|
+
|
|
349
|
+
```text
|
|
350
|
+
authentication-logging
|
|
351
|
+
privileged-access
|
|
352
|
+
data-access-logging
|
|
353
|
+
external-data-sharing
|
|
354
|
+
change-approval
|
|
355
|
+
separation-of-duties
|
|
356
|
+
configuration-integrity
|
|
357
|
+
incident-traceability
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Control categories MUST NOT contain regulation names, article numbers, control framework identifiers
|
|
361
|
+
or jurisdiction names. Mapping a control category to a specific framework is the job of a separate,
|
|
362
|
+
optional mapping artifact maintained outside the core specification.
|
|
363
|
+
|
|
364
|
+
## 13. Tags
|
|
365
|
+
|
|
366
|
+
`tags` is an OPTIONAL array of producer-defined labels for filtering and routing. Tags carry no
|
|
367
|
+
normative meaning, and a consumer MUST NOT derive audit semantics from them.
|
|
368
|
+
|
|
369
|
+
## 14. Complete minimal event
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"specVersion": "0.1",
|
|
374
|
+
"id": "018f1b5c-6d2a-7c3e-9a1b-4f5e6d7c8b9a",
|
|
375
|
+
"time": "2026-03-14T09:24:31.412Z",
|
|
376
|
+
"event": {
|
|
377
|
+
"name": "data.record.update",
|
|
378
|
+
"category": "data-modification",
|
|
379
|
+
"outcome": "success"
|
|
380
|
+
},
|
|
381
|
+
"actor": { "type": "user", "id": "user-123" },
|
|
382
|
+
"resource": { "type": "record", "id": "resource-123" },
|
|
383
|
+
"application": { "name": "application-service", "environment": "production" }
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
This is a complete, conforming audit event. Nothing else is required.
|