# Part XVIII. Review and issue

*This part is proposed for 0.2. It comes from plan 688, "One review flow", which the steward decided on 25 October 2026: the reference implementation and a host share one review flow, written into the standard, on which contracts will later rest. It is not part of OpenEik 0.1, and no 0.1 conformance class requires it. A 0.1 reader keeps its files as kept files (Part III, 7) while they are UTF-8 text of at most 1 MiB, and leaves larger ones alone. It builds on Part XIV, proposed for 0.2 too: its registry of people and keys (XIV.5), the codes of its drawing profile (XIV.4), its issues (XIV.12) and its signed statements (XIV.14). The steward decided its questions, open questions 61 to 68, on 26 October 2026, each as this part proposed it (section 10). `@openeik/tools` checks it when asked: `checkFolder(…, { proposals: ["review"] })`, or `openeik check --proposal review`, which apply the proposal `drawings` too (section 9).*

## 1. Status and scope

Plan 688 states five rules, which this part and Part XIV keep:

- **The building is the truth.** What is reviewed, issued and signed is a version of the model, or a revision drawn from one, named by its digests (SHA-256).
- **A drawing is reviewed as it was issued.** A round on a drawing is on one revision, its issued SVG and its records (Part XIV, 13), never on a print of it.
- **A print is paper.** A PDF printed from a revision and an IFC exported from a version are copies: never the record, and never compared (section 7).
- **A decision has a name.** Only a signed-in person, a person of the registry (Part XIV, 5), gives an item, a decision or a signature. A link without sign-in looks and prints, nothing more (section 8, rule 1).
- **The authoring tool keeps the record.** It saves into the building what a host offers: closed rounds, statements, receipts and acceptances. A host coordinates, and never writes the building.

Part XIV says how a drawing is issued, reviewed and signed. This part says what that leaves out:

- a review round on a version of the model itself, in 3D (section 2);
- an issue that releases a version, and names the prints sent with it (section 3);
- a signed issue: the sender's statement, and a recipient's (section 4);
- a host's receipt of an issue, signed with a key the host publishes, and the file in which it publishes its keys (section 5);
- a record that a person accepted named terms (section 6);
- prints that say they are prints: an IFC that names the version it came from (section 7), and the PDF rendition's margin note (Part XIV, 15.3);
- the shapes of review items (Part XIV, 13.2; open question 61).

**Status.** The rules of this part bind an implementation that implements the proposal `review`, and no other. They are numbered, and cited as `XVIII.<section>.<rule>`: XVIII.4.3.2 is section 4.3, rule 2. A validator that implements the proposal reports a breach of a MUST as an error and a breach of a SHOULD as a warning (section 9). The proposal's schemas are in `schemas/proposed/review/`, and its conformance cases in `conformance/proposed/review/`. An implementation of the proposal implements, of Part XIV, at least its drawing profile (4), its registry (5), its issues (12) and its statements and their verification (14.1 to 14.3).

### 1.1 What it leaves out

- **Delivery.** How a host sends an issue, its links and how long they live, its notifications and its seen ticks are the host's (Part XIV, 1.2). A receipt records what the host saw, not how it delivered.
- **Accounts and sign-in.** How a person signs in, and which account is which person, are the host's. The building names people by the registry's ids (Part XIV, 5).
- **The terms' text,** which is the project's or the host's: this part names terms by their digest.
- **Law.** Whether a signature, a receipt or an acceptance binds anyone is the law's to say, as Part XIV, 1.2 says of signatures. The legal tier waits for a legal review (Part XIV, 14.6).
- **A 2D extension of BCF,** as Part XIV, 18 says.

### 1.2 Words

| Word | Meaning |
|---|---|
| model review | a review round on one version of the model, in 3D: items on its elements, each seen from a viewpoint |
| viewpoint | where an item is seen from: a camera, and a section where there is one |
| print | a rendition made for people to read or for another tool, a PDF of a revision or an IFC of a version: a copy, never the record |
| statement | a signed in-toto statement in a DSSE envelope, time-stamped, as Part XIV, 14 makes a sign-off |
| receipt | a host's signed record of what happened to an issue for one recipient |
| terms accepted | a signed record that a person accepted named terms, for the building or for an issue |

### 1.3 Where its files live

| File | Path | Kind | Section |
|---|---|---|---|
| a model review | `project/reviews/<review>.json` | a file of this part | 2 |
| what an issue releases beside revisions | in `drawings/issues/<issue>.json` | members of Part XIV's issue | 3 |
| an issue's statements | `drawings/issues/<issue>/signoffs/<n>.json` | a statement | 4 |
| an issue's receipts | `drawings/issues/<issue>/receipts/<n>.json` | a statement | 5 |
| terms accepted | `project/terms-accepted/<n>.json` | a statement | 6 |
| the terms' text, where the folder keeps it | `assets/<sha256>.pdf` | an asset (Part XV) | 6 |

Every JSON file of this part is a drawing file as Part XIV, 7.2 says: it has a `format`, is in the stored form, writes times in UTC, names people by the registry's ids and is complete without a host. The file of a host's keys (5.6) is the one file of this part that no building holds: a host serves it on its own origin. A statement is a file of Part XIV, 14.2's format, `openeik-drawing-signoff/0.1`, which its predicate type tells apart from a drawing's sign-off. `<review>` and `<issue>` are path segments of ids, and `<n>` is `s-` and the first 12 hexadecimal digits of the envelope's hash, as Part XIV, 14.2 makes it.

### 1.4 How it fits into 0.1 and Part XIV

A 0.1 reader keeps every file of this part as a kept file while it is UTF-8 text of at most 1 MiB, and leaves a larger one alone (Part III, 7). An implementation of the proposal `drawings` alone reads the members this part adds to an issue as Part XIV, 12.1 lists them, and checks them only with this proposal.

*Note (informative).* `project` and `drawings` are folder names that 0.1 gives (Part I, 11.1, rule 2). A writer that implements this proposal writes the standard's own files, proposed for 0.2, into them, as Part XIII's note says of `project/circuits/`.

### 1.5 At 0.2 this text moves into

- wherever Part XIV goes at 0.2: model reviews beside drawing reviews, what an issue releases, statements, receipts and terms accepted;
- Part VI: an issued version, which the history keeps (3.2, rule 3);
- Part VIII: an IFC that names its version (7.2);
- Conformance: the classes' new duties (section 9);
- Security and privacy: receipts and acceptances as personal data, a host's keys, and what a link without sign-in may do (section 8).

## 2. Reviewing the model

A model review is a round on one version of the model, as a review of Part XIV, 13 is a round on one revision: the same reviewers, item kinds, states, decisions, replies and close, so that a reviewer who knows one knows the other. Its items sit on elements of the version, each seen from a viewpoint in 3D, and the version is named by its root tree hash, which no later change alters. A host keeps a round and its working state while it is open; at the close it offers the round in this file, and the authoring tool saves it into the building with the next version it records, so that the building holds the conclusion.

### 2.1 The file

`project/reviews/<review>.json`, held to [`schemas/proposed/review/model-review.schema.json`](https://openeik.org/schemas/0.1/proposed/review/model-review.schema.json):

| Member | | Type | Meaning |
|---|---|---|---|
| `format` | required | `"openeik-model-review/0.1"` | |
| `id` | required | Part I, 6 | unique without coordination across hosts and forks, such as a ULID, and never a host's sequence number; the file's name |
| `model` | required | `{version, root, ref?}` | the version reviewed: its id, as Part VI, 8.2's `computedFrom.version` names one (`null` for a folder under no version control); its root tree hash (Part VI, 2, rule 4), which is what the round reviews; and the line of work it is on |
| `issue` | optional | `{id, hash}` | the issue that released the version (section 3), by its id and its record's hash, where the round reviews an issued version |
| `compared` | optional | `{version, root}` | the version the round compared it with |
| `profile` | required | `{id, code?, purpose?}` | the drawing profile (Part XIV, 4), and the status or purpose code the round reviewed for |
| `opened` | required | `{by, at}` | |
| `due` | optional | date | |
| `closed` | optional | `{by, at}` | absent while the round is open |
| `reviewers` | required | list | `{person, role, decision?}`, as Part XIV, 13.1 has them |
| `items` | required | list (2.2) | |

### 2.2 Items

An item has these members of Part XIV, 13.2, as that section has them: `id`, `kind` (`comment`, `cloud`, `arrow`, `text`, `measure` or `highlight`), `author`, `at`, `text`, `replies`, `state`, `history`, `carriedFrom`, `bcf` and `attachments`. It has no `change` or `anchorState`, which are a revision's. In place of a sheet's anchors, its fallback and its shape it has:

| Member | | Meaning |
|---|---|---|
| `anchors` | required, and may be empty | tried in order: `{element, hash?, point?}`: an element of the model at the reviewed version, by its id; the hash of its record there (Part III, 6), so that a later reader sees whether it has changed since without the history; and a point on it, `[x, y, z]` in mm of the model |
| `viewpoint` | required | where the item is seen from (2.3) |
| `shape` | optional | `{points, closed?, form?}`, each point `[x, y, z]` in mm of the model: a cloud's, an arrow's, a highlight's or a measure's geometry, or the frame of a text's or a comment's words, its `form` as Part XIV, 13.2 says. A measure's value is derived: the length along its points |

A highlight marks its anchors' elements, and its shape where it has one. `carriedFrom` may name an item of a model review or of a drawing's review (Part XIV, 13): a point raised on a drawing can be carried to the model, and back.

Seen ticks, read receipts and each person's progress are the host's, and stay out of the file, as they do of Part XIV's.

### 2.3 Viewpoints

`viewpoint` is `{camera, section?}`, in the model's coordinates: millimetres, Z up (Part I, 4).

| Member | | Meaning |
|---|---|---|
| `camera.projection` | required | `perspective` or `orthographic` |
| `camera.position` | required | `[x, y, z]`: where the camera stands |
| `camera.direction` | required | `[x, y, z]`: where it looks; a direction only, which a reader uses as a unit vector |
| `camera.up` | required | `[x, y, z]`: which way is up in the picture |
| `camera.fieldOfView` | for `perspective` | the vertical field of view, in degrees, more than 0 and less than 180 |
| `camera.height` | for `orthographic` | the height of what it shows, in mm of the model, more than 0 |
| `camera.aspect` | optional | the picture's width over its height, as the reviewer saw it |
| `section` | optional | `{planes}` or `{box}`: what the viewpoint cuts away |
| `section.planes` | | 1 to 6 `{point, normal}`: each hides what lies on the side its normal points to, as BCF's clipping planes do |
| `section.box` | | `{min, max}`: shows only what lies inside the box, whose sides run along the model's axes. A box turned in plan is six planes |

What else a viewer showed (elements hidden, coloured or isolated, the path of a walk) is the viewer's, and stays out of the file.

### 2.4 Example

A round on the version of the L-house from which Part XIV's A-101 P02 was drawn, with one comment on the window `study-window-east`, seen from outside the east facade with the roof cut away above the wall heads (the camera's numbers are illustrative):

```json
{
  "format": "openeik-model-review/0.1",
  "id": "mr-01jcq7d2v9",
  "items": [
    {
      "anchors": [
        {"element": "study-window-east", "hash": "21df22d8acaa1d8d85a9be9238cf35e85a152cd808170c3b73b767dc5091b4eb", "point": [12000, 4000, 1650]}
      ],
      "at": "2026-10-26T09:12:00Z",
      "author": {"person": "person-r4t8wd"},
      "id": "i-1",
      "kind": "comment",
      "state": "new",
      "text": "Check the sill height after the move.",
      "viewpoint": {
        "camera": {"direction": [-1, 0, 0], "fieldOfView": 60, "position": [18000, 4000, 1650], "projection": "perspective", "up": [0, 0, 1]},
        "section": {"box": {"max": [13000, 7000, 2500], "min": [-1000, -1000, -500]}}
      }
    }
  ],
  "model": {"ref": "main", "root": "6b572fb1e20adea6f4d012a31588f32aef0dc7995796d2630497fb3f74e1f378", "version": null},
  "opened": {"at": "2026-10-26T09:00:00Z", "by": "person-k2m9xa"},
  "profile": {"code": "S3", "id": "example"},
  "reviewers": [{"person": "person-r4t8wd", "role": "checker"}]
}
```

### 2.5 Rules

1. The file MUST validate against its schema, and its `id` MUST be its name read back.
2. A folder that holds a model review MUST hold the drawing profile (Part XIV, 4). `profile.id` MUST be the profile's; every item's `state` and every state of its `history` MUST be a code of its `reviewStates`; every `decision.code` MUST be a code of its `decisions`.
3. Every reviewer MUST be a person of the registry of the kind `person`: a decision is a person's, and an agent gives none (Part XIV, 3.2, the AI rule).
4. An item's anchors SHOULD name elements of the model at the reviewed version, and an anchor's `hash`, where given, MUST be the hash of that element's record there. A validator checks this rule where it has that version.
5. A closed review MUST be frozen: no item, reply, state or decision is later than `closed.at`, and a later version holds the file unchanged.
6. `carriedFrom` SHOULD name an item of another review, a model review or a drawing's, where the folder holds it.
7. A camera's `direction` and `up` MUST NOT be zero, nor parallel to each other; a `perspective` camera MUST give `fieldOfView` and no `height`, and an `orthographic` one `height` and no `fieldOfView`. The schema states the second part.
8. A section's plane MUST have a `normal` that is not zero, and its box a `min` below its `max` on each axis.
9. A shape's `form` MUST be as Part XIV, 13.4, rule 9 says, its points in mm of the model.
10. `issue`, where given, MUST name an issue of the folder by its record's hash, and that issue MUST release the reviewed version: its `versions` hold `model.root`.
11. The people it names MUST follow Part XIV, 5.2, rules 2 and 3.

### 2.6 BCF (informative)

A model review exchanges as BCF 3.0, whose viewpoints are 3D, more directly than a drawing's review does (Part XIV, 18). Each item is a topic: its GUID the item's `bcf`, or one made from the review's id and the item's; its type the item's kind; its status the profile's review state; its replies its comments. Its viewpoint is the item's: the camera's position, direction and up, in metres, as a `PerspectiveCamera` with the field of view or an `OrthogonalCamera` whose `ViewToWorldScale` is the height in metres, and `AspectRatio` the item's aspect, or 1; each plane of its section as a `ClippingPlane`, and a box as six; its shape as `Lines`, one for each two points that follow each other, and one from the last to the first where it is closed; and its anchors' elements selected, by their OpenEik ids as `AuthoringToolId`. A bridge that derives GlobalIds from the project's id and the record's id (Part VIII, 1, rule 3) can give each its `IfcGuid` too, so that the topic opens on the IFC print of the same version. The round's version is named in each topic's document reference, by its root.

## 3. Issuing a version

An issue (Part XIV, 12) releases revisions to recipients for a purpose. Under this proposal it may also release a version of the model, which the recipients then review in 3D (section 2), and it names the prints sent with it.

### 3.1 The members

An issue MAY give, beside the members of Part XIV, 12.1:

| Member | | Type | Meaning |
|---|---|---|---|
| `versions` | optional | list, 1 to 20 | `{version, root, ref?}`: the versions released, each by its root tree hash (Part VI, 2, rule 4), its id as Part VI, 8.2 names a version (`null` for a folder under no version control), and its line of work. Two lines of one model, a design and its alternative, can be released together |
| `renditions` | optional | list, at most 10 000 | the prints sent with it: `{of, format, sha256, bytes, by}`. `of` is what it was printed from: `{root}`, a version the issue releases, or `{drawing, sequence}`, a revision it releases; `format`, its kind by its file's extension (`pdf`, `ifc`); `sha256` and `bytes`, the SHA-256 and the length of the file sent; and `by`, `{name, version}`, the bridge or renderer that made it |
| `terms` | optional | list, at most 20 | `{sha256, title, version?}`: the terms the issue is sent under, by the SHA-256 of their text, with their title and version as people know them (section 6) |

An issue then releases revisions, versions or both: Part XIV, 12.1's `revisions` is required unless `versions` is given.

A print the issue names is named by its digest, and kept nowhere: the digest says which file was sent, and the print is not the record of what was issued (section 7). The folder keeps no IFC, and a PDF only where Part XIV, 15 keeps a signed rendition.

### 3.2 Rules

1. Each version an issue releases MUST be listed once, by its `root`.
2. A rendition's `of` MUST name a version or a revision that the issue releases.
3. An issuer SHOULD record a named version (Part VI, 3) of each version it issues, and push it where the recipients read: a version of the kind `auto` stays local (Part VI, 6.3, rule 3), and a recipient could not fetch it.
4. Each version an issue releases MUST be a version of the building's history. A validator checks this rule where it has the history.

### 3.3 Example

A-101 at P02, and the version it was drawn from, released for review with an IFC print of that version:

```json
{
  "format": "openeik-drawing-issue/0.1",
  "id": "iss-01jcq6x4k7",
  "purpose": {"code": "S3", "profile": "example"},
  "recipients": [{"person": "person-r4t8wd"}],
  "renditions": [
    {"by": {"name": "@openeik/tools", "version": "0.1.0-draft"}, "bytes": 1482211, "format": "ifc", "of": {"root": "6b572fb1e20adea6f4d012a31588f32aef0dc7995796d2630497fb3f74e1f378"}, "sha256": "<sha256 of the IFC file>"}
  ],
  "revisions": [
    {"drawing": "A-101", "record": "<hash of revision.json>", "revision": "P02", "sequence": 2}
  ],
  "sent": {"at": "2026-10-26T08:30:00Z", "by": "person-k2m9xa"},
  "versions": [
    {"ref": "main", "root": "6b572fb1e20adea6f4d012a31588f32aef0dc7995796d2630497fb3f74e1f378", "version": null}
  ]
}
```

## 4. Signing an issue

### 4.1 The statement

An issue is signed as a revision is (Part XIV, 14): an in-toto Statement v1 in a DSSE envelope, signed by a person of the registry with a key of their record, a passkey among them, each signature time-stamped (RFC 3161), in a file of Part XIV, 14.2's format. Its statement:

- `_type`: `https://in-toto.io/Statement/v1`;
- `subject`: exactly one, the issue record: `drawings/issues/<issue>.json`, with its hash (Part III, 6);
- `predicateType`: `https://openeik.org/attestation/issue/v0.1`;
- `predicate`:

| Member | | Meaning |
|---|---|---|
| `issue` | required | `{id, hash}`: the issue, by its id and its record's hash |
| `action` | required | `issued`, `received` or `withdrawn` |
| `signer` | required | a person of the registry |
| `role` | optional | the role in which the signer acts, a plain value |
| `at` | required | the signer's own time; the time-stamp's is the trusted one |
| `withdraws` | required for `withdrawn` | the hash of the statement file it withdraws |

`issued` is the sender's: "I released exactly these revisions, versions and prints, to these recipients, for this purpose." `received` is a recipient's, one the issue names or one a host sent it to (5.2): "I received exactly this issue." `withdrawn` withdraws a statement of the signer's own about the same issue; it does not unsend the issue, which stays as it was released, and a later issue supersedes it.

*Note (informative).* Why one subject. The issue record names each revision by its record's hash, which names the revision's SVG by its digest (Part XIV, 8.1), each version by its root tree hash, and each print by its digest: a chain of hashes. So the record's hash covers all of them, whatever the issue's size, and the statement of an issue of five hundred drawings is as small as that of one. A verifier follows the chain (4.4).

```json
{
  "_type": "https://in-toto.io/Statement/v1",
  "predicate": {
    "action": "issued",
    "at": "2026-10-26T08:31:00Z",
    "issue": {"hash": "<hash of the issue record>", "id": "iss-01jcq6x4k7"},
    "role": "issuer",
    "signer": "person-k2m9xa"
  },
  "predicateType": "https://openeik.org/attestation/issue/v0.1",
  "subject": [
    {"digest": {"sha256": "<hash of the issue record>"}, "name": "drawings/issues/iss-01jcq6x4k7.json"}
  ]
}
```

### 4.2 The file

`drawings/issues/<issue>/signoffs/<n>.json`, in Part XIV, 14.2's format, named as 14.2 names a sign-off.

### 4.3 Rules

1. The file MUST validate against Part XIV, 14.2's schema; its payload MUST decode to an in-toto Statement v1 whose `predicateType` is `https://openeik.org/attestation/issue/v0.1`, and its predicate MUST validate against its schema.
2. The statement's subject MUST be exactly one, the issue record of the folder it is in, by its path and its hash, and the predicate's `issue` MUST be that issue's `id` and that hash.
3. `signer` MUST name a person of the registry of the kind `person`, and each signature's `keyid` a key of that record: an agent never signs a statement about an issue.
4. An `issued` statement's signer MUST be the issue's `sent.by`, and a `received` statement's signer a recipient that the issue names by `person`, or a person that a receipt of the issue, which the folder holds, names as its `recipient`. A recipient outside the registry, named by `name`, signs nothing.
5. Every signature MUST have a time-stamp.
6. Each signature MUST verify, with its time-stamp, as Part XIV, 14.7, rule 9 says.
7. A `withdrawn` statement's `withdraws` MUST name a statement of the same issue by the same signer, by its file's hash.
8. The file's name SHOULD be made as Part XIV, 14.2 says.

### 4.4 Verifying

A verifier checks a statement as Part XIV, 14.3 checks a sign-off: the DSSE signature with the registry's key, the time-stamp and its imprint, the key's window of validity at the time-stamp's time, the subject's digest against the issue record, and the predicate type, explicitly. It then follows the record: each revision it lists against its drawing's register (Part XIV, 12.2, rule 2), and, where it has the history or a store, each version's root. A print's file is checked against its digest where the verifier has the file. It needs the folder and the time-stamp authority's certificate, and no host.

## 5. Receipts

### 5.1 What a host knows

A host that sends an issue knows, for each recipient, what happened to it:

| Event | What happened |
|---|---|
| `sent` | the host sent the issue to the recipient |
| `delivered` | the recipient's mail system took the message, as the host's mail provider reported |
| `opened` | the recipient opened the issue for the first time |
| `viewed` | the recipient looked at a revision or a version that the issue releases |
| `downloaded` | the recipient took a file: a print, one the issue names or one the host printed on demand, or the issue's set of files |
| `withdrawn` | the sender withdrew the issue from the recipient, and the host stopped showing it |
| `superseded` | a later issue released a later revision of a drawing that this issue released, and the host showed the recipient the later one |

These events, the links that lead to them and how long a link lives are the host's. The building holds them only as a receipt.

The issue record is written once, by the authoring tool, when it issues; a host never writes the building. A host may then send that issue as one transmittal or several, days later, and to people the record does not name, as when a consultant forwards it to a subcontractor; and it may print on demand what the issue releases, the same bytes each time (7.2, rule 6; Part XIV, 15.3), a print the record does not list. The record does not change: those sendings and prints reach the building only in receipts (5.2).

### 5.2 The receipt

A receipt is a statement the host signs, with a key it publishes, about one recipient of one issue: a file of Part XIV, 14.2's format, its envelope signed with the host's key and time-stamped, so that it is checked offline, without the host. Its statement:

- `subject`: exactly one, the issue record, as in 4.1;
- `predicateType`: `https://openeik.org/attestation/issue-receipt/v0.1`;
- `predicate`:

| Member | | Meaning |
|---|---|---|
| `issue` | required | `{id, hash}`, as in 4.1 |
| `host` | required | `{name}`: the host, as people know it |
| `recipient` | required | the recipient: `{person}`, or `{name, organisation?}`; one the issue names, as it names them, or one the host sent the issue to |
| `sentBy` | required for a recipient the issue does not name | `{person}`: who on the host sent the issue to that recipient, a person of the registry |
| `transmittal` | required for a recipient the issue does not name | the host's own id for that sending, a plain string of 1 to 200 characters, unique only together with the receipt's `host.name` |
| `events` | required | 1 to 10 000, oldest first: `{event, at, drawing?, sequence?, root?, sha256?, of?, issue?}`: the event (5.1) and when it happened, by the host's clock; for `viewed`, `downloaded` and `superseded`, the revision (`drawing` and `sequence`), the version (`root`) or the print (`sha256`) it was about; for a print the issue does not list, `of`, what it was printed from, `{drawing, sequence}` or `{root}`; and for `superseded`, the later issue's id |
| `at` | required | when the host made the receipt: it holds the events until then |

For a recipient the issue does not name, the receipt is the building's only record that the host sent the issue to them, and says by whom (`sentBy`) and in which sending (`transmittal`). `transmittal` is the one id that only a host knows which a file of this part holds, as Part XIV, 7.2 otherwise forbids: it tells one sending from another, for people and for the host, and nothing in the building resolves it. It is unique only together with the receipt's `host.name`: two hosts' ids are never compared alone. A print the issue does not list is named by its digest and by what it was printed from: since a print is the same bytes each time, anyone who can read the issue can print it again and compare.

The file is `drawings/issues/<issue>/receipts/<n>.json`, named as 4.2 names a statement. A host MAY make a later receipt for the same recipient, with the events since; it holds that host's earlier events too. Two hosts that send one issue to one recipient never see each other's events: each makes receipts of its own, which hold its own events only, and are told apart by their `host.name`.

### 5.3 Rules

1. The file MUST validate against Part XIV, 14.2's schema; its payload MUST decode to an in-toto Statement v1 whose `predicateType` is `https://openeik.org/attestation/issue-receipt/v0.1`, and its predicate MUST validate against its schema.
2. The statement's subject MUST be exactly one, the issue record of the folder it is in, by its path and its hash, and the predicate's `issue` MUST be that issue's `id` and that hash.
3. `recipient` MUST be a recipient the issue names, as it names them, or one the host sent the issue to: for one the issue does not name, the predicate MUST give `sentBy` and `transmittal`.
4. The events MUST be in the order of their times, and none later than the receipt's `at`.
5. An event's `drawing` and `sequence` MUST name a revision that the issue releases, its `root` a version that it releases, and its `sha256` a print that it names or, with `of`, a print of what `of` names, which MUST be a revision or a version that the issue releases.
6. Every signature MUST have a time-stamp.
7. Each signature MUST verify, with its time-stamp, by a key the host publishes (5.6), of a file whose `host.name` is the receipt's: a verifier is given the host's keys, with their windows of validity, as trust anchors, as it is given the time-stamp authority's certificate (Part XIV, 14.3).
8. A later receipt for the same recipient from the same host, by its `host.name`, SHOULD hold every event of an earlier one. Receipts of two hosts are not compared: neither sees the other's events.
9. The file's name SHOULD be made as Part XIV, 14.2 says.

### 5.4 The recipient's own word

A receipt is the host's word. A recipient MAY also sign `received` (section 4): their own word, with their own key, about exactly this issue. A contract that rests on receipt SHOULD ask for the recipient's statement.

### 5.5 Where they are kept

A host keeps its events, its delivery and its links, and offers a receipt as this file; the authoring tool saves it beside its issue, as it saves reviews (Part XIV, 13) and sign-offs (Part XIV, 14.4). A host never writes the building. A receipt names a person and says when they looked at what: it is personal data (section 8, rule 2).

### 5.6 A host's keys

A host publishes the keys it signs with in one file on its own origin, so that a verifier can fetch them, or be given them, as the trust anchors of 5.3, rule 7. It is no file of the building, which names no host's URL (section 8, rule 5): the folder never holds it, and which origin is a host's is the verifier's caller's to say, as which time-stamp authority to trust is.

The file is `/.well-known/openeik/host-keys.json` at the host's origin, over HTTPS (`https://host.example/.well-known/openeik/host-keys.json`), held to [`schemas/proposed/review/host-keys.schema.json`](https://openeik.org/schemas/0.1/proposed/review/host-keys.schema.json), which is closed, as this part's other schemas are:

| Member | | Type | Meaning |
|---|---|---|---|
| `format` | required | `"openeik-host-keys/0.1"` | |
| `host` | required | `{name}` | the host, as people know it: the `host.name` of its receipts and of its attestations (5.2; 6.1) |
| `keys` | required | list, 1 to 100 | the keys it signs with, each in the shape of a key of the registry (Part XIV, 5.1), `{keyid, algorithm, public, validFrom, validUntil?}`, with no `rpId`: `algorithm` is `ed25519` or `ecdsa-p256`, as Part XIV allows; `public`, the base64 of its DER SubjectPublicKeyInfo; and `validFrom` and `validUntil`, RFC 3339 times in UTC, the window in which it signs |

```json
{
  "format": "openeik-host-keys/0.1",
  "host": {"name": "Example Host"},
  "keys": [
    {"algorithm": "ed25519", "keyid": "k-host-3f2a9c1d7e40", "public": "<base64 SPKI>", "validFrom": "2026-10-26T00:00:00Z", "validUntil": "2027-04-26T00:00:00Z"},
    {"algorithm": "ed25519", "keyid": "k-host-8b51e6a0c2d9", "public": "<base64 SPKI>", "validFrom": "2027-04-20T00:00:00Z"}
  ]
}
```

The first key is retired and stays listed; the second took over a few days before the first ended, as a host rolls one key over to the next.

The path is a well-known URI (RFC 8615) under the name `openeik`, which other files of the standard that a host serves can share. The name is not registered with IANA, as RFC 8615, 3 asks of a well-known URI: that registration is not done. The steward decided on 26 October 2026 that it is done once this part is published in 0.2 (open question 84).

1. A host that signs a receipt, or attests terms accepted, MUST serve its keys in this file, at `/.well-known/openeik/host-keys.json` on its origin, over HTTPS, as `application/json`, and the file MUST validate against its schema. It SHOULD let any origin read it (`Access-Control-Allow-Origin: *`), so that a verifier in a browser can fetch it.
2. `host.name` MUST be, exactly, the `host.name` of each receipt and attestation the host signs with a key of the file.
3. A key of the file MUST sign only the host's statements of this part: its receipts (5.2) and its attestations of terms accepted (6.1, `by` `host`). The host signs nothing else with it, and signs those with no key the file does not list.
4. Each key's `public` MUST be a key of its `algorithm`, and its `validUntil`, where given, MUST NOT be before its `validFrom`. A `keyid` MUST be listed once, and a host MUST NOT give a key id to another key, ever, not even one of a key it no longer lists.
5. A host retires a key by giving it a `validUntil`. A retired key MUST stay listed, its `validUntil` unchanged, and a host MUST NOT remove a key, nor change its `algorithm`, `public` or `validFrom`, once it has signed with it. Several keys MAY be valid at once, as while a host rolls a key over.
6. A verifier fetches the file from the host's origin over HTTPS, or is given it by its caller, and pins it: it records the SHA-256 of the bytes it trusted, and verifies with the keys of that file, not with what the host serves later. A later file it takes in place of a pinned one SHOULD hold every key of the pinned one, unchanged but for a `validUntil` given to a key that had none; a verifier that finds a key removed or changed SHOULD NOT take the later file without its caller's word.
7. A verifier verifies a receipt, or a host's attestation of terms accepted, only by a key of a file whose `host.name` is the statement's `host.name`, and only where the key was valid at the time-stamp's time: from its `validFrom`, and before its `validUntil` (5.3, rule 7; 6.2, rule 7).

## 6. Terms accepted

### 6.1 The record

`project/terms-accepted/<n>.json` says that a named person accepted named terms, for the building or for one issue. It is a statement in a file of Part XIV, 14.2's format, signed by the person with a key of their record, or attested by a host with a key it publishes, where the person accepted the terms on the host after signing in. Its statement:

- `subject`: the terms, `terms/<sha256>` with the SHA-256 of their text, and, where they are an issue's, the issue record as in 4.1;
- `predicateType`: `https://openeik.org/attestation/terms-accepted/v0.1`;
- `predicate`:

| Member | | Meaning |
|---|---|---|
| `person` | required | who accepted them, a person of the registry |
| `terms` | required | `{sha256, title, version?}`: the terms, by the SHA-256 of their text, with their title and version as people know them |
| `for` | required | `{project, issue?}`: the building, by its project's id (Part II, 2), and, where the terms are an issue's, the issue as `{id, hash}` |
| `by` | required | `person`, where the person signed with their own key; `host`, where a host attests it |
| `host` | required for `host`, and absent for `person` | `{name}`: the host, as people know it |
| `at` | required | when the person accepted them, by the signer's clock |

The folder MAY keep the terms' text as an asset, `assets/<sha256>.pdf` (Part XV). Terms that change are new terms, with a new digest: an acceptance names the text it accepted.

### 6.2 Rules

1. The file MUST validate against Part XIV, 14.2's schema; its payload MUST decode to an in-toto Statement v1 whose `predicateType` is `https://openeik.org/attestation/terms-accepted/v0.1`, and its predicate MUST validate against its schema.
2. The subjects MUST be the terms, `terms/<sha256>` with their digest, and, where `for` names an issue, that issue's record by its path and its hash: nothing else.
3. `person` MUST name a person of the registry of the kind `person`.
4. With `by` `person`, each signature's `keyid` MUST be a key of that person's record; with `by` `host`, the signature is the host's, by a key the host publishes (5.3, rule 7; 5.6).
5. `for.project` MUST be the project's id, and `for.issue`, where given, MUST name an issue of the folder by its record's hash.
6. Every signature MUST have a time-stamp.
7. Each signature MUST verify, with its time-stamp, as Part XIV, 14.7, rule 9 says, or, with `by` `host`, as 5.3, rule 7 says.
8. The file's name SHOULD be made as Part XIV, 14.2 says.

### 6.3 What it says

An acceptance says that a person named in the registry accepted the terms with this digest, for this building or this issue, at a time that a time-stamp proves. Whether that binds anyone is the law's (1.1). A host that asks a recipient to accept terms before an issue opens keeps its own record, and offers this file; the authoring tool saves it. An acceptance is personal data (section 8, rule 2).

## 7. Prints

### 7.1 A print is not the record

A print is a copy made for people to read or for another tool: a PDF of a revision (Part XIV, 15), or an IFC exported from a version by a bridge (Part VIII). It is dead matter, as paper is. What was issued, reviewed and signed is the revision or the version, by its digests; a print says only what it is a copy of. Two prints are never compared to find what changed: revisions are (Part XIV, 9 and 17.3), and so are versions (Part VI, 4). One print is kept: a PDF rendition signed in the legal tier, beside its revision as its signed copy (Part XIV, 15.1). A consultant's PDF that is the drawing, an unlinked drawing (Part XIV, 8.3), is no print: it is issued and reviewed as it is.

1. An application MUST NOT take a print in place of the revision or the version it was printed from: a review, a sign-off and a statement are about the revision or the version, never about a print.
2. A tool SHOULD NOT compare two prints to say what changed between them; it compares the revisions or the versions they were printed from.
3. A host or an application that offers a print SHOULD call it one ("Print as PDF", "Print as IFC"), so that no one takes it for the record.

### 7.2 An IFC names its version

1. A bridge that exports IFC from a version MUST name in the file the version it was exported from, where it knows it: an `IfcPropertySet` named `OpenEik_Source`, related to the `IfcProject` by an `IfcRelDefinesByProperties`, with the single-value properties `VersionRoot`, the version's root tree hash (Part VI, 2, rule 4), and `VersionId`, its id, each where it knows it; `ProjectId`, the project's id (Part II, 2); `Bridge`, the bridge and its version (`@openeik/tools 0.1.0-draft`); and `Print`, the `IfcBoolean` true. `VersionRoot`, `VersionId` and `ProjectId` are `IfcIdentifier`s, and `Bridge` an `IfcLabel`.
2. Its `IfcApplication` MUST name the bridge and its version, and its `IfcOwnerHistory` that application.
3. The file SHOULD say that it is a print where people read it: the header's `FILE_DESCRIPTION`, in its description list, and the `IfcProject`'s `Description` SHOULD each carry a sentence that names the version by the short form of its root, says that the file is a copy, like paper, and that the OpenEik building is its source, such as "A print of OpenEik version 6b572fb1e20a: a copy, like paper. The OpenEik building is the source." `Print` is the mark a program reads; the sentence is for people.
4. The `IfcOrganization` of its `IfcOwnerHistory`, the organisation of its owning user, SHOULD name who printed the file, a host or an application, as people know it, which the bridge's caller gives; the `IfcApplication` names the bridge, which did the printing for it.
5. The fidelity report of Part VIII, 1, rule 2 goes with every export, as before, and a short form of it SHOULD travel in the file: an `IfcDocumentInformation` named `Fidelity report`, associated with the `IfcProject` by an `IfcRelAssociatesDocument`, saying the IFC schema written, the counts and what the export leaves out or approximates.
6. A bridge SHOULD give the same bytes for the same version, bridge and options, who printed the file among them. It writes no time but one its caller gives it, or else a fixed time it states, in the header's `time_stamp` and in `IfcOwnerHistory`'s `CreationDate`; and no id, order or name that changes from one export to the next.

`OpenEik_Source` is no `Pset_` set: IFC keeps the prefix `Pset_` for buildingSMART's own property sets, and a reader finds this one by its name on the project. Rules 1 and 2 let a program tell a print and the version it came from; rules 3 and 4 tell a person, in any IFC viewer, what they are looking at and who printed it; rule 5 keeps what the print leaves out with the print.

*Note (informative).* By plan 688's decision 7, `@openeik/tools` prints IFC for every application, so that a host and the authoring tools print a version alike: `ifcOfModel` (Part VIII, 1) writes rules 1 to 6 as they stand, with `printedBy` for rule 4, and a host that prints with it names itself as the organisation of rule 4. The reference implementation's bridge (`exportIfc` in `src/core/io/ifcExport.ts`) writes the time of the export in the header's `FILE_NAME`, and no `IfcApplication` or `IfcOwnerHistory`: two exports of one version differ, and neither says which version it came from.

### 7.3 A PDF's margin note

Part XIV, 15.3 says it, by plan 688's decision 6: a PDF rendition carries in its bottom margin the drawing's number and revision code, the building, the version it was drawn from, a sentence saying it is a copy, and a QR code that names the revision by its SVG's digest; and no time of printing, so that it stays the same bytes.

## 8. Security and privacy

1. **A link looks, a person decides.** A host MUST NOT offer, as a person's item, reply, decision or statement, anything made by someone who has not signed in as that person. A link without sign-in looks and prints: it gives nothing that the building records.
2. **Personal data.** A receipt says when a named person opened what and looked at it; an acceptance, that they accepted terms; a model review, what they said. Each is personal data, kept in every version that holds it (Security and privacy, 2.4). An authoring tool SHOULD save a receipt only where the project needs it, as a contract may, and a host SHOULD tell a recipient that what it records of them may be saved into the building.
3. **A host's word.** A receipt and a host's attestation are as good as the host's key. A verifier trusts a host's key only as its caller says, within the key's window of validity, and a host ends a key it stops using, and never reuses it. A receipt's time-stamp proves when the receipt was made, not when its events happened: those are the host's clock. The file of a host's keys (5.6) is a trust anchor, and its integrity is the verifier's business: HTTPS proves only that the host's origin served it when it was fetched. So a verifier records the SHA-256 of the file it trusted, and a host never removes a key once it has signed with it: what verified once verifies again with the same file, and a host that serves another file later cannot change it.
4. **The check code.** A print's QR code names a digest, and the page it leads to is the host's. That page SHOULD say only whether the revision is current, and the later revision's code where there is one, and SHOULD show nothing more to anyone who has not signed in: whoever holds the paper has seen what it holds, and no more.
5. **No host in the building.** No file of this part holds a host's URL, an account's id or an email address (Part XIV, 7.2): a host is named as people know it, and its key by a keyid. A receipt's `transmittal` is the one id of a host's it holds, and names a sending, never an account (5.2).

## 9. Conformance

No 0.1 conformance class requires this part ([Conformance](https://openeik.org/spec/0.1/conformance)). An implementation that implements the proposal says which of its classes do:

- a **validator** checks the rules of sections 2 to 6 that the folder decides, reporting a breach of a MUST as an error and a breach of a SHOULD as a warning, and naming the file and the rule, as `XVIII.2.5.7`;
- a **drawing validator** (Part XIV, 19.2) also verifies statements, receipts and acceptances (4.3, rule 6; 5.3, rule 7; 6.2, rule 7), given the hosts' keys (5.6) and the time-stamp authorities' certificates as trust anchors, and keeps 5.6, rules 6 and 7;
- a **bridge** names the version it exports from (7.2);
- a **host** keeps the events of 5.1 and offers receipts, publishes its keys as 5.6, rules 1 to 5 say, and keeps section 8, rules 1 and 4;
- an **authoring tool** saves what a host offers, and records a named version of what it issues (3.2, rule 3).

The folder alone decides most rules. These need more:

- **the reviewed version, or the history**: 2.5, rule 4; 2.5, rule 5, for the file held unchanged; 3.2, rule 4;
- **cryptography beyond hashing**: 4.3, rule 6; 5.3, rule 7; 6.2, rule 7;
- **a host's keys**, which the folder never holds: 5.3, rule 7; 5.6; 6.2, rule 7 for `by` `host`;
- **the bridge's output**: 7.2.

A validator that implements the proposal reports a breach of these rules as an error: 2.5, rules 1 to 3, 5 and 7 to 11, and rule 4 for an anchor's `hash`; 3.2, rules 1, 2 and 4; 4.3, rules 1 to 7; 5.3, rules 1 to 7; and 6.2, rules 1 to 7. It reports a breach of these as a warning: 2.5, rule 4 for an anchor's element, and rule 6; 4.3, rule 8; 5.3, rules 8 and 9; and 6.2, rule 8. A verifier does not take a host's file of keys that does not validate or breaks 5.6, rule 4, and reports a statement that 5.6, rule 7 does not let verify as a breach of 5.3, rule 7 or 6.2, rule 7. The other rules bind issuers, tools, bridges, hosts and verifiers, and a validator does not check them from the folder: 3.2, rule 3; 5.6, rules 1 to 3, 5 and 6; section 7; and section 8.

*Note (informative).* What `@openeik/tools` does with the proposal `review`, since 25 October 2026: `checkFolder(…, { proposals: ["review"] })` and `checkModel`, and `openeik check --proposal review`, apply the proposal `drawings` too and check every rule of sections 2 to 6 that the folder decides (`reviewChecks`, with the codes its README lists); `openeik review bcf <folder> <review>` exports a model review as BCF 3.0 (`modelReviewBcf`), as 2.6 says; and the proposal's conformance cases are in `conformance/proposed/review/`. Since 26 October 2026 it verifies statements, receipts and acceptances (4.4; 5.3, rule 7; 6.2, rule 7): `verifyReviewStatements` and `verifyReceipts`, and `openeik review verify <folder> --host-keys <file> --tsa <certificate>`, check each signature with its key, the registry's or a host's of the files given (`readHostKeys` holds each to 5.6), its time-stamp and the key's window, and each statement's subjects and predicate type; they record the SHA-256 of the host's file that verified a receipt, and leave comparing a pinned file with a later one (5.6, rule 6) to their caller. Since 28 October 2026 it checks 2.5, rule 4 where it has the version reviewed: `checkReviewAnchors(folder, source)` and `openeik review anchors <folder>` read each round's version by its root from a store or from objects given, report an anchor's `hash` that is not its record's there as an error and an element that version does not hold as a warning, and list a round whose version the source does not hold as not checked; the suite's cases of the form `anchors` give that version beside the model, as `reviewed/`. Since 26 October 2026 `ifcOfModel` writes IFC as section 7 says: an IFC names its version, the bridge and who printed it, and says it is a print (Part VIII, 1).

## 10. The questions

The steward decided these questions on 26 October 2026, each as this part proposed it (open questions 61 to 68). Question 84 is proposed.

| Question | What | Decided |
|---|---|---|
| 61 | Markup shapes | an optional `shape.form` (`freehand`, `polyline`, `polygon`, `rectangle`, `ellipse`), a rectangle or an ellipse by the four corners of its rectangle, turned as it lies, and `shape` on a text and a comment as its words' frame; the six item kinds stay; colour and line weight are extension members (Part XIV, 13.2 and 13.4, rule 9) |
| 62 | Where a model's review lives | in this part, a part of its own with its own proposal, `review`, built on Part XIV's registry, profile, issues and statements, rather than in Part XIV or Part VI (sections 1 and 2) |
| 63 | How a version is issued | by Part XIV's issue, which gains `versions`, `renditions` and `terms`, rather than by a file of its own (section 3) |
| 64 | What an issue's statement signs | the issue record alone, one subject, which names everything else by its hash (section 4) |
| 65 | How a host's receipt is checked | with a key the host publishes, which a verifier is given as a trust anchor, rather than a record of the building's registry (section 5) |
| 66 | Terms accepted | a statement in `project/terms-accepted/`, signed by the person or attested by a host, for the building or an issue (section 6) |
| 67 | A profile without fonts | `drawings/profile.json` serves a model's reviews too, and needs `fonts` only where the folder holds a linked issued drawing (Part XIV, 4.1 and 4.3, rule 5) |
| 68 | Recipients a host adds | a receipt may be for someone the host sent the issue to and the record does not name, with `sentBy` and `transmittal`; an event may name a print the host made on demand, by its digest and what it was printed from (`of`); and such a recipient, a person of the registry, may sign `received` (sections 4 and 5) |
| 84 | The well-known name `openeik` | proposed, not decided: registered with IANA as a well-known URI (RFC 8615) once this part is published in 0.2, with a template in this part; until then a host serves its keys at the path 5.6 gives, unregistered |
