From cbe7fe03239a500433c7e0872f1d81095a2659bd Mon Sep 17 00:00:00 2001 From: Kishore2572 Date: Thu, 22 Oct 2026 16:57:53 +0530 Subject: [PATCH 1/2] Require proposal_id and version on commitments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A commitment names the proposal and version it was priced against (§ 5.6.1). The seller rejects a new attempt on a stale version, or past valid_until or price_valid_until, and returns the current version. An authenticated replay with the same idempotency_key and terms returns the stored result without re-running those checks; the same key with different terms is a conflict. Keys are opaque and scoped to one buyer and seller pair. § 4.1 states that each revision while published increments version. The display, CTV and audio examples carry proposal_id and version. Refs #24 --- examples/line-item-audio.yaml | 2 ++ examples/line-item-ctv.yaml | 2 ++ examples/line-item-display.yaml | 2 ++ spec/CHANGELOG.md | 5 +++++ spec/openproposal-3.0.md | 26 ++++++++++++++++++++++---- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/examples/line-item-audio.yaml b/examples/line-item-audio.yaml index 4ca2f21..28b49c5 100644 --- a/examples/line-item-audio.yaml +++ b/examples/line-item-audio.yaml @@ -21,6 +21,8 @@ pricing: commitment_bounds: { min_budget: 5000.00 } commitment: + proposal_id: "examplepub-us-q4-2026-omni" + version: 1 basis: spend amount: 60000.00 currency: USD diff --git a/examples/line-item-ctv.yaml b/examples/line-item-ctv.yaml index fda3994..7cc3d82 100644 --- a/examples/line-item-ctv.yaml +++ b/examples/line-item-ctv.yaml @@ -17,6 +17,8 @@ pricing: commitment_bounds: { min_budget: 25000.00 } commitment: + proposal_id: "examplepub-us-q4-2026-omni" + version: 1 basis: spend amount: 160000.00 currency: USD diff --git a/examples/line-item-display.yaml b/examples/line-item-display.yaml index 808af24..929a346 100644 --- a/examples/line-item-display.yaml +++ b/examples/line-item-display.yaml @@ -62,6 +62,8 @@ pricing: commitment_bounds: { min_budget: 10000.00, max_budget: 500000.00 } commitment: + proposal_id: "examplepub-us-q4-2026-omni" + version: 1 basis: spend amount: 80000.00 currency: USD diff --git a/spec/CHANGELOG.md b/spec/CHANGELOG.md index 1dfacef..1e649d5 100644 --- a/spec/CHANGELOG.md +++ b/spec/CHANGELOG.md @@ -5,6 +5,11 @@ issue that caused it, so the comment record and the spec history line up. ## [Unreleased] +- § 4.1, § 5.6.1 — commitments carry `proposal_id` and `version`; a new + attempt on a stale version, or past `valid_until` or `price_valid_until`, is + rejected, and a replay returns the stored result. Breaking + ([#24](https://github.com/IABTechLab/OpenProposal/issues/24)). + ## [3.0-draft-1] — 2026-09-22 Initial draft published for public comment. Ported from the working-group diff --git a/spec/openproposal-3.0.md b/spec/openproposal-3.0.md index 6940958..717c234 100644 --- a/spec/openproposal-3.0.md +++ b/spec/openproposal-3.0.md @@ -181,7 +181,7 @@ items. | Field | Type | Mutability | Description | | :--- | :--- | :--- | :--- | | `proposal_id` | String | immutable | Unique identity for the proposal object | -| `version` | Integer | derived | Monotonically increasing revision number | +| `version` | Integer | derived | Monotonically increasing revision number. Each revision while status is `published` increments it | | `seller_id` | String | immutable | IAB Tech Lab registry reference | | `seller_name` | String | immutable | Canonical legal seller entity name, from the registry | | `type` | Enum | immutable | `standard` \| `custom` | @@ -288,6 +288,8 @@ when status becomes `agreed`. | Key | Required | Meaning | | :--- | :--- | :--- | +| `proposal_id` | REQUIRED | The proposal the commitment is made against | +| `version` | REQUIRED | The proposal `version` the commitment was priced against | | `basis` | REQUIRED | `spend` \| `units` | | `amount` | REQUIRED | The figure committed | | `currency` | REQUIRED when `basis = spend` | ISO-4217 | @@ -296,8 +298,22 @@ when status becomes `agreed`. | `derived_units` | derived | Computed from `amount`, `agreed_rate` and `cost_method`. Informative, not binding | | `idempotency_key` | REQUIRED | Buyer-generated, unique per commitment attempt | -A request carrying an `idempotency_key` already seen MUST return the original -result and MUST NOT create a second commitment. +An `idempotency_key` is opaque and scoped to one buyer and seller pair. The +seller MUST NOT interpret it. + +The seller MUST reject a new commitment attempt whose `version` is not the +current version of the proposal named by `proposal_id`, whose proposal is past +`valid_until`, or whose `pricing[].price_valid_until` has passed. The rejection +MUST return the current version number, so the buyer can fetch it again. These +checks MUST be deterministic, as for `idempotency_key`. + +For an authenticated replay of a commitment attempt the seller has already +processed, with the same `idempotency_key` and the same commitment terms, the +seller MUST return the original result and MUST NOT create a second +commitment. The version and expiry checks apply to a new attempt, not to +returning a stored result. Reusing an `idempotency_key` with different +commitment terms MUST produce an explicit conflict and MUST NOT create or +modify a commitment. #### 5.6.2 hold_status @@ -369,7 +385,9 @@ buyer commitment, and `settable` bounds. See ### 6.3 CTV line item -See [`examples/line-item-ctv.yaml`](../examples/line-item-ctv.yaml). +Shows a buyer commitment naming the `proposal_id` and `version` it was priced +against (§ 5.6.1). See +[`examples/line-item-ctv.yaml`](../examples/line-item-ctv.yaml). ### 6.4 Audio line item From 74a25448baf9e38048a5bd417b4372d46faaa77f Mon Sep 17 00:00:00 2001 From: Kishore2572 Date: Thu, 22 Oct 2026 20:16:47 +0530 Subject: [PATCH 2/2] Scope the replay rule to the same buyer The specification does not define authentication yet (WS1; see #62), so the replay rule now applies to a replay from the same buyer rather than an authenticated replay. Refs #24 --- spec/openproposal-3.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/openproposal-3.0.md b/spec/openproposal-3.0.md index 717c234..62d37d5 100644 --- a/spec/openproposal-3.0.md +++ b/spec/openproposal-3.0.md @@ -307,9 +307,9 @@ current version of the proposal named by `proposal_id`, whose proposal is past MUST return the current version number, so the buyer can fetch it again. These checks MUST be deterministic, as for `idempotency_key`. -For an authenticated replay of a commitment attempt the seller has already -processed, with the same `idempotency_key` and the same commitment terms, the -seller MUST return the original result and MUST NOT create a second +For a replay from the same buyer of a commitment attempt the seller has +already processed, with the same `idempotency_key` and the same commitment +terms, the seller MUST return the original result and MUST NOT create a second commitment. The version and expiry checks apply to a new attempt, not to returning a stored result. Reusing an `idempotency_key` with different commitment terms MUST produce an explicit conflict and MUST NOT create or