Resolution Protocol (RP) — version 0.2, draft

Status: working draft, 2026-10-12. Not yet reviewed outside Resolution Co (backlog B-27). Changes from 0.1 are listed in Appendix C. Editors: Rafael David; drafted with Claude. Reference implementations:

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in BCP 14 (RFC 2119, RFC 8174), only when in capitals. Sections and notes marked informative are not requirements.


1. Purpose and principles

RP lets every person whose voice a recorder captures decide how much of their own words is kept, and for how long. It applies to pocket and wearable recorders, smart glasses, meeting software, phone systems and note-taking bots.

  1. Each voice owns its record. A voice gains an identity when first heard. Its speech is filed in its own record, which only it can raise.
  2. Lowest wins. Anyone may lower the level for themselves or for everyone. No one may raise anyone else's own request.
  3. Raising for others is provisional. A raise that affects voices other than the speaker's takes effect at once. For the objection window, anyone can undo it, and the undo reaches back over everything said since.
  4. Uncertainty falls toward less. When the recorder is unsure who spoke, a lowering applies to everyone, and a raise is ignored.
  5. Forgetting is scheduled and enforced. Content degrades in steps as it ages, and degraded content becomes unrecoverable.
  6. No network is required.

Non-goals:

2. Terms

Term Meaning
Recorder The system that captures speech and keeps records: a device, a service, or both together. Its boundary is what its profile declares (§8); every part inside it is bound by this spec.
Session One conversation. It starts with the first voice heard, and ends when the binding says so (a meeting ends, a call hangs up) or after session_gap of silence.
Voice A distinct speaker. Its identity can come from speaker separation (diarisation), a stream (one participant per stream), a roster or authenticated account, or a claim (§4.3).
Room Everyone in the session, across every endpoint (in person and remote). room scope means all of them.
Record One voice's segments in a session.
Segment A stretch of one voice's speech, with start, end and level.
Level One of the six values in §3.
Requested level What a voice asked for its own words. Absent means no preference.
Applied level What the recorder does with a voice's words now (§6.1).
Default The level applied to a voice with no request. It is set by the profile (the operator), not by anyone's consent.
Ceiling A room-wide lowering in force.
Derived artefact A transcript, summary, list of outcomes or similar, made from records.

3. Levels

A closed, totally ordered set. Each level keeps everything the levels below it keep. Implementations MUST NOT add, remove or redefine levels.

Value ID What may be kept
0 off Only that a gap occurred, and its length
1 outcomes Decisions and commitments, without saying who argued what
2 gist A summary of the reasoning, unattributed
3 attributed Who said what, paraphrased
4 verbatim The exact words
5 audio The recording itself

Informative: a commitment such as "Ana will send the deck" names its owner at level 1. That is the commitment, not attribution of the argument. A separate attribution flag ("verbatim but unnamed") may come in a later version.

4. Voices and records

4.1 Identity

4.2 Unlinkability and voice features

4.3 What a voice controls

A voice MAY, with immediate effect:

4.4 Transient processing

5. Retention

5.1 Schedule

5.2 Shortening

5.3 Enforcement

5.4 Derived artefacts

6. Rules

6.1 Applied level

applied(v) = min( requested(v) if present else default,  ceiling,  cap(v) )

6.2 Lowering, and a voice's own words

6.3 Raising for others

A raise for others is either:

Rules:

  1. Who may raise the room: a room raise MUST be addressed: the recorder's name was spoken, or the voice took an explicit non-voice action such as a button. A profile MAY set room_raise: disabled (RECOMMENDED for wearables, where bystanders cannot be expected to object).
  2. Limits: a room raise is capped at max_default_level.
  3. Resume needs no name. A resume returns to a level the room already had, so the recorder's name is not required.
    • A room raise or resume from speech MUST come from a confidently attributed voice inside the participant cap; otherwise it is ignored.
    • A room raise only sets the default. It never lifts a ceiling or cap; that is resume's job.
  4. Takes effect at once, provisionally: the recorder MUST indicate the provisional state for the whole objection_window.
  5. What undoes it: any objection (including one at the deadline itself), any room lowering, or the end of the session before the window closes MUST undo the raise.
    • The levels before the raise come back.
    • Every segment captured since the raise opened is restamped at the level that applied before it.
  6. A second raise: a raise during an open window restarts the window and keeps the original before-state.
  7. Crash safety: content captured during the window MUST be held so that a crash or reboot undoes it (fail closed). For example, keep it under a key that is destroyed on objection, end or boot, and promoted when the window closes.

6.4 Nobody raises anyone else

No intent raises another voice's requested level. A room raise changes the default, which affects only voices without a request, and it is still subject to every ceiling and cap.

A voice over the participant cap may still lower the room (§4.1 does not stop that; lowering is always safe).

6.5 Uncertain attribution

Each intent comes with an attribution confidence: 1 for a stream, roster or UI source; the separator's own estimate for speech. Below attribution_threshold:

6.6 Forgetting

forget with a span lowers to 0 every segment overlapping the last span seconds:

Speech filed after the forget, but overlapping its span, MUST also be stamped 0.

6.7 End of session

At the end of a session:

  1. An open raise is undone (§6.3).
  2. The default returns to default_level.
  3. Ceilings, caps and session limits are cleared.
  4. Identities are discarded, and their records are handed to the store.

forget cannot reach across a session boundary.

7. Intents and grammars

7.1 Intents

Kind Fields Scopes
lower level self, room
raise level self, room
forget seconds (span back from now; absent = all) self, room
shorten seconds (the limit; 0 = keep nothing) self, room
resume — self, room
object — room

Every intent carries:

Inputs beyond speech:

7.2 Locale grammars

A locale grammar (schemas/locale.schema.json) is data. It defines:

Normalisation. Lowercase, then the locale's replace pairs, then optional accent folding. Every run of characters that are not letters, digits or apostrophes becomes one space. The result is padded with a space at each end. Patterns match whole words with literal spaces.

Patterns:

Matching:

Locales:

8. Profiles

A profile (schemas/profile.schema.json) is one recorder's expression of the protocol: its parameters and its localisation. Required: id, default_level, locales, retention. Optional (defaults in the schema):

Field Default Meaning
max_default_level 4 Highest default, including after a room raise
max_voice_level 4 Highest level a voice can request for itself
invocation [] Names that address the recorder
objection_window PT30S §6.3
session_gap PT10M §2, §6.7
attribution_threshold 0.8 §6.5
max_participants 10 §4.1
room_raise invocation invocation or disabled (§6.3)
extensions [] Optional parts (Appendix A), or URI / reverse-domain names
reply_window, replies_for_group PT6S, 2 Side-conversation detection (Appendix A.1)
bindings — Device or service ports (§10.3); free-form

Constraints: default_level ≤ max_default_level ≤ max_voice_level; objection_window > 0; session_gap > objection_window; levels are integers 0–5; retention steps increase in age and decrease in level. vectors/profiles.json tests these.

Defaults and consent:

9. Events

The engine reports outcomes as events (schemas/event.schema.json). Each event has:

Type Meaning
voice A voice gained an identity, with its applied level
level Applied levels changed: levels maps voice ids (and * for the default) to their new levels
provisional A raise for others took effect and can be undone until until
confirmed An objection window closed with no objection
reverted A raise was undone
forget A span (t0–t1; t0 null for all) was forgotten, for one voice or all. A voice forgetting everything is logged as scope: self with no id.
claim Extension claiming: a voice asked to be recognised later
retention A retention limit was set or shortened
ignored An intent was ignored, with a reason
end The session ended

What events reveal:

Receipts:

10. Indication and bindings

10.1 Indication

10.2 Indication by medium

10.3 Bindings

The engine is the protocol. Everything around it is a binding the profile declares:

Binding What it supplies
audio in captured speech
speaker separation voice labels with confidence
recogniser speech to text
intent parser text to intents
store §5 enforcement
indicator §10.1–10.2
hand-off export
clock trusted time
session source start and end of a session
identity source roster, stream or authenticated identities

11. Exchange between recorders, and standing preferences

11.1 Lowerings exchange

Recorders in the same room, such as glasses, a phone and a meeting bot, MAY exchange their lowerings:

{"rp": "0.2", "type": "lowerings", "session": "…", "ceiling": 0, "retention": null,
 "voices": {"<shared label>": {"level": 1, "cap": null, "retention": 604800}}}

11.2 Standing preferences

A voice MAY carry a standing preference, {"level": n, "retention": seconds}, from an account setting, a claim or another system:

12. Conformance

Class Requirements Tested by
Engine §4.1, §6, §9, §11 vectors/sessions.json
Grammar (per locale) §7.2 vectors/phrases.<locale>.json + the rule-order lint
Retention §5.1–5.2 vectors/retention.json
Profile §8 vectors/profiles.json
Store §5.3–5.4, §6.3 point 7 implementation tests: decay, fail-closed clock, per-voice forget, held content
Indicator §10 inspection, and a cold-read test with strangers
Deployment §4.2, §4.4, §7.1 (language-independent lowering), §8 (consent), §9 (log retention, receipts) a written statement per profile, checked by the adopter's reviewer

13. Versioning and extensions

14. Security and privacy considerations


Appendix A. Extensions

A.1 side_conversations

The recorder may detect that the room has split into conversations: the reference implementations link voices that reply to each other quickly.

A.2 claiming

The claim intent ("remember my voice") asks the recorder to keep the features needed to recognise the voice in later sessions. It is an explicit, opt-in claim, usually biometric processing.

Appendix B. Mappings to other standards (informative)

Appendix C. Changes from 0.1

# Resolution Protocol (RP) — version 0.2, draft

**Status:** working draft, 2026-10-12. Not yet reviewed outside Resolution Co (backlog B-27). Changes from 0.1 are listed in Appendix C.
**Editors:** Rafael David; drafted with Claude.
**Reference implementations:**
- `python/resolution_protocol` and `js/resolution.js` pass every vector in `vectors/`;
- `tools/fuzz_parity.py` checks that they agree step by step on random sessions.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in BCP 14 (RFC 2119, RFC 8174), only when in capitals. Sections and notes marked *informative* are not requirements.

---

## 1. Purpose and principles

RP lets every person whose voice a recorder captures decide how much of **their own words** is kept, and for how long. It applies to pocket and wearable recorders, smart glasses, meeting software, phone systems and note-taking bots.

1. **Each voice owns its record.** A voice gains an identity when first heard. Its speech is filed in its own record, which only it can raise.
2. **Lowest wins.** Anyone may lower the level for themselves or for everyone. No one may raise anyone else's own request.
3. **Raising for others is provisional.** A raise that affects voices other than the speaker's takes effect at once. For the objection window, anyone can undo it, and the undo reaches back over everything said since.
4. **Uncertainty falls toward less.** When the recorder is unsure who spoke, a lowering applies to everyone, and a raise is ignored.
5. **Forgetting is scheduled and enforced.** Content degrades in steps as it ages, and degraded content becomes unrecoverable.
6. **No network is required.**

**Non-goals:**
- identifying people;
- deciding what is lawful anywhere;
- the content of summaries;
- recording required by law or regulation (for example financial call recording). That is out of scope. Such recorders MUST NOT claim RP conformance for those recordings.

## 2. Terms

| Term | Meaning |
|---|---|
| **Recorder** | The system that captures speech and keeps records: a device, a service, or both together. Its boundary is what its profile declares (§8); every part inside it is bound by this spec. |
| **Session** | One conversation. It starts with the first voice heard, and ends when the binding says so (a meeting ends, a call hangs up) or after `session_gap` of silence. |
| **Voice** | A distinct speaker. Its identity can come from speaker separation (diarisation), a stream (one participant per stream), a roster or authenticated account, or a claim (§4.3). |
| **Room** | Everyone in the session, across every endpoint (in person and remote). `room` scope means all of them. |
| **Record** | One voice's segments in a session. |
| **Segment** | A stretch of one voice's speech, with start, end and level. |
| **Level** | One of the six values in §3. |
| **Requested level** | What a voice asked for its own words. Absent means no preference. |
| **Applied level** | What the recorder does with a voice's words now (§6.1). |
| **Default** | The level applied to a voice with no request. It is set by the profile (the operator), not by anyone's consent. |
| **Ceiling** | A room-wide lowering in force. |
| **Derived artefact** | A transcript, summary, list of outcomes or similar, made from records. |

## 3. Levels

A closed, totally ordered set. Each level keeps everything the levels below it keep. Implementations MUST NOT add, remove or redefine levels.

| Value | ID | What may be kept |
|---|---|---|
| 0 | `off` | Only that a gap occurred, and its length |
| 1 | `outcomes` | Decisions and commitments, without saying who argued what |
| 2 | `gist` | A summary of the reasoning, unattributed |
| 3 | `attributed` | Who said what, paraphrased |
| 4 | `verbatim` | The exact words |
| 5 | `audio` | The recording itself |

*Informative:* a commitment such as "Ana will send the deck" names its owner at level 1. That is the commitment, not attribution of the argument. A separate attribution flag ("verbatim but unnamed") may come in a later version.

## 4. Voices and records

### 4.1 Identity

- On first hearing a voice in a session, the recorder MUST create a session-local pseudonymous identity and an empty record for it.
- The binding supplies a label for each voice: a diarisation cluster, stream or roster id. The engine's identities (`v1`, `v2`, …) MUST NOT reveal those labels in any output that leaves the recorder.
- If the profile caps voices (`max_participants`), speech from voices over the cap MUST be applied level 0, and is never recorded.

### 4.2 Unlinkability and voice features

- Identities from speaker separation MUST be unlinkable across sessions, unless claimed (§4.3).
- Voice features used to tell voices apart MUST stay inside the recorder, MUST NOT persist past the session, and MUST NOT be used for anything else.
- Roster or stream identities are linkable by nature. The recorder MUST keep them out of RP outputs: events, receipts and manifests use only the session-local identities.

### 4.3 What a voice controls

A voice MAY, with immediate effect:
- set its requested level (§6.2);
- forget a span of its own speech, or all of it in the session (`forget` without a span), with the effect of §5.3;
- shorten its record's retention (§5.2).

### 4.4 Transient processing

- The recorder MUST still listen at level 0, or it could not hear "back on the record".
- Speech above a voice's applied level MAY be processed only to:
  - detect intents;
  - display live captions where the profile declares them.
- It MUST NOT be stored, MUST NOT be sent to a third party, and MUST NOT be used to train models.

## 5. Retention

### 5.1 Schedule

- A profile's schedule is a list of steps `(until, max_level)`, with increasing ages and decreasing levels.
- A segment's **readable level** at age *a* is the lower of:
  - its stamped level;
  - the `max_level` of the first step whose `until` exceeds *a*.
- After the last step, the readable level is 0.

### 5.2 Shortening

- `shorten` sets a retention limit for the session (room scope) or for the voice's record (self scope).
- From the limit on, the readable level is 0.
- Limits only decrease. Nothing lengthens retention.

### 5.3 Enforcement

- Content whose readable level has fallen MUST become unrecoverable at the higher level: deleted, or made unreadable by destroying its keys.
- Every copy the recorder controls counts, including backups and replicas. The profile MUST state how long those lag.
- If time cannot be trusted, the recorder MUST act as if the longest step has passed (fail closed).
- Cryptographic erase (NIST SP 800-88r2) is RECOMMENDED. Where it is used:
  - content MUST NOT be stored in plaintext at rest;
  - keys MUST NOT be escrowed.

### 5.4 Derived artefacts

- An artefact at level *L* MUST draw only on segments whose readable level is at least *L*. Each voice's contribution is therefore bounded by that voice's own level.
- While a raise is provisional (§6.3), artefacts MUST use the level that applied before it.
- An exported artefact MUST state its level and expiry.
- If a later `forget`, lowering or shortening affects content in an artefact the recorder still controls, the recorder MUST update or withdraw it. It SHOULD tell the holders it knows about.

## 6. Rules

### 6.1 Applied level

```
applied(v) = min( requested(v) if present else default,  ceiling,  cap(v) )
```

- `cap(v)` is a per-voice lowering by others. It is used by the side-conversations extension (Appendix A.1); without it, `cap(v)` is 5.
- Each segment MUST be stamped with the lowest applied level in force at any time during it. A segment filed late counts the same: undone raises (§6.3) and forgets (§6.6) reach it.
- Speech from before the current session's start MUST NOT be recorded. Bindings should file a session's speech before ending it.
- After every change, the recorder MUST report the new applied levels (§9, §10).

### 6.2 Lowering, and a voice's own words

- **Room lowering:** anyone MAY lower the level for the room. The recorder MUST apply it immediately, with no name and no confirmation: `ceiling = min(ceiling, level)`.
- **Own words:** a voice MAY set its requested level at once.
  - A lowering takes the minimum of the current and requested values; it never raises.
  - A raise takes the maximum, bounded by `max_voice_level`.
  - `resume` in self scope clears the request.

### 6.3 Raising for others

A raise for others is either:
- a room raise (`raise`, room scope), which sets the default; or
- a room resume (`resume`, room scope), which clears a ceiling.

Rules:
1. **Who may raise the room:** a room raise MUST be addressed: the recorder's name was spoken, or the voice took an explicit non-voice action such as a button. A profile MAY set `room_raise: disabled` (RECOMMENDED for wearables, where bystanders cannot be expected to object).
2. **Limits:** a room raise is capped at `max_default_level`.
3. **Resume needs no name.** A resume returns to a level the room already had, so the recorder's name is not required.
   - A room raise or resume from speech MUST come from a confidently attributed voice inside the participant cap; otherwise it is ignored.
   - A room raise only sets the default. It never lifts a ceiling or cap; that is resume's job.
4. **Takes effect at once, provisionally:** the recorder MUST indicate the provisional state for the whole `objection_window`.
5. **What undoes it:** any objection (including one at the deadline itself), any room lowering, or the end of the session before the window closes MUST undo the raise.
   - The levels before the raise come back.
   - Every segment captured since the raise opened is restamped at the level that applied before it.
6. **A second raise:** a raise during an open window restarts the window and keeps the original before-state.
7. **Crash safety:** content captured during the window MUST be held so that a crash or reboot undoes it (fail closed). For example, keep it under a key that is destroyed on objection, end or boot, and promoted when the window closes.

### 6.4 Nobody raises anyone else

No intent raises another voice's requested level. A room raise changes the default, which affects only voices without a request, and it is still subject to every ceiling and cap.

A voice over the participant cap may still lower the room (§4.1 does not stop that; lowering is always safe).

### 6.5 Uncertain attribution

Each intent comes with an attribution confidence: 1 for a stream, roster or UI source; the separator's own estimate for speech. Below `attribution_threshold`:
- a self-scoped lowering, forget or shorten MUST apply to the room instead;
- a self-scoped raise or resume MUST be ignored and reported.

### 6.6 Forgetting

`forget` with a span lowers to 0 every segment overlapping the last *span* seconds:
- the speaker's own segments, in self scope;
- every voice's segments, in room scope.

Speech filed after the forget, but overlapping its span, MUST also be stamped 0.

### 6.7 End of session

At the end of a session:
1. An open raise is undone (§6.3).
2. The default returns to `default_level`.
3. Ceilings, caps and session limits are cleared.
4. Identities are discarded, and their records are handed to the store.

`forget` cannot reach across a session boundary.

## 7. Intents and grammars

### 7.1 Intents

| Kind | Fields | Scopes |
|---|---|---|
| `lower` | `level` | self, room |
| `raise` | `level` | self, room |
| `forget` | `seconds` (span back from now; absent = all) | self, room |
| `shorten` | `seconds` (the limit; 0 = keep nothing) | self, room |
| `resume` | — | self, room |
| `object` | — | room |

Every intent carries:
- `addressed`: the recorder was named, or an explicit action was taken;
- `source`: `speech`, `ui`, `dtmf`, `sdp`, `preference` or `exchange`.

**Inputs beyond speech:**
- Intents MAY come from any source.
- Every profile MUST offer at least one language-independent way to lower to 0, such as a button, a tap or a keypad tone.
- Conformance to the phrase vectors applies only to the speech source.

### 7.2 Locale grammars

A locale grammar (`schemas/locale.schema.json`) is data. It defines:
- normalisation;
- number words and duration units;
- **self markers**: first-person phrases;
- ordered rules, each a pattern and the intent it yields.

**Normalisation.** Lowercase, then the locale's `replace` pairs, then optional accent folding. Every run of characters that are not letters, digits or apostrophes becomes one space. The result is padded with a space at each end. Patterns match whole words with literal spaces.

**Patterns:**
- Patterns MUST NOT use `\b`, `\w`, `\W` or `\d` (they differ between regex dialects), and MUST NOT contain capturing groups.
- Three placeholders expand at run time: `{count}`, `{unit}` (from the locale) and `{invocation}` (from the profile).

**Matching:**
- The first matching rule wins, and an utterance yields at most one intent. Locales are tried in profile order.
- **Order rules by protection.** Rules that lower (`object`, `forget`, `lower`, `shorten`) MUST come before rules that raise, and a room-wide rule MUST come before a personal rule of a higher level. A sentence that both raises and lowers therefore lowers. The `lint` in the reference implementation checks the first rule.
- Each rule has a fixed scope. A first-person phrase elsewhere in the sentence never makes a room lowering personal.
- `addressed` is true if the invocation name appears anywhere in the utterance. A binding MAY require it at the start.

**Locales:**
- Locales are BCP 47 tags with truncation fallback (`es-MX` → `es`), as in CLDR.
- A recorder MAY use any recogniser, including a language model, instead of the grammar. It MUST still pass the locale's phrase vectors.

## 8. Profiles

A profile (`schemas/profile.schema.json`) is one recorder's expression of the protocol: its parameters and its localisation. **Required:** `id`, `default_level`, `locales`, `retention`. **Optional** (defaults in the schema):

| Field | Default | Meaning |
|---|---|---|
| `max_default_level` | 4 | Highest default, including after a room raise |
| `max_voice_level` | 4 | Highest level a voice can request for itself |
| `invocation` | [] | Names that address the recorder |
| `objection_window` | PT30S | §6.3 |
| `session_gap` | PT10M | §2, §6.7 |
| `attribution_threshold` | 0.8 | §6.5 |
| `max_participants` | 10 | §4.1 |
| `room_raise` | invocation | `invocation` or `disabled` (§6.3) |
| `extensions` | [] | Optional parts (Appendix A), or URI / reverse-domain names |
| `reply_window`, `replies_for_group` | PT6S, 2 | Side-conversation detection (Appendix A.1) |
| `bindings` | — | Device or service ports (§10.3); free-form |

**Constraints:** `default_level` ≤ `max_default_level` ≤ `max_voice_level`; `objection_window` > 0; `session_gap` > `objection_window`; levels are integers 0–5; retention steps increase in age and decrease in level. `vectors/profiles.json` tests these.

**Defaults and consent:**
- A `default_level` above 0 is the operator's choice, made under the operator's legal basis. It MUST NOT be represented as anyone's consent.
- A profile MUST NOT treat any class of participant (hardware rooms, dial-in, bystanders) as having consented.
- `default_level` 0 ("silent until someone speaks up") is RECOMMENDED for:
  - telephony and wearables;
  - any place where every party must consent before recording.

## 9. Events

The engine reports outcomes as events (`schemas/event.schema.json`). Each event has:
- `session`: a random id, new per session;
- `seq`: increasing within the session;
- `t`: seconds on the recorder's clock;
- `type`;
- fields that depend on the type.

| Type | Meaning |
|---|---|
| `voice` | A voice gained an identity, with its applied `level` |
| `level` | Applied levels changed: `levels` maps voice ids (and `*` for the default) to their new levels |
| `provisional` | A raise for others took effect and can be undone until `until` |
| `confirmed` | An objection window closed with no objection |
| `reverted` | A raise was undone |
| `forget` | A span (`t0`–`t1`; `t0` null for all) was forgotten, for one voice or all. A voice forgetting everything is logged as `scope: self` with no id. |
| `claim` | Extension `claiming`: a voice asked to be recognised later |
| `retention` | A retention limit was set or shortened |
| `ignored` | An intent was ignored, with a `reason` |
| `end` | The session ended |

**What events reveal:**
- Events never contain speech content.
- They do reveal that a pseudonymous voice used its rights, and when. The event log is therefore personal data.
- The log MUST have a retention no longer than the profile's last step.
- A voice's `forget` of all its speech MUST remove its id from the log: its events keep only their types and times, and level maps drop it.

**Receipts:**
- A recorder SHOULD be able to give each voice a human-readable receipt of its own record, in its language. The receipt covers levels over time, limits and forgets.
- Refusals and lowerings MUST be included (as IEEE 7012-2025 §5.2.4 does for rejected agreements).

## 10. Indication and bindings

### 10.1 Indication

- **Capture:** the recorder MUST make capture perceptible to everyone the profile expects to be in earshot, not only to its owner.
- **Applied levels:** it SHOULD let each voice perceive its own applied level.
- **Provisional raises:** it MUST indicate a provisional raise for its whole window.

### 10.2 Indication by medium

- **Visual:** every change MUST stay visible for at least 3 seconds. This is adapted from the W3C Media Capture privacy-indicator rule, which covers only the off-to-on transition.
- **Audio:** announce at the start of capture and on every change of the indicated state.
- **Haptic:** MAY accompany either. A non-visual signal SHOULD be offered for people who cannot see the indicator.

### 10.3 Bindings

The engine is the protocol. Everything around it is a binding the profile declares:

| Binding | What it supplies |
|---|---|
| audio in | captured speech |
| speaker separation | voice labels with confidence |
| recogniser | speech to text |
| intent parser | text to intents |
| store | §5 enforcement |
| indicator | §10.1–10.2 |
| hand-off | export |
| clock | trusted time |
| session source | start and end of a session |
| identity source | roster, stream or authenticated identities |

## 11. Exchange between recorders, and standing preferences

### 11.1 Lowerings exchange

Recorders in the same room, such as glasses, a phone and a meeting bot, MAY exchange their lowerings:

```json
{"rp": "0.2", "type": "lowerings", "session": "…", "ceiling": 0, "retention": null,
 "voices": {"<shared label>": {"level": 1, "cap": null, "retention": 604800}}}
```

- **Merging:** a receiver merges by taking the minimum, as a lowering from source `exchange`. Merging is idempotent and order-independent, and creates identities without counting as speech (no reply links, no clock).
- **Validation:** a receiver MUST ignore a message whose `type` is not `lowerings` or whose major version differs, and any member that is not a level 0–5 or a duration ≥ 0.
- **Raises:** raises MUST NOT be exchanged.
- **Labels:** only labels the recorders genuinely share, such as roster or account ids, may be exchanged. Diarisation labels MUST NOT be; they are meaningless elsewhere and would leak (§4.2).

### 11.2 Standing preferences

A voice MAY carry a standing preference, `{"level": n, "retention": seconds}`, from an account setting, a claim or another system:
- Its lowerings apply as soon as the voice is identified, compared with the voice's own request (not with a ceiling in force).
- Its raise applies only when the binding has authenticated who the voice is: a logged-in account, or a claimed voice re-identified by its features.
- IEEE 7012 "first-party terms" can carry it.

## 12. Conformance

| Class | Requirements | Tested by |
|---|---|---|
| Engine | §4.1, §6, §9, §11 | `vectors/sessions.json` |
| Grammar (per locale) | §7.2 | `vectors/phrases.<locale>.json` + the rule-order lint |
| Retention | §5.1–5.2 | `vectors/retention.json` |
| Profile | §8 | `vectors/profiles.json` |
| Store | §5.3–5.4, §6.3 point 7 | implementation tests: decay, fail-closed clock, per-voice forget, held content |
| Indicator | §10 | inspection, and a cold-read test with strangers |
| Deployment | §4.2, §4.4, §7.1 (language-independent lowering), §8 (consent), §9 (log retention, receipts) | a written statement per profile, checked by the adopter's reviewer |

- A recorder claims conformance per class, naming the RP version, the profile and any extensions. For example: "RP 0.2 Engine + Grammar(en) + Retention, profile goldfish-v01, extensions: side_conversations".
- The vector runners are `python -m resolution_protocol.conformance` and `node js/conformance.js`.

## 13. Versioning and extensions

- `rp` is a version string: `major.minor`. Within a major version, readers MUST ignore members they do not understand.
- The levels are closed (§3).
- Intent kinds, event types, `ignored` reasons, extensions and binding types are open. A new value is named by a URI or reverse-domain name until a registry exists. RP 1.0 will open registries for each.
- An extension MUST NOT weaken a rule in §6. It may only add ways to lower, or narrow a lowering's scope under the conditions it defines.

## 14. Security and privacy considerations

- **Lowering is not an attack.** Anyone can lower by design.
- **Raises:** a raise cannot touch another voice's request. It needs addressing, and stays provisional for the window.
- **Speaker separation is safety-relevant.** Misattribution moves words between records. Implementers SHOULD measure and publish their attribution error rate.
- **Biometric data:** voice features are biometric data in many jurisdictions (GDPR Art. 9 per EDPB Guidelines 02/2021; Illinois BIPA). §4.2 keeps them session-local.
- **Emotion inference:** profiles MUST NOT infer emotion from voice for derived artefacts.

---

## Appendix A. Extensions

### A.1 `side_conversations`

The recorder may detect that the room has split into conversations: the reference implementations link voices that reply to each other quickly.
- **Narrowing a lowering:** a room lowering by a confidently attributed voice MAY then lower only that conversation. It does so by setting `cap(v) = min(cap(v), level)` on each member at that moment.
- **Merges:** caps are per voice, so a later merge of conversations cannot lift them.
- **Resume:** a room resume by a member lifts those caps, provisionally (§6.3).
- **When to use it:** narrowing is a MAY, and only above the recorder's own group-detection confidence. Profiles SHOULD leave the extension off unless they need it.

### A.2 `claiming`

The `claim` intent ("remember my voice") asks the recorder to keep the features needed to recognise the voice in later sessions. It is an explicit, opt-in claim, usually biometric processing.
- **What the engine does:** it only reports the claim.
- **What the binding must do:**
  - make it opt-in by the voice itself;
  - let a `forget` of everything withdraw it;
  - apply the voice's standing preference only after re-identifying it (§11.2).

## Appendix B. Mappings to other standards (informative)

- **SIPREC** (RFC 6341, 7245, 7865, 7866):
  - **Where it runs:** the engine runs where the store runs (the recording server); the recording client relays indication.
  - **Indication out:**
    - room level 0 maps to session-level `a=record:paused`;
    - a voice at 0 maps to `a=record:paused` on that participant's media stream;
    - levels 1–5 map to `on`.
  - **Requests in:** an inbound `a=recordpref:off` or `pause` from a participant is a self `lower` to 0 from source `sdp`; `on` is a self `raise`.
  - **Metadata:** per-voice levels belong in RFC 7865 participant metadata, as an extension element still to be defined.
  - **Departure:** SIPREC lets the recorder ignore preferences. RP does not.
- **ODRL 2.2:**
  - "Lowest wins" corresponds to the `prohibit` conflict strategy, if each level *L* is modelled as prohibitions on actions above *L*.
  - Retention steps are Duties: "derive the lower level, then delete the higher one, when `elapsedTime` ≥ until".
- **W3C DPV:**
  - Consent states:
    - a voice's own lowering is `ConsentWithdrawn` for the levels above it;
    - a voice's own raise is `ConsentGiven`;
    - a lowering by another voice is `ConsentRevoked` (by another party).
  - Processing terms:
    - `forget` is `Erase`;
    - `shorten` sets a `StorageDuration`.
  - **What is not consent:** the default and a room raise are not consent. Record the operator's legal basis instead. An ignored, uncertain intent leaves the prior state unchanged.
- **ISO/IEC TS 27560:**
  - A session's events can fill a 27560-style consent record, once a controller and a purpose are added in a header.
  - The text was not available to the editors (paywalled); unverified.
- **C2PA 2.2:**
  - A derived artefact MAY carry an external manifest with an assertion `org.resolutionprotocol.levels`. It should hold aggregates only: the profile, the levels used, the schedule, and no per-voice data.
  - Lowerings after capture map to `c2pa.redacted`.
  - This may contribute to machine-readable marking of generated text.
- **Prior art in general:** `research/prior-art.md`. It covers Zoom, Teams, Meet, IEEE 7012, GPC, NIST SP 800-88r2, and the data-degradation work of Anciaux et al. (2008).

## Appendix C. Changes from 0.1

- **Raises for others are now provisional:** they take effect at once and can be undone retroactively. In 0.1 they were pending, which made "back on the record" look broken.
- **Room raises:** capped at `max_default_level`, and can be disabled.
- **Side conversations are an extension:** caps are per voice, so a merge of conversations can no longer lift a lowering (0.1 bug).
- **Fewer intents:** `erase` became `forget` without a span, and `claim` became an extension.
- **Grammar:**
  - space-delimited normalisation instead of `\b`, `\w` and `\d` (Python and JavaScript differed on non-ASCII text);
  - `{count}` and `{unit}` placeholders;
  - first match wins;
  - self markers only inside the match. "As I said, this is off the record" was personal in 0.1.
- **Segments:** stamped with the lowest level over their span, and clamped by earlier forgets.
- **Session end** undoes an open raise, and speech from before a session is never filed into it. Voices over the cap are applied level 0.
- **Round-2 fixes:** undone raises reach late-filed speech; the clock never moves back; a raise and a lowering in one sentence lowers; unsure or over-cap voices cannot raise the room; a room raise no longer lifts ceilings; exchange input is validated and never fakes side conversations; standing lowerings compare with the voice's own request; forgetting everything removes the voice from the log; an objection at the deadline counts.
- **Profile:** durations are named without the `_s` suffix; required fields have no defaults; `room_raise` and `extensions` were added; `raise_room_requires_invocation` and `speaking_window` were removed (the latter moved to the indicator binding).
- **Events:** session id, seq, level maps, and the new types `provisional`, `confirmed` and `reverted`.
- **New sections:** identity sources, transient processing, session boundaries from bindings, exchange and standing preferences, versioning, and the non-goal of regulated recording.
- **Standards mappings** moved to an informative appendix, with corrections (SIPREC direction, DPV categories).