diff --git a/.spellcheck-en-custom.txt b/.spellcheck-en-custom.txt index 4bb487d..dddf06c 100644 --- a/.spellcheck-en-custom.txt +++ b/.spellcheck-en-custom.txt @@ -25,6 +25,7 @@ cardpane cdevent cdevents ce +changewindow cloudevents contenttype customdata diff --git a/conformance/changewindow_access.json b/conformance/changewindow_access.json new file mode 100644 index 0000000..d0d1afc --- /dev/null +++ b/conformance/changewindow_access.json @@ -0,0 +1,64 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.access.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z", + "change": { + "id": "deploy-90217", + "source": "/prod/deploys" + }, + "status": "rejected", + "reason": "Deployment requested during active holiday freeze" + } + } +} diff --git a/conformance/changewindow_breached.json b/conformance/changewindow_breached.json new file mode 100644 index 0000000..ae603b9 --- /dev/null +++ b/conformance/changewindow_breached.json @@ -0,0 +1,63 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.breached.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z", + "change": { + "id": "deploy-90217", + "source": "/prod/deploys" + }, + "reason": "Deployment executed during active holiday freeze" + } + } +} diff --git a/conformance/changewindow_closed.json b/conformance/changewindow_closed.json new file mode 100644 index 0000000..db522ae --- /dev/null +++ b/conformance/changewindow_closed.json @@ -0,0 +1,58 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.closed.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z" + } + } +} diff --git a/conformance/changewindow_created.json b/conformance/changewindow_created.json new file mode 100644 index 0000000..be0c55b --- /dev/null +++ b/conformance/changewindow_created.json @@ -0,0 +1,62 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.created.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z", + "author": { + "id": "alice", + "source": "/changecontrol/prod" + } + } + } +} diff --git a/conformance/changewindow_modified.json b/conformance/changewindow_modified.json new file mode 100644 index 0000000..f7fbe11 --- /dev/null +++ b/conformance/changewindow_modified.json @@ -0,0 +1,62 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.modified.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z", + "author": { + "id": "alice", + "source": "/changecontrol/prod" + } + } + } +} diff --git a/conformance/changewindow_opened.json b/conformance/changewindow_opened.json new file mode 100644 index 0000000..286e5f0 --- /dev/null +++ b/conformance/changewindow_opened.json @@ -0,0 +1,58 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.opened.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z" + } + } +} diff --git a/conformance/changewindow_revoked.json b/conformance/changewindow_revoked.json new file mode 100644 index 0000000..4a5e03c --- /dev/null +++ b/conformance/changewindow_revoked.json @@ -0,0 +1,63 @@ +{ + "context": { + "specversion": "0.6.0-draft", + "id": "271069a8-fc18-44f1-b38f-9d70a1695819", + "chainId": "4c8cb7dd-3448-41de-8768-eec704e2829b", + "source": "/changecontrol/prod", + "type": "dev.cdevents.changewindow.revoked.0.1.0-draft", + "timestamp": "2026-12-24T14:05:00Z", + "schemaUri": "https://myorg.com/schema/custom", + "links": [ + { + "linkType": "RELATION", + "linkKind": "TRIGGER", + "target": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "PATH", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + }, + { + "linkType": "END", + "from": { + "contextId": "5328c37f-bb7e-4bb7-84ea-9f5f85e4a7ce" + }, + "tags": { + "foo1": "bar", + "foo2": "bar" + } + } + ] + }, + "subject": { + "id": "freeze-holiday-2026", + "source": "/changecontrol/prod", + "content": { + "target": { + "id": "prod", + "source": "/clusters/prod", + "type": "environment" + }, + "windowType": "restrictive", + "notBefore": "2026-12-20T00:00:00Z", + "notAfter": "2027-01-02T00:00:00Z", + "author": { + "id": "alice", + "source": "/changecontrol/prod" + }, + "reason": "Freeze lifted early by change board" + } + } +} diff --git a/continuous-deployment.md b/continuous-deployment.md index fba55e9..cffa8c4 100644 --- a/continuous-deployment.md +++ b/continuous-deployment.md @@ -16,10 +16,13 @@ Continuous Deployment (CD) events are related to continuous deployment pipelines This specification defines two subjects in this stage: `environment` and `service`. The term `service` is used to represent a running Artifact. A `service` can represent a binary that is running, a daemon, an application, a docker container. The term `environment` represent any platform which has all the means to run a `service`. +Changes to a target are often governed by when they are permitted to happen. A [`changeWindow`](#changewindow) is a bounded interval during which a window policy governs changes to a target. + | Subject | Description | Predicates | |---------|-------------|------------| | [`environment`](#environment) | An environment where to run services | [`created`](#environment-created), [`modified`](#environment-modified), [`deleted`](#environment-deleted)| | [`service`](#service) | A service | [`deployed`](#service-deployed), [`upgraded`](#service-upgraded), [`rolledback`](#service-rolledback), [`removed`](#service-removed), [`published`](#service-published)| +| [`changeWindow`](#changewindow) | A bounded interval during which changes to a target are governed by a window policy | [`created`](#changewindow-created), [`opened`](#changewindow-opened), [`access`](#changewindow-access), [`breached`](#changewindow-breached), [`modified`](#changewindow-modified), [`revoked`](#changewindow-revoked), [`closed`](#changewindow-closed)| ### `environment` @@ -43,6 +46,31 @@ A `service` can represent for example a binary that is running, a daemon, an app | environment | `Object` ([`environment`](#environment)) | Reference for the environment where the service runs | `{"id": "1234"}`, `{"id": "maven123, "source": "tekton-dev-123"}` | | artifactId | `Purl` | Identifier of the artifact deployed with this service | `pkg:oci/myapp@sha256%3A0b31b1c02ff458ad9b7b81cbdf8f028bd54699fa151f221d1e8de6817db93427`, `pkg:golang/mygit.com/myorg/myapp@234fd47e07d1004f0aed9c` | +### `changeWindow` + +A [`changeWindow`](#changewindow) is a bounded interval during which changes to a target are governed by a window policy. The window is the gate: these events report that a gate exists, when it is in force, and when it stops being in force. + +A window is either **permissive** — changes are allowed only inside it, as with a maintenance or deployment window — or **restrictive**, where changes are forbidden inside it, as with a freeze. `windowType` carries which, and the audit reading of every event on this subject depends on it, so it is present on all of them. + +Window-governed change control has no representation in the vocabulary today. `environment.modified` says a mutation happened; it cannot say whether that mutation was permitted or whether a restriction was violated, and those are different audit facts. + +Arriving at the gate is a separate occurrence from the gate existing. [`access`](#changewindow-access) reports a change presented to the window and the result, carrying the same `status` values as the `approval` subject's `closed` event so consumers handle both alike. [`breached`](#changewindow-breached) is not an access result: it reports a change that took effect without passing the gate at all. + +`revoked` and `closed` are the terminal events. A revoked window was invalidated by an actor before its bounds ran out; a closed window reached `notAfter` and ended normally. Between them they account for the window ceasing to exist, so there is no separate deletion predicate. + +| Field | Type | Description | Examples | +|-------|------|-------------|----------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | +| source | `URI-Reference` | See [source](spec.md#source-subject) | `/changecontrol/prod` | +| target | `Object` | The resource whose changes the window governs. `type` names its kind. | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | +| windowType | `String (enum)` | Whether changes are allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | +| author | `Object` | The actor the producer attributes the action to | `{"id": "alice", "source": "/changecontrol/prod"}` | +| change | `Object` ([`change`](source-code-version-control.md#change)) | The change presented to the window, or the change that bypassed it | `{"id": "deploy-90217", "source": "/prod/deploys"}` | +| status | `String (enum)` | The result of presenting a change to the window | `approved`, `rejected`, `cancelled`, `expired` | +| reason | `String` | Why the window exists, or detail for a result, revocation or breach | `Holiday change freeze` | + ## Events ### [`environment created`](conformance/environment_created.json) @@ -161,3 +189,141 @@ This event represents an existing instance of a service that has an accessible U | id | `String` | See [id](spec.md#id-subject)| `service/myapp`, `daemonset/myapp` | ✅ | | source | `URI-Reference` | See [source](spec.md#source-subject) | | | | environment | `Object` ([`environment`](#environment)) | Reference for the environment where the service runs | `{"id": "1234"}`, `{"id": "maven123, "source": "tekton-dev-123"}` | ✅ | + +### [`changeWindow created`](conformance/changewindow_created.json) + +A change window has been brought into existence with its bounds, type and target. The window is not necessarily in force yet. + +- Event Type: __`dev.cdevents.changewindow.created.0.1.0-draft`__ +- Predicate: created +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The resource whose changes the window governs | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | Whether changes are allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | `permissive`, `restrictive` | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | ✅ | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | ✅ | +| author | `Object` | The actor the producer attributes the creation to | `{"id": "alice", "source": "/changecontrol/prod"}` | ✅ | +| reason | `String` | Why the window exists | `Holiday change freeze` | | + +### [`changeWindow opened`](conformance/changewindow_opened.json) + +A change window has become active. Its policy is now in force for the target. + +- Event Type: __`dev.cdevents.changewindow.opened.0.1.0-draft`__ +- Predicate: opened +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The resource whose changes the window governs | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | Whether changes are allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | `permissive`, `restrictive` | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | | +| reason | `String` | Why the window exists | `Holiday change freeze` | | + +### [`changeWindow access`](conformance/changewindow_access.json) + +A change was presented to the window and a result was reached. `status` carries the result, using the same values as the `approval` subject's `closed` event: `approved`, `rejected`, `cancelled`, `expired`. + +Receiving this event is a record of what the window decided. A consumer MUST NOT treat it as authorization for anything. + +- Event Type: __`dev.cdevents.changewindow.access.0.1.0-draft`__ +- Predicate: access +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The resource whose changes the window governs | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | Whether changes are allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | `permissive`, `restrictive` | +| change | `Object` ([`change`](source-code-version-control.md#change)) | The change presented to the window | `{"id": "deploy-90217", "source": "/prod/deploys"}` | ✅ | +| status | `String (enum)` | The result of presenting the change to the window | `approved`, `rejected`, `cancelled`, `expired` | `approved`, `rejected`, `cancelled`, `expired` | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | | +| reason | `String` | Detail for the result | `Deployment requested during active holiday freeze` | | + +### [`changeWindow breached`](conformance/changewindow_breached.json) + +A change took effect in violation of the window, without passing the gate. This is not an access result: nothing was presented and nothing decided. + +Which changes constitute a breach depends on polarity. A change outside a `permissive` window is a breach; a change inside a `restrictive` window is a breach. + +- Event Type: __`dev.cdevents.changewindow.breached.0.1.0-draft`__ +- Predicate: breached +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The resource whose changes the window governs | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | Whether changes are allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | `permissive`, `restrictive` | +| change | `Object` ([`change`](source-code-version-control.md#change)) | The change that bypassed the window | `{"id": "deploy-90217", "source": "/prod/deploys"}` | ✅ | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | | +| reason | `String` | Detail for the breach | `Deployment executed during active holiday freeze` | | + +### [`changeWindow modified`](conformance/changewindow_modified.json) + +A change window has been changed. The subject retains its identity across the change. + +This event carries the window's complete state after the change — the resulting `notBefore`, `notAfter`, `windowType` and `target` — rather than a description of what moved. A consumer reading a single `modified` event knows the window's current bounds without holding an earlier event to compare against. + +- Event Type: __`dev.cdevents.changewindow.modified.0.1.0-draft`__ +- Predicate: modified +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The governed resource, as it stands after the change | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | The polarity, as it stands after the change | `permissive`, `restrictive` | `permissive`, `restrictive` | +| notBefore | `Timestamp` | The start bound, as it stands after the change | `2026-12-20T00:00:00Z` | ✅ | +| notAfter | `Timestamp` | The end bound, as it stands after the change | `2027-01-02T00:00:00Z` | ✅ | +| author | `Object` | The actor the producer attributes the change to | `{"id": "alice", "source": "/changecontrol/prod"}` | ✅ | +| reason | `String` | Detail for the change | `Freeze extended through the first trading day` | | + +### [`changeWindow revoked`](conformance/changewindow_revoked.json) + +A change window has been invalidated by an actor before its bounds ran out. Its policy is no longer in force. + +- Event Type: __`dev.cdevents.changewindow.revoked.0.1.0-draft`__ +- Predicate: revoked +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The resource the window governed | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | Whether changes were allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | `permissive`, `restrictive` | +| author | `Object` | The actor the producer attributes the revocation to | `{"id": "alice", "source": "/changecontrol/prod"}` | ✅ | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | | +| reason | `String` | Detail for the revocation | `Freeze lifted early by change board` | | + +### [`changeWindow closed`](conformance/changewindow_closed.json) + +A change window reached `notAfter` and ended normally. Its policy is no longer in force. + +- Event Type: __`dev.cdevents.changewindow.closed.0.1.0-draft`__ +- Predicate: closed +- Subject: [`changeWindow`](#changewindow) + +| Field | Type | Description | Examples | Required | +|-------|------|-------------|----------|----------------------------| +| id | `String` | See [id](spec.md#id-subject)| `freeze-holiday-2026` | ✅ | +| source | `URI-Reference` | [source](spec.md#source) from the context | | | +| target | `Object` | The resource the window governed | `{"id": "prod", "source": "/clusters/prod", "type": "environment"}` | ✅ | +| windowType | `String (enum)` | Whether changes were allowed only inside the window or forbidden inside it | `permissive`, `restrictive` | `permissive`, `restrictive` | +| notBefore | `Timestamp` | Window start bound | `2026-12-20T00:00:00Z` | | +| notAfter | `Timestamp` | Window end bound | `2027-01-02T00:00:00Z` | | +| reason | `String` | Detail for the closure | `Holiday change freeze ended` | | diff --git a/schemas/changewindowaccess.json b/schemas/changewindowaccess.json new file mode 100644 index 0000000..f2d5ef7 --- /dev/null +++ b/schemas/changewindowaccess.json @@ -0,0 +1,174 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-access-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.access.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.access.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "change": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id" + ] + }, + "status": { + "type": "string", + "enum": [ + "approved", + "rejected", + "cancelled", + "expired" + ] + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType", + "change", + "status" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +} diff --git a/schemas/changewindowbreached.json b/schemas/changewindowbreached.json new file mode 100644 index 0000000..85b0b1c --- /dev/null +++ b/schemas/changewindowbreached.json @@ -0,0 +1,164 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-breached-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.breached.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.breached.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "change": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id" + ] + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType", + "change" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +} diff --git a/schemas/changewindowclosed.json b/schemas/changewindowclosed.json new file mode 100644 index 0000000..7431e5e --- /dev/null +++ b/schemas/changewindowclosed.json @@ -0,0 +1,145 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-closed-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.closed.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.closed.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +} diff --git a/schemas/changewindowcreated.json b/schemas/changewindowcreated.json new file mode 100644 index 0000000..efac2e9 --- /dev/null +++ b/schemas/changewindowcreated.json @@ -0,0 +1,166 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-created-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.created.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.created.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "author": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id" + ] + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType", + "author", + "notBefore", + "notAfter" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +} diff --git a/schemas/changewindowmodified.json b/schemas/changewindowmodified.json new file mode 100644 index 0000000..0d857a9 --- /dev/null +++ b/schemas/changewindowmodified.json @@ -0,0 +1,166 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-modified-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.modified.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.modified.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "author": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id" + ] + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType", + "author", + "notBefore", + "notAfter" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +} diff --git a/schemas/changewindowopened.json b/schemas/changewindowopened.json new file mode 100644 index 0000000..f12bcf4 --- /dev/null +++ b/schemas/changewindowopened.json @@ -0,0 +1,145 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-opened-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.opened.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.opened.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +} diff --git a/schemas/changewindowrevoked.json b/schemas/changewindowrevoked.json new file mode 100644 index 0000000..c707015 --- /dev/null +++ b/schemas/changewindowrevoked.json @@ -0,0 +1,164 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://cdevents.dev/0.6.0-draft/schema/changewindow-revoked-event", + "properties": { + "context": { + "properties": { + "specversion": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "enum": [ + "dev.cdevents.changewindow.revoked.0.1.0-draft" + ], + "default": "dev.cdevents.changewindow.revoked.0.1.0-draft" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "schemaUri": { + "type": "string", + "minLength": 1, + "format": "uri" + }, + "chainId": { + "type": "string", + "minLength": 1 + }, + "links": { + "$ref": "links/embeddedlinksarray" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "specversion", + "id", + "source", + "type", + "timestamp" + ] + }, + "subject": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "content": { + "properties": { + "target": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + }, + "type": { + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "type" + ] + }, + "windowType": { + "type": "string", + "enum": [ + "permissive", + "restrictive" + ] + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, + "author": { + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "source": { + "type": "string", + "minLength": 1, + "format": "uri-reference" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id" + ] + }, + "reason": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "target", + "windowType", + "author" + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "content" + ] + }, + "customData": { + "oneOf": [ + { + "type": "object" + }, + { + "type": "string", + "contentEncoding": "base64" + } + ] + }, + "customDataContentType": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "context", + "subject" + ] +}