1. Introduction
1.1. Status of This Document
This document is a CG Draft of the RecordWeb Community Group. It is an Editor’s Draft and does not represent a W3C standard or endorsed position.
The published baseline is available at [ZENODO-RWP] (Version 0.1, DOI: 10.5281/zenodo.20475345). This working document reflects ongoing development toward the Community Group deliverable.
Feedback is invited via GitHub Issues.
2. 1 Introduction and Scope
2.1. 1.1 Purpose
The RecordWeb Protocol (RWP) defines the technical standards for the creation, identification, versioning, linking, and proof of Records in an RWP-compliant system.
RWP is the normative counterpart to the RecordWeb Concept ([RWC]). The RWC describes the conceptual foundations; RWP translates these into binding technical requirements that MUST be observed for interoperable implementation.
2.2. 1.2 Scope
This document applies to:
-
Systems that create, store, or manage Records in the sense of the [RWC]
-
Systems that import Records from other RWP-compliant systems or access them
-
Implementations claiming interoperability with other RWP-compliant systems
This document does not apply to:
-
The internal storage architecture of a compliant system (database, file system, object store)
-
User interfaces and workflows built on top of an RWP-compliant system
-
The content of payloads, insofar as these are not standardised by the schema of the Record type
2.3. 1.3 Relationship to Other Standards
| Standard | Relationship |
|---|---|
| [ISO15489] | RWP structurally implements the requirements for authenticity, reliability, integrity, and usability |
| [ISO14721] (OAIS) | RWP is complementary; finalised Records MAY be migrated to long-term archives according to OAIS principles |
| [DID-CORE] | RWP uses DIDs as the identity mechanism; conformance with W3C DID Core 1.0 is REQUIRED |
| [PROVO] | RWP lineage MAY be serialised as PROV-O; this is non-normative |
| [ECH-0164] | RWP maps the eCH information lifecycle as states; no structural dependency |
| [RWC] v1.0 | Conceptual foundation; in the event of conflict, RWP takes precedence |
2.4. 1.4 Versioning of This Document
This document uses semantic versioning (SemVer): MAJOR.MINOR.PATCH.
-
MAJOR: Incompatible changes to existing standards
-
MINOR: Backward-compatible extensions
-
PATCH: Corrections without substantive change
The current version is 0.0.1 (Draft — not yet approved for production implementations).
3. 2 Record Identity (DID)
3.1. 2.1 Requirements for Record Identity
Every Record MUST possess a globally unique, permanent identity.
This identity:
-
MUST be independent of the physical storage location of the Record
-
MUST be independent of the organisation managing the Record
-
MUST NOT be changed after the Record has been created
-
MUST remain valid without change even after a system migration
-
MUST be machine-resolvable (a resolver MUST be able to retrieve the DID document from the identity)
3.2. 2.2 DID Format
The identity of a Record MUST be expressed as a Decentralized Identifier (DID) in accordance with [DID-CORE].
Normative format:
did:rwp:{namespace}:{unique-id}
| Component | Requirement |
|---|---|
did
| Literal; immutable |
rwp
| DID method for RecordWeb Protocol; immutable |
<namespace>
| Identifier of the organisation or system; MUST be uniquely registered. During the transition phase, a namespace MAY be formed on the basis of the DNS domain name of the organisation (e.g. did:rwp:bern.ch:<uuid>).
|
<unique-id>
| Unique identifier of the Record within the namespace; MUST be a UUID v4 or a cryptographically equivalent random value |
Example: did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479
3.3. 2.3 DID Document
For every Record DID, a DID document MUST exist containing the following fields:
{ "@context" : "https://www.w3.org/ns/did/v1" , "id" : "did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479" , "recordEndpoint" : "https://records.bern.ch/api/v1/records/f47ac10b" , "created" : "2026-05-31T14:00:00Z" , "updated" : "2026-05-31T14:00:00Z" , "currentVersion" : "sha256:e3b0c44298fc1c149afb..." , "controller" : "did:rwp:bern.ch:controller-001" , "verificationMethod" : [ { "id" : "did:rwp:bern.ch:f47ac10b#key-1" , "type" : "Ed25519VerificationKey2020" , "controller" : "did:rwp:bern.ch:controller-001" , "publicKeyMultibase" : "z6MkhaXgBZDvotDkL..." } ] }
| Field | Required | Description |
|---|---|---|
@context
| MUST | W3C DID Context URI |
id
| MUST | DID of the Record; identical to the Record DID |
recordEndpoint
| MUST | URL at which the Record can be retrieved |
created
| MUST | ISO 8601 timestamp of DID creation |
updated
| MUST | ISO 8601 timestamp of last DID document update |
currentVersion
| MUST | Content hash of the current finalised version (empty for pure draft) |
controller
| MUST | DID of the controlling entity (organisation or person) |
verificationMethod
| MUST | At least one verification method for signature validation |
The DID document MUST be updated when the physical storage location changes (recordEndpoint, updated, currentVersion). All other fields MUST NOT be changed.
3.4. 2.4 DID Resolver Requirements
An RWP-compliant system MUST provide a DID resolver that:
-
Resolves DIDs of the method
rwpand returns the associated DID document -
Delivers the DID document as JSON-LD in accordance with [DID-CORE]
-
Returns HTTP 200 when the DID is known
-
Returns HTTP 404 when the DID is unknown
-
Returns HTTP 410 when the DID is known but the Record has been deleted (payload deletion per Chapter 13)
4. 3 Record Structure
4.1. 3.1 Overview
Every Record MUST consist of exactly three components:
Together, these three components form a snapshot — an immutable, versioned representation of the Record at a specific point in time.
4.2. 3.2 Snapshot Definition
A snapshot is the atomic unit of versioning in RecordWeb.
A snapshot MUST fulfil the following properties:
-
It is immutable after its creation
-
It is uniquely identifiable by its content hash
-
It references exactly one version of the payload (or multiple representations per Section 4.3)
-
It references exactly one version of the schema of its Record type
-
It is linked to its predecessor snapshots by directed edges in the version graph
4.3. 3.3 Minimum Metadata Set
Every snapshot MUST contain the following metadata fields:
{ "did" : "did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479" , "snapshotHash" : "sha256:e3b0c44298fc1c149afb4c8996fb924..." , "recordType" : "did:rwp:ech-standards.ch:schema-baubewilligung-antrag" , "schemaVersion" : "sha256:a665a45920422f9d417e4867efdc4fb8a..." , "state" : "finalized" , "created" : "2026-05-31T14:00:00Z" , "finalized" : "2026-05-31T15:30:00Z" , "owner" : "did:rwp:bern.ch:user-petra-muster" , "parents" : [ "sha256:previousSnapshotHash..." ], "classification" : "internal" , "retentionPolicy" : "did:rwp:bern.ch:retention-bauakten-30y" , "tags" : [ "building-permit" , "parcel-451" , "2026" ], "accessPolicy" : null , "payloadHash" : "sha256:d2d2d2d2d2..." , "payloadFormat" : "application/pdf;profile=PDF-A-2b" , "signature" : "z3FttV7..." }
| Field | Required | Type | Description |
|---|---|---|---|
did
| MUST | DID | Identity of the Record |
snapshotHash
| MUST | SHA-256 | Hash of this snapshot (incl. payload and metadata) |
recordType
| MUST | DID | Reference to the SchemaRecord of the type |
schemaVersion
| MUST | SHA-256 | Hash of the schema version against which this snapshot was validated |
state
| MUST | Enum | draft or finalized; additional values permitted per type schema
|
created
| MUST | ISO 8601 | Timestamp of snapshot creation |
finalized
| MUST (when finalized) | ISO 8601 | Timestamp of finalisation |
owner
| MUST | DID | Responsible person or organisational unit |
parents
| MUST | Array<SHA-256> | Hashes of predecessor snapshots; empty for first snapshot |
classification
| SHOULD | String | Protection level per the organisation’s internal scheme |
retentionPolicy
| SHOULD | DID | Reference to retention rule |
tags
| MAY | Array<String> | Free-text markers for search and filtering |
accessPolicy
| MAY | Object|null | Delegated authorisation model (Chapter 11) |
payloadHash
| MUST | SHA-256 | Hash of the payload |
payloadFormat
| MUST | MIME-Type | Format of the primary payload incl. profiles |
signature
| MUST (when finalized) | Multibase | Cryptographic signature of the record owner over the snapshot hash |
4.4. 3.4 Snapshot Hash Calculation
The snapshotHash MUST be calculated as follows:
-
Serialise the metadata fields (without
snapshotHashitself) as canonical JSON ([RFC8785]) -
Concatenate:
canonicalMetadataJSON || payloadBytes -
Calculate SHA-256 over the result
snapshotHash = SHA-256( canonicalize(metadata \ {snapshotHash}) || payloadBytes )
A system MUST verify the snapshotHash after every finalisation before the snapshot is accepted as valid.
5. 4 Payload and Formats
5.1. 4.1 Payload Definition
The payload of a Record is the content the Record carries, the actual information.
A payload:
-
MUST conform to the schema of the Record type in the referenced schema version
-
MUST be validated against the schema before finalisation
-
MUST NOT be changed after finalisation
-
MUST be uniquely identified in the
payloadHashof the snapshot
5.2. 4.2 Permitted Payload Types
RWP distinguishes three payload categories:
| Category | Description | Examples |
|---|---|---|
| Structured | Machine-readable, schema-validatable data | JSON, XML, YAML |
| Documentary | Human-readable documents with defined structure | PDF/A, DOCX, ODT, Markdown |
| Binary | Non-textual data with descriptive schema | TIFF, JPEG 2000, MP4, ZIP |
5.3. 4.3 Multi-Representation
A snapshot MAY carry multiple representations of the same payload. This is provided in particular for the transition from working format (draft) to long-term format (finalised).
If a snapshot contains multiple representations, ALL of the following MUST apply:
-
All representations MUST depict the same content
-
All representations MUST be individually hashed and referenced in the snapshot
-
One representation MUST be designated as primary (
primary: true) -
The
payloadHashMUST contain the hash of the primary representation
{ "payloadRepresentations" : [ { "format" : "text/markdown" , "hash" : "sha256:aabbcc..." , "primary" : false , "role" : "source" }, { "format" : "application/pdf;profile=PDF-A-2b" , "hash" : "sha256:ddeeff..." , "primary" : true , "role" : "publication" } ] }
| Field | Required | Description |
|---|---|---|
format
| MUST | MIME type incl. profiles |
hash
| MUST | SHA-256 of this representation |
primary
| MUST | Exactly one representation MUST be true
|
role
| SHOULD | Semantic role: source, publication, preview, archive
|
5.4. 4.4 Format Requirements per State
The schema of a Record type MUST define the permitted payload formats for each state. If a schema contains no explicit format requirements, the following defaults apply:
| State | Permitted formats (default) |
|---|---|
draft
| All MIME types not explicitly excluded in the schema of the Record type |
finalized
| Only formats from the RWP list of archival-grade formats (Annex A, Table A-1) |
RWP archival-grade formats (normative):
| MIME Type | Format | Suitability |
|---|---|---|
application/pdf;profile=PDF-A-1b
| PDF/A-1b | Documents without embedded multimedia |
application/pdf;profile=PDF-A-2b
| PDF/A-2b | Documents with embedded files |
application/pdf;profile=PDF-A-3b
| PDF/A-3b | Documents with arbitrary attachments |
text/xml
| XML (well-formed) | Structured data |
application/json
| JSON | Structured data (with schema reference) |
image/tiff
| TIFF | Raster images |
image/jp2
| JPEG 2000 | Raster images (near-lossless) |
text/plain;charset=UTF-8
| Plain Text UTF-8 | Pure text documents |
text/markdown
| CommonMark Markdown | Source format with publication representation |
5.5. 4.5 Conversion During Finalisation
When a system transitions a Record from state draft to finalized, it MUST:
-
Validate the payload against the finalised format of the schema
-
If the current payload is not in archival-grade format: convert automatically or request conversion from the operator
-
Store both formats as multi-representation in the snapshot (SHOULD)
-
Document the conversion act as metadata (
conversionMethod,conversionTimestamp)
A system MUST NOT finalise a Record whose payload does not conform to the format defined for finalized and for which no archival-grade conversion is possible.
6. 5 Record Types and Schemas
6.1. 5.1 Record Type Definition
Every Record MUST be assigned to a Record type. A Record type:
-
defines the schema of the payload
-
defines the permitted states and their transitions
-
defines the format requirements per state
-
defines mandatory and optional metadata fields beyond the minimum set
-
is itself a Record of the type
SchemaRecord
6.2. 5.2 SchemaRecord
A SchemaRecord is a specialised Record type that carries the definition of another Record type.
A SchemaRecord MUST:
-
fulfil all requirements for a regular Record (DID, metadata, version graph)
-
contain a JSON Schema (Draft 2020-12 or later, [JSON-SCHEMA]) in the payload
-
contain the field
rwpSchemaVersionin the payload with the RWP version for which it is valid -
contain the field
allowedStatesin the payload with the permitted states and their transitions -
contain the field
payloadFormatsin the payload with the permitted formats per state
Minimal SchemaRecord payload example:
{ "rwpSchemaVersion" : "0.1" , "schemaId" : "did:rwp:bern.ch:schema-building-permit-application" , "displayName" : "Building Permit Application" , "allowedStates" : [ "draft" , "finalized" ], "stateTransitions" : [ { "from" : "draft" , "to" : "finalized" , "requiresOwnerSignature" : true } ], "payloadFormats" : { "draft" : [ "application/vnd.openxmlformats-officedocument.wordprocessingml.document" , "text/markdown" ], "finalized" : [ "application/pdf;profile=PDF-A-2b" ] }, "jsonSchema" : { "$schema" : "https://json-schema.org/draft/2020-12/schema" , "type" : "object" , "required" : [ "parcelNumber" , "applicant" , "constructionProject" , "date" ], "properties" : { "parcelNumber" : { "type" : "string" }, "applicant" : { "type" : "string" }, "constructionProject" : { "type" : "string" }, "date" : { "type" : "string" , "format" : "date" } } } }
6.3. 5.3 Core Record Types (normative)
RWP defines the following core Record types that every compliant system MUST support:
| Type Name | DID Suffix | Description |
|---|---|---|
SchemaRecord
| schema-record
| Definition of a Record type |
MergeRecord
| merge-record
| Documentation of a merge act |
DeletionRecord
| deletion-record
| Log of a payload deletion (Chapter 13) |
CaseRecord
| case-record
| Case (Chapter 8) |
MigrationRecord
| migration-record
| Log of a system migration |
Additional Record types MAY be defined by organisations or standards bodies. They MUST be published as SchemaRecords and be resolvable via a DID.
6.4. 5.4 Schema Versioning and Backward Compatibility
When a SchemaRecord is finalised in a new version, the following applies:
-
Existing snapshots referencing an earlier schema version REMAIN valid
-
The new schema MUST contain the field
previousSchemaVersionin the payload with the hash of the predecessor schema -
Systems MUST keep all schema versions referenced by existing Records permanently accessible
-
A system MUST NOT retroactively change an existing SchemaRecord, corrections are made as a new version
7. 6 States and Transitions
7.1. 6.1 State Model
Every Record snapshot has exactly one *state. The state describes the validity and linkability status of the snapshot.
Core states (normative, for all Record types):
| State | Linkable | Immutable | Description |
|---|---|---|---|
draft
| NO | NO | Being processed; content may change |
finalized
| YES | YES | Complete; content cryptographically secured |
Record types MAY define additional states. These MUST be specified in the SchemaRecord and MUST be reachable from one of the core states or lead into one.
Example of an extended state machine for type "contract":
draft → in-review → finalized ↓ rejected (terminal state, not linkable)
7.2. 6.2 Linkability
A Record MUST NOT be used as the target of a hard link when its current state is draft.
A system MUST return an error when an attempt is made to set a hard link to a draft Record.
Exception: Working references (soft links) per Section 8.4 MAY point to draft Records.
7.3. 6.3 Finalisation Requirements
A system MUST perform the following checks before finalising a snapshot:
-
Schema validation: Payload conforms to the schema in the version defined for
finalized -
Format validation: Payload format conforms to the
payloadFormats.finalizedrequirements of the schema -
Mandatory field check: All MUST metadata fields are present and correctly typed
-
Owner signature: If the schema defines
signaturePolicy: owner, the record owner MUST have cryptographically signed the snapshot hash. ForsignaturePolicy: system, the signature MAY be generated by an authorised system key. ForsignaturePolicy: none, signature validation is omitted. -
Parent integrity: All referenced parent hashes are resolvable in the system
If any of these checks fails, the system MUST NOT finalise the snapshot and MUST return an error with a validation code.
7.4. 6.4 Irreversibility of Finalisation
A finalized snapshot MUST NOT be reverted to the state draft.
Corrections to a finalized Record are made exclusively by creating a new snapshot with:
-
Reference to the snapshot to be corrected as parent
-
Reasoning field
correctionReasonin the metadata object -
Complete finalisation process per Section 6.3
8. 7 Version Graph (DAG)
8.1. 7.1 Graph Structure
The totality of all snapshots of a Record and their parent-child relationships forms the version graph.
The version graph MUST be a Directed Acyclic Graph (DAG):
-
Directed: Edges point from child to parent (backward in time)
-
Acyclic: There MUST be NO cycles — a snapshot MUST NOT be directly or indirectly its own ancestor
A system MUST check upon receipt of a new snapshot whether adding its parent references would create a cycle. If so, the snapshot MUST be rejected.
8.2. 7.2 Nodes
Every node in the version graph is a snapshot per Section 3.2.
| Property | Requirement |
|---|---|
| Unique identity | SHA-256 of the snapshot (snapshotHash); MUST be unique
|
| Immutability | Finalised nodes MUST NOT be changed |
| Resolvability | Every node MUST be retrievable in the system via its snapshotHash
|
8.3. 7.3 Edges (Parent References)
An edge connects a snapshot (child) with its predecessor snapshot (parent).
-
A snapshot MAY have zero, one, or multiple parent references
-
A snapshot without parents is the root snapshot (first snapshot of the Record)
-
A snapshot with exactly one parent reference is a linear continuation
-
A snapshot with multiple parent references is a merge snapshot
Edge requirements:
-
Every parent reference MUST be a valid
snapshotHashof a known snapshot -
Edges MUST NOT point to snapshots of other Records. Cross-Record connections are made via links in the payload or as Case linkage (Chapter 8)
8.4. 7.4 Branches
A branch arises when two or more new draft snapshots with the same parent reference emerge from a finalized snapshot.
-
Branches are explicitly permitted in RecordWeb and MUST be made visible by the system
-
A system MUST return all active branches (draft snapshots without successors) when a Record is retrieved
-
Branches MAY exist simultaneously as long as they are in state draft
8.5. 7.5 Merges
A merge snapshot is a snapshot with two or more parent references. It consolidates two branches.
Before a merge snapshot can be finalised, a MergeRecord MUST be created:
{ "mergeRecord" : { "mergedSnapshots" : [ "sha256:branch-a-snapshot-hash..." , "sha256:branch-b-snapshot-hash..." ], "mergeReason" : "Consolidation after coordination between departments" , "mergedBy" : "did:rwp:bern.ch:user-petra-muster" , "mergedAt" : "2026-05-31T16:00:00Z" , "resultSnapshot" : "sha256:merged-snapshot-hash..." } }
The MergeRecord MUST be finalised before the merge snapshot can be finalised.
8.6. 7.6 Cross-Record Merges
When a new Record arises from two or more existing finalized Records (cross-Record merge), the following MUST apply:
-
The new Record MUST have a root snapshot with an empty
parentsarray -
The
MergeRecordMUST reference the DIDs of the source Records (not their snapshot hashes) -
The source Records MUST remain in state finalized and receive a metadata note
contributedTowith the DID of the new Record
9. 8 Case Specification
9.1. 8.1 Case as a Specialised Record Type
A Case is a Record of type CaseRecord. It is subject to all requirements for regular Records (Chapters 2–7) and supplements these with Case-specific requirements.
A Case SHOULD be a standalone storage object, it is a persisted view of a set of linked Records.
9.2. 8.2 Case Schema (normative)
A CaseRecord payload MUST contain the following fields:
{ "caseId" : "did:rwp:bern.ch:f47ac10b-case-001" , "caseType" : "did:rwp:bern.ch:schema-building-permit-case" , "title" : "Building Permit Musterstrasse 12, Parcel 451" , "trigger" : { "type" : "hard" , "recordDid" : "did:rwp:bern.ch:application-001" , "snapshotHash" : "sha256:application-finalized-hash..." }, "context" : [ { "type" : "hard" , "recordDid" : "did:rwp:bern.ch:zoning-plan-2024" , "snapshotHash" : "sha256:zoning-plan-hash..." , "role" : "Legal basis" } ], "process" : [ { "type" : "hard" , "recordDid" : "did:rwp:bern.ch:minutes-site-inspection" , "snapshotHash" : "sha256:minutes-hash..." , "role" : "Investigation" } ], "decision" : null , "result" : null , "merkleRoot" : "sha256:case-merkle-root..." }
9.3. 8.3 Case Elements (normative)
A complete Case MUST contain all five elements. A Case MAY be finalised if and only if all five elements are present as hard links.
| Element | Required for finalisation | Description |
|---|---|---|
trigger
| MUST | Exactly one Record that triggered the Case; MUST be a hard link |
context
| SHOULD | Information relevant to understanding and processing |
process
| MAY (empty allowed) | Documentation of processing steps |
decision
| MAY (empty allowed) | Record(s) documenting the decision; MUST be a hard link |
result
| MUST (min. 1) | At least one Record documenting the outcome; MUST be a hard link |
Exception — ad-hoc Case: A Case schema MAY be marked as adhoc: true. Ad-hoc Cases have no mandatory fields other than trigger and caseId. They MUST be marked in the metadata object as caseVariant: "adhoc".
9.4. 8.4 Hard and Soft Links
A hard link points to a finalized snapshot (snapshotHash MUST be specified). It is immutable after finalisation of the Case.
A soft link (working reference) points to a Record DID without a snapshot reference. It MAY point to finalized or draft Records. Soft links prevent finalisation of the Case and MUST be converted to hard links before Case finalisation.
A system MUST check during a Case finalisation attempt whether soft links are still present. If so, the system MUST reject the finalisation and return the open soft links.
9.5. 8.5 Case Merkle Root
The Merkle root of a Case MUST be calculated as follows:
-
Collect all
snapshotHashvalues of all hard links in the Case (trigger, context, process, decision, result) -
Sort alphabetically by SHA-256 value
-
Calculate the Merkle root over the sorted list (algorithm per Section 9.5)
merkleRoot = MerkleRoot( sort( [snapshotHash_1, snapshotHash_2, ... snapshotHash_n] ) )
The Merkle root MUST be recalculated every time a hard link is added or removed. A system MUST verify the Merkle root before finalising the Case.
9.6. 8.6 Case Completeness Check
A system SHOULD perform a completeness check on a Case at any time upon request and return:
{ "caseId" : "did:rwp:bern.ch:f47ac10b-case-001" , "complete" : false , "missingElements" : [ "decision" , "result" ], "openWorkingReferences" : [ "did:rwp:bern.ch:expert-opinion-draft-001" ], "merkleRootValid" : true }
10. 9 Integrity and Hashing Procedures
10.1. 9.1 Hashing Procedure
RWP uses exclusively SHA-256 as the hashing procedure for:
-
Snapshot hashes (
snapshotHash) -
Payload hashes (
payloadHash) -
Schema version hashes (
schemaVersion) -
Parent references (
parents) -
Merkle root calculations
A system MUST NOT use any hashing procedure other than SHA-256 for normative integrity proofs.
Note: A future version of RWP MAY permit SHA-3 or other procedures as an alternative if cryptographic weaknesses in SHA-256 become known. Until then, SHA-256 is the only permitted procedure.
10.2. 9.2 Canonical JSON Serialisation
For all hash calculations over JSON objects, RFC 8785 (JSON Canonicalization Scheme, JCS) [RFC8785] MUST be used.
Requirements:
-
Keys MUST be sorted alphabetically
-
Unicode characters MUST be normalised (NFC)
-
No superfluous whitespace or line breaks
-
Numbers MUST be represented in their canonical form
10.3. 9.3 Snapshot Integrity
A system MUST check the integrity of a snapshot on the following events:
-
Receipt of a snapshot from an external system
-
Retrieval of a snapshot for a linking operation
-
Periodic integrity check
If the integrity check fails, the system MUST:
-
Mark the snapshot as
compromised -
Mark all dependent Records as
integrity-warning -
Log the incident in the system log with timestamp and discrepancy
-
Notify the responsible record owner via the DID (SHOULD)
The system MUST NOT automatically attempt to reconstruct the original content.
10.4. 9.4 Signature Procedure
If the schema prescribes a signature (signaturePolicy: owner or signaturePolicy: system), the snapshot hash MUST be cryptographically signed during the finalisation act.
Requirements:
-
MUST be Ed25519 (EdDSA, [RFC8037]) or P-256 (ECDSA, NIST)
-
The signature MUST be calculated over the canonical
snapshotHashvalue (as hex string) -
The signature MUST be stored in the
signaturefield as a multibase-encoded value -
The key used MUST be published in the DID document of the record owner as a verification method
10.5. 9.5 Merkle Tree Algorithm
For the calculation of the Case Merkle root, the following algorithm MUST be used:
function merkleRoot(hashes: SHA256[]) -> SHA256:
if hashes.length == 0: return SHA256("")
if hashes.length == 1: return hashes
sorted = sort(hashes) // alphabetically by hex string
while sorted.length > 1:
nextLevel = ]
for i in range(0, sorted.length, 2):
if i + 1 < sorted.length:
nextLevel.append( SHA256( sorted[i] || sorted[i+1] ) )
else:
nextLevel.append( sorted[i] ) // odd element carried over unchanged
sorted = nextLevel
return sorted
11. 10 Optional Ledger Anchoring
11.1. 10.1 Purpose and Scope
Ledger anchoring is an optional extension of RWP. It enables the external, immutable proof of Case Merkle roots on a distributed infrastructure.
A system MUST be fully RWP-compliant without ledger anchoring. Ledger anchoring supplements the internal integrity assurance with an external proof independent of the operating organisation.
11.2. 10.2 Requirements for the Ledger
If a system implements ledger anchoring, the ledger used MUST:
-
be permissioned (no public, anonymous blockchain)
-
guarantee immutable entries (append-only)
-
deliver a provable timestamp per entry
-
be operated independently of the organisation that manages the Records
-
be readable by all authorised verification parties
Recommended implementation: Hyperledger Fabric [HYPERLEDGER]. Other permissioned distributed ledger technologies MAY be used if they fulfil the above requirements.
11.3. 10.3 Anchoring Protocol
When a finalised Case is anchored, the ledger entry MUST contain the following fields:
{ "rwpAnchor" : { "version" : "0.1" , "caseDid" : "did:rwp:bern.ch:f47ac10b-case-001" , "caseSnapshotHash" : "sha256:case-finalized-snapshot-hash..." , "merkleRoot" : "sha256:case-merkle-root..." , "anchoredAt" : "2026-05-31T16:00:00Z" , "anchoredBy" : "did:rwp:bern.ch:controller-001" , "ledgerTxId" : "abc123..." } }
The ledger entry MUST NOT contain any payload content, personal data, or classified information. Only cryptographic hash values and identifiers are permitted.
11.4. 10.4 Anchoring Reference in the Case
When a Case has been anchored, the Case snapshot MUST be updated with an anchorReference field:
{ "anchorReference" : { "ledgerType" : "hyperledger-fabric" , "ledgerEndpoint" : "https://ledger.bern.ch/api/v1" , "txId" : "abc123..." , "anchoredAt" : "2026-05-31T16:00:00Z" } }
12. 11 Access Control (Delegation Framework)
12.1. 11.1 Scope and Principles
RWP deliberately defines no mandatory authorisation model. Data security and information protection are delegated to the implementing systems. This chapter defines an optional, interoperable delegation framework that compliant systems MAY implement.
The core principle: access control in RWP is attribute-based (ABAC) and delegatable. Rights are not assigned to roles in a fixed hierarchy, but expressed as verifiable claims attached to DIDs.
12.2. 11.2 Access Policy Block
A Record snapshot MAY contain an accessPolicy block in the metadata:
{ "accessPolicy" : { "visibility" : "restricted" , "readAccess" : [ "did:rwp:bern.ch:unit-legal" , "did:rwp:bern.ch:unit-planning" ], "writeAccess" : [ "did:rwp:bern.ch:user-petra-muster" ], "delegationAllowed" : true , "expiresAt" : null } }
| Field | Required | Description |
|---|---|---|
visibility
| SHOULD | public, restricted, or confidential
|
readAccess
| MAY | Array of DIDs with read permission |
writeAccess
| MAY | Array of DIDs with write permission (draft state only) |
delegationAllowed
| MAY | Whether listed DIDs may further delegate access |
expiresAt
| MAY | ISO 8601 timestamp after which access policy expires |
12.3. 11.3 Delegation Chain
When delegationAllowed: true, a listed DID MAY issue a signed delegation token granting access to a third DID. The delegation token MUST:
-
be signed by the delegating DID’s verification key
-
reference the original Record DID
-
specify the access level being delegated (
readorwrite) -
include an expiry timestamp
Implementing systems MAY enforce delegation chains and MUST validate delegation token signatures before granting access.
13. 12 Federation
13.1. 12.1 Federation Model
RWP federation is modelled after the DNS architecture: a decentralised, hierarchical namespace model without a single point of failure.
Every RWP namespace (the <namespace> component of a DID, e.g. bern.ch) is operated by the owning organisation. Namespace resolvers are federated: each resolver knows its own namespace and can route resolution requests for unknown namespaces to peer resolvers.
13.2. 12.2 Namespace Registry
The authoritative namespace registry is the subject of a future RWP extension. During the transition phase, the following applies:
-
A namespace SHOULD be formed on the basis of the DNS domain name of the organisation (e.g.
bern.ch,bag.admin.ch,usz.ch) -
DNS ownership of the domain implies namespace authority, no separate registration required during transition
-
Namespace conflicts are resolved by DNS priority
13.3. 12.3 Resolver Requirements
An RWP federation resolver MUST:
-
Accept DID resolution requests of the method
rwp -
Resolve DIDs within its own namespace directly
-
For unknown namespaces: query the DNS TXT record of the namespace domain for the
_rwp-resolverentry -
Cache resolver endpoints with a TTL of at least 300 seconds
-
Return a
404response when neither the DID nor a resolver for its namespace can be found
DNS TXT record format for resolver discovery:
_rwp-resolver.bern.ch. 300 IN TXT "v=rwp1;endpoint=https://resolver.bern.ch/api/v1"
13.4. 12.4 Cross-Namespace Links
A Record in namespace bern.ch MAY contain hard links to Records in other namespaces (e.g. did:rwp:bag.admin.ch:guideline-001).
When resolving cross-namespace links, a system MUST:
-
Extract the namespace component from the linked DID
-
Discover the resolver endpoint via DNS TXT lookup (Section 12.3)
-
Verify the snapshot hash of the linked Record after retrieval
-
Cache the resolved snapshot locally with the original hash for integrity verification
14. 13 Payload Deletion
14.1. 13.1 Deletion Principles
RecordWeb’s immutability and data protection erasure obligations (GDPR, Swiss DSG) are in tension. RWP resolves this tension through a tiered deletion regime.
The applicable regime is declared in the deletionRegime field of the Record’s metadata at the time of creation. Implementations MUST respect this field and MAY enforce it at the infrastructure level.
14.2. 13.2 Deletion Regimes
RWP defines three deletion regimes:
| Regime | Key | Description | When applicable |
|---|---|---|---|
| Payload deletion | payload-only
| The payload is deleted; DID, metadata, and version graph are retained. The Record continues to exist as a provable "empty shell". | Default. Where provenance continuity is legally required despite erasure (e.g. audit trails, public registers). |
| Full deletion | full-delete
| The entire Record (including DID, metadata, and graph edges) is deleted. | Where no retention obligation exists and the right to erasure is absolute (e.g. erroneously captured personal data with no public interest basis). |
| Deletion exemption | exempt
| No deletion occurs. The statutory retention obligation is documented as metadata. | Where a statutory retention obligation overrides the erasure claim (e.g. tax records, notarial acts, public register entries). |
14.3. 13.3 DeletionRecord Protocol
When a payload deletion is executed, a DeletionRecord MUST be created and finalised before the payload is removed.
The DeletionRecord MUST contain:
{ "deletionRecord" : { "targetDid" : "did:rwp:bern.ch:f47ac10b-58cc-4372-a567-0e02b2c3d479" , "targetSnapshotHash" : "sha256:snapshot-to-be-deleted..." , "deletionRegime" : "payload-only" , "legalBasis" : "GDPR Art. 17 — Right to erasure" , "requestedBy" : "did:rwp:bern.ch:user-max-mustermann" , "approvedBy" : "did:rwp:bern.ch:unit-legal" , "deletedAt" : "2026-06-01T10:00:00Z" , "retainedFields" : [ "did" , "metadata" , "versionGraph" ] } }
After the DeletionRecord is finalised:
-
The payload bytes are securely overwritten
-
The
payloadHashfield in the snapshot is replaced with the string"deleted:<DeletionRecord-DID>" -
The DID document’s
currentVersionfield is updated -
The DID resolver MUST return HTTP 410 for
full-deleteregimes
14.4. 13.4 Solid Pod Delivery (optional)
For Records whose primary subject is an individual citizen, RWP offers an optional delivery extension: delivery of finalised Records into a citizen’s Solid Pod.
When a system implements Solid Pod delivery, it MUST:
-
Obtain explicit, revocable write consent from the citizen via their Pod’s access control mechanism
-
Deliver the complete snapshot (DID, metadata, payload) to the Pod
-
Retain the canonical copy in its own RWP system, the Pod copy is an additional output, not a transfer of authority
-
Include a
deliveryTargetfield in the optional metadata extension block
Typical use cases: driving licences, medical images, residence registration confirmations, diplomas, building permits (delivered to the applicant).
The citizen MAY present the Pod-held Record to any third party, who can independently verify its authenticity via the DID and cryptographic hash without querying the issuing authority.
15. 14 Conformance Requirements
15.1. 14.1 Conformance Levels
RWP defines two conformance levels.
| Level | Chapters | Description |
|---|---|---|
| Level 1 — Basic | 2, 3, 4, 5, 6, 7, 9 | Record identity, snapshots, payload, hashing, version graph. Systems fulfilling Level 1 can interoperate with one another. |
| Level 2 — Full | 2–9, 8, 12, 13 | All Level 1 requirements plus Cases, federation, and payload deletion. Systems fulfilling Level 2 can produce complete, provable Cases. |
15.2. 14.2 Level 1 Checklist
A system claiming Level 1 conformance MUST implement:
-
[ ] DID generation per Section 2.2 (
did:rwp:<namespace>:<uuid-v4>) -
[ ] DID document per Section 2.3 with all MUST fields
-
[ ] DID resolver per Section 2.4 (HTTP 200/404/410)
-
[ ] Snapshot structure per Section 3.2 with minimum metadata set (Section 3.3)
-
[ ] Snapshot hash calculation per Section 3.4 (SHA-256 + RFC 8785)
-
[ ] Payload validation before finalisation per Section 4.1
-
[ ] SchemaRecord support per Section 5.2
-
[ ] Core Record types per Section 5.3
-
[ ] State model (draft/finalized) per Section 6.1
-
[ ] Finalisation checks per Section 6.3
-
[ ] Version graph as DAG per Section 7.1 (cycle detection MUST)
-
[ ] SHA-256 exclusively per Section 9.1
-
[ ] RFC 8785 canonical JSON per Section 9.2
15.3. 14.3 Level 2 Checklist
A system claiming Level 2 conformance MUST additionally implement:
-
[ ] CaseRecord per Chapter 8 (trigger, context, process, decision, result)
-
[ ] Hard and soft link distinction per Section 8.4
-
[ ] Merkle root calculation per Section 8.5 and 9.5
-
[ ] Case completeness check per Section 8.6
-
[ ] Federation resolver per Section 12.3 (DNS TXT lookup)
-
[ ] Cross-namespace link resolution per Section 12.4
-
[ ] Deletion regime declaration per Section 13.1
-
[ ] DeletionRecord protocol per Section 13.3
16. Annex A: Normative JSON Schemas
16.1. A.1 Snapshot Metadata Schema
{ "$schema" : "https://json-schema.org/draft/2020-12/schema" , "$id" : "https://recordweb.github.io/rwp/schemas/snapshot-metadata.json" , "title" : "RWP Snapshot Metadata" , "type" : "object" , "required" : [ "did" , "snapshotHash" , "recordType" , "schemaVersion" , "state" , "created" , "owner" , "parents" , "payloadHash" , "payloadFormat" ], "properties" : { "did" : { "type" : "string" , "pattern" : "^did:rwp:[^:]+:[^:]+$" }, "snapshotHash" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" }, "recordType" : { "type" : "string" , "pattern" : "^did:rwp:" }, "schemaVersion" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" }, "state" : { "type" : "string" , "enum" : [ "draft" , "finalized" ] }, "created" : { "type" : "string" , "format" : "date-time" }, "finalized" : { "type" : "string" , "format" : "date-time" }, "owner" : { "type" : "string" , "pattern" : "^did:rwp:" }, "parents" : { "type" : "array" , "items" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" } }, "classification" : { "type" : "string" }, "retentionPolicy" : { "type" : "string" , "pattern" : "^did:rwp:" }, "tags" : { "type" : "array" , "items" : { "type" : "string" } }, "accessPolicy" : { "type" : [ "object" , "null" ] }, "payloadHash" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" }, "payloadFormat" : { "type" : "string" }, "signature" : { "type" : "string" }, "deletionRegime" : { "type" : "string" , "enum" : [ "payload-only" , "full-delete" , "exempt" ] } } }
16.2. A.2 CaseRecord Payload Schema
{ "$schema" : "https://json-schema.org/draft/2020-12/schema" , "$id" : "https://recordweb.github.io/rwp/schemas/case-record.json" , "title" : "RWP CaseRecord Payload" , "type" : "object" , "required" : [ "caseId" , "caseType" , "title" , "trigger" , "merkleRoot" ], "properties" : { "caseId" : { "type" : "string" , "pattern" : "^did:rwp:" }, "caseType" : { "type" : "string" , "pattern" : "^did:rwp:" }, "title" : { "type" : "string" }, "trigger" : { "$ref" : "#/$defs/hardLink" }, "context" : { "type" : "array" , "items" : { "$ref" : "#/$defs/link" } }, "process" : { "type" : "array" , "items" : { "$ref" : "#/$defs/link" } }, "decision" : { "oneOf" : [{ "$ref" : "#/$defs/hardLink" }, { "type" : "null" }] }, "result" : { "type" : "array" , "items" : { "$ref" : "#/$defs/hardLink" } }, "merkleRoot" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" } }, "$defs" : { "hardLink" : { "type" : "object" , "required" : [ "type" , "recordDid" , "snapshotHash" ], "properties" : { "type" : { "const" : "hard" }, "recordDid" : { "type" : "string" , "pattern" : "^did:rwp:" }, "snapshotHash" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" }, "role" : { "type" : "string" } } }, "link" : { "type" : "object" , "required" : [ "type" , "recordDid" ], "properties" : { "type" : { "type" : "string" , "enum" : [ "hard" , "working" ] }, "recordDid" : { "type" : "string" , "pattern" : "^did:rwp:" }, "snapshotHash" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" }, "role" : { "type" : "string" } } } } }
16.3. A.3 DeletionRecord Payload Schema
{ "$schema" : "https://json-schema.org/draft/2020-12/schema" , "$id" : "https://recordweb.github.io/rwp/schemas/deletion-record.json" , "title" : "RWP DeletionRecord Payload" , "type" : "object" , "required" : [ "deletionRecord" ], "properties" : { "deletionRecord" : { "type" : "object" , "required" : [ "targetDid" , "targetSnapshotHash" , "deletionRegime" , "legalBasis" , "requestedBy" , "approvedBy" , "deletedAt" ], "properties" : { "targetDid" : { "type" : "string" , "pattern" : "^did:rwp:" }, "targetSnapshotHash" : { "type" : "string" , "pattern" : "^sha256:[0-9a-f]{64}$" }, "deletionRegime" : { "type" : "string" , "enum" : [ "payload-only" , "full-delete" , "exempt" ] }, "legalBasis" : { "type" : "string" }, "requestedBy" : { "type" : "string" , "pattern" : "^did:rwp:" }, "approvedBy" : { "type" : "string" , "pattern" : "^did:rwp:" }, "deletedAt" : { "type" : "string" , "format" : "date-time" }, "retainedFields" : { "type" : "array" , "items" : { "type" : "string" } } } } } }
17. Annex B: Reference Implementation Notes (non-normative)
This annex is non-normative. It provides guidance for implementers and does not impose additional requirements.
17.1. B.1 Technology Choices
The following technology choices are recommended for a first RWP implementation:
| Component | Recommended | Notes |
|---|---|---|
| DID method | did:web or did:key during pilot
| Full did:rwp method requires resolver deployment
|
| Storage | PostgreSQL + object store (S3-compatible) | Metadata in relational DB; payloads in object store |
| Hashing | Node.js crypto.createHash('sha256')
| Standard library; no external dependency |
| Canonical JSON | canonicalize npm package
| Implements RFC 8785 |
| Signatures | @noble/ed25519
| Audited Ed25519 implementation |
| Schema validation | AJV (JSON Schema validator) | Supports JSON Schema 2020-12 |
17.2. B.2 Pilot Scope Recommendation
For a first pilot, the following Level 1 subset is recommended:
-
Implement DID generation and DID document (Sections 2.2–2.3)
-
Implement snapshot creation with minimum metadata set (Section 3.3)
-
Implement SHA-256 snapshot hash calculation (Section 3.4)
-
Implement draft → finalized state transition with schema validation (Section 6.3)
-
Implement version graph storage (Section 7.1) — without branch detection initially
This covers the critical path: a Record can be created, finalised, and its integrity verified. Cases and federation can be added in a subsequent iteration.