White paper

Brickchain White Paper

As explained in the Introduction to Integrity, Brickchain is made of three main components. Here, we will explore these from a technical standpoint.


Integrity is an existing implementation of the protocol so we will use it as an example in this document, in order to illustrate what can be built with Brickchain. However, please keep in mind that any other implementation remains possible.

Overview of the Integrity App

The mobile app is the remote control for every use of the Integrity Platform. It also serves as a storage for all the users’ data: keys (“My profile”), receipts, facts, mandates, etc.

The app offers views for service discovery, setting context depending on nearby realms, functions for scanning any Integrity document (mandates, action descriptors and so on), performing actions, and managing the users log (receipts etc.) and documents.

If you are the admin of a realm or controller you can use these mandates to enter the dedicated administration pages. However, none of this functionality happens locally: it is loaded as web views in the app, without users noticing that another host is loading the content. If the controller’s design follows the standard look, the users experience a native integration within the app.

When performing an action, a UI for adding extra parameters that are needed to configure the action (such as the required dates for a booking service) can also be loaded from the controller.

If the receipt from an action contains a reference to a UI, the receipt can be rendered with a custom view from the controller as well. This is useful if the user wants to cancel or change the details afterwards.


Key formats and principles

All keys on the Integrity Platform are JSON Web Keys (JWK), and all signatures are JSON Web Signatures (JWS).

JWK has been produced through an IETF standardization effort (JOSE, JSON Object Signing and Encryption) and published as a series of RFCs (most notably RFC7515). Integrity currently uses ECDSA P-256 and SHA-256 for cryptographic operations. Using a standard framework such as JOSE makes it easy to be cryptographically agile when an algorithm shows signs of weaknesses.

Any key or document that an Integrity app user wants to validate has to be signed by a party which is already trusted by the app. Initially, it all starts with the Integrity infastructure realm key. The full chain of signatures must validate successfully to accept any incoming signature.

Users join the infrastructure realm by default

When the Integrity app first starts up, users implicitly receive a guest mandate from the infrastructure realm, and its keys are trusted by default. This operation gives access to basic information about the platform, and one of its services offers the possibility to create a new realm. The user can also access the KYC service offered by the infrastructure realm, making it possible to receive signed facts such as a verified e-mail address and phone number.

Sharing a mandate to a user from a realm published on the Integrity Platform, makes it implicit that any new Integrity app user can trust the documents this organization will issue. Any user that encounters a new realm on the Integrity Platform, can validate the realm key from the .well-known file from the domain it belongs to, and discover its public services.

Users will trust any service from a realm that they have been connected to (by receiving a mandate). But any service that the users will interact with, will not have the same view on trust. Most services will be configured to anchor the trust with our infrastructure realm (i.e. use the root key for our infrastructure as default). However, it will be easy for any service to configure the keys to any realm the operator wants, in order to serve a more isolated community of users.

However, in most cases trust will be delegated in the system by using our model of certificate chains. The way it works it that any actor on our platform can issue a certificate. The certificate is a document, and multiple certificates can be attached to a document as an array. The certificate mimics some of the mechanics in X.509. The issuer of the certificate gets a certificate request from the subject, and performs some validation (this process is out of scope for this document). In return of a validated certificate request, the requestor receives a signed certificate document (JWS).

The content of a certificate is among other data, a date of issue, a realm identifier, and a certificate (if it is part of a longer certificate chain). Outside of its standard base class, the certificate has a TTL (end of life), the issuer (as JWK), and the subject (as JWK). To limit the use of further delegations of the certificate, each certificate has a limit on what roles and document types that can be signed using the certificate. Any subsequently issued certificate cannot extend this use, only issue further limits on any issued certificate.

Validating a signed document that includes a certificate follows this process:

  1. Validate the signature over the original document - extract key level
  2. Extract the certificate from the document
    2.1. If the original document type does not match any of the valid document types from the certificate: fail
    2.2. If the document type is a mandate, match the role from the mandate with the valid roles from the certificate. If there is no match: fail
    2.3. If the certificate includes another certificate, validate the signature over the certificate and extract the Key Level. If the Key Level is the higher than the Key Level in the current certificate: fail
    2.4. If the certificate includes another certificate (creating a certificate chain), repeat step 2 for that certificate.
  3. If the iterations of validating the certifications success, and the service trust the signature over the last certificate, the process of validating the whole certificate chain has succeeded.

Key generation process details

  • ROOT = always for document decryption
  • APP KEY is not used for document decryption, but just for signing

Step 1: Root keypair generation

When the Integrity app is run the first time it generates a root keypair. The private part is protected by a user defined PIN code or a biometric fingerprint/face ID, and the public part is a thumbprint of the JWK, which is a SHA-256 of the key. This ID is what uniquely identifies the user throughout the Integrity Platform.

Step 2: App keypair generation

When the root is set up and secured, the app goes on by generating an app keypair. The app key is signed by the root key and protected by the same pin code as the user root key. It is only used for document signing while the root key is used for signing new user keys and for every document decryption.

Step 3: Device keypair generation

A third key called device is also generated and signed by the user root key. It is not protected by any PIN code and is used for document signing and decryption when performing low value operations, such as registering a device for push notifications.

Some actions may be flagged as low value operations, in order to allow pinless actions to be performed in the background. For example: booking a conference room by simply scanning an NFC tag.

Key generation summary

This schema shows how the app generates the three key pairs that every new user needs.

Created with Raphaël 2.2.0appappuseruserEnter PIN codePINEnable fingerprint?YesTouch fingerprint sensor(touches sensor)Store PIN in secure storageGenerate ‘root’ keypairEncrypt ‘root’ keypairStore encrypted ‘root’ keypairGenerate ‘app’ keypairEncrypt ‘app’ keypairSign ‘app’ keypair with ‘root’ keyStore encrypted ‘app’ keypair with cert chainGenerate ‘device’ keypairSign ‘device’ keypair with ‘root’ keyStore unencrypted ‘device’ keypair with cert chain

Key storage

All the keys generated by the app are stored in a secure storage which is device specific. On iOS the data is stored in the system Keychain. On Android it uses the Keystore which may be hardware or software based, depending on the device.

Managing Keys Backup

Keys backup can be done in multiple ways, either using paper keys for offline storage, or encrypted using a strong password and PBKDF2 (or bcrypt, possibly Argon2) in a storage considered safe by the users.

By relying on the users’ friends, it should also be possible to construct a split key and share it among some trusted parties using Shamir’s Secret Sharing. When the secure backup of the key has been done, a backup for the rest of the encrypted user data can be done in a decentralized storage, thanks to the user’s public key.

Managing Key Revocations

Since a user stores all the documents in the phone, and nobody other that the user himself can remove this information, a revocation of a document has to be done using a separate revocation service.

When a document needs to be revoked, the issuer will create a revocation document with the checksum of the signature of the original issued document.

Note that the current Revocation Service can only revoke signatures. Revoking keys is currently not an option.

To ensure privacy, the Integrity Platform does not rely on a centralized key revocation system, where the lookups would reveal which services are being used by who.

Therefore, revocations get published on a Blockchain. The irreversibility of the operation ensures that the data can’t be tampered, and the services can carry out verifications anonymously, with no central operator overseeing their query.

To avoid leaking any metadata to a controller, a local blockchain may even be used.



A Revocation Service expects a Revocation Request document that itself contains two Integrity protocol documents:

  1. the original JWS containing the signature that needs to be revoked
  2. a Revocation Checksum document that contains a multihash of the document and the signature combined.

Both documents are signed as a JWS, and are contained in a Revocation Request document that is also signed as a JWS.

RevocationRequest+Signature(JWS) contains:

  • JWS(Original Document)
  • JWS(Multihash of the Original Document checksum + the signature)
  • Priority (a number)

When the revocation service receives a RevocationRequest, it checks the signature of the RevocationRequest (and gets keyid1: ki1). Then it checks the signature of the Original Document (ki2), and the signature of the Multihash (ki3). If ki1ki2ki3, and the multihash from the Revocation Checksum document matches the hash of the Original Document, the revocation is ok. Otherwise an error message is returned.

If the revocation is ok, a Revocation document is published. The Revocation document contains the JWS(Multihash of the checksum of the JWS(Original Document) + the signature to be revoked) and is covered by an additional signature from the Revocation Service:


  • JWS(Multihash of the original document Revocation Checksum)

The Revocation Service signs and publishes the Revocation document to a blockchain. The key to the document is the original Multihash in the Revocation Checksum document.

Serving the Revocation is done through the following mechanisms:

  • The Revocation service subscribes to the blockchain, and serves the Revocation document on request from any party through a REST API. The query is for the multihash, and the response is either a HTTP 404 message, or the Revocation document.

  • The Revocation Service publishes the Revocation document on a Blockchain. Depending on the priority of the Revocation Request, it may push the publication immediately, or delay it until a certain amount of Revocation documents has been collected or after a certain time has passed.

Created with Raphaël 2.2.0RealmRealmRevocationServiceRevocationServiceBlockchainBlockchainSelect signatureand documentfor revocationCreate Revocation RequestSend Revocation RequestValidate Revocation RequestCreate RevocationPublish RevocationCreate ReceiptSend Receipt

Client lookups

Services focusing on privacy can use a local Revocation Service with read-only access, and the blockchain node in direct connection to the Revocation Service.

Traditional PKI structures query a central revocation service for every request, but such systems leak user behaviour. Using a blockchain for revocations removes this privacy pain point.

Once the client receives a Revocation document, it has to validate the content. The following process is used. The client (typically a service):

  1. Gets a JWS from someone, and needs to check its validity.
  2. Makes a Multihash of the JWS with SHA-256, the currently recommended hash algorithm of the signature.
  3. Queries any of the Revocation Service for the Multihash
  4. Receives the answer, if 404 the JWS is ok.
  5. If the client receives a Revocation document back, it checks its signature and compares it to the whitelist of valid Revocation Services keys.
  6. If the signature is not valid, the reply is dicarded.
  7. If the signature is valid, retrieves the Checksum from the Revocation document.
  8. Decodes the Multihash Checksum, and compare it with the signature of the original JWS.
  9. If the signatures matches, the document is to be considered invalid.
Created with Raphaël 2.2.0ClientClientServiceServiceRevocationServiceRevocationServiceCacheCacheBlockchainBlockchainActionRevocation Lookupon Signature(s)LookupResponseResponseMatch any Action JWS?Perform or allow ActionResponseSubscribe

Revocation Cache

The Revocation Service subscribes to the blockchain in order to catch any new published revocations, and store them in the Cache.

Most original JWS documents have an expire date. If this latter has passed, the cache entry is invalidated.

Risk management of hashing algorithms

The current recommended hashing algorithm is SHA-256. If over time there any sign of weakness is discovered for this algorithm, a slow rollover to a new standard will happen.

The Revocation Checksum cannot change, so over a long time any service that needs to validate an incoming transaction may need to perform the double number of lookups for a revocation, until the rollover to the new algorithm has been completed.

Priority handling

The Priority field in the Revocation Request handles the urgency of the publication to the blockchain. However, such an option may eventually incur a cost for the client. Handling paid transactions is currently not an option, but it is a potential additional layer to the Brickchain protocol stack.

Handling multiple Revocation Services

A service or realm that does not wish to leak data to any external party may deploy a local read-only Revocation Service that subscribes to the blockchain. The service can then be configured to use this local option by default.

For Services that want to be able to handle Revocation Requests and then publish official Revocations on the platform, the requirements are higher. The Revocation Service needs an official key to sign new revocations with. An audit program is available for organizations who wish do deploy their own Service. Please get in touch via contact@integrity.app if you are interested in such a process. All official Revocation Services will get published in a keylist of services to trust.


Step 1: Document Discovery

Discovery of realms and services in the Integrity app can happen in several ways. Users can:

  • Tap any URL (from chat, e-mail, etc.) on their device to trigger the Integrity app.
  • Scan a QR code or NFC tag to activate a link.
  • receive a push notification that triggers the document handler.
  • Carry out a geographical lookup. The app can perform a query with its location as parameters and receive back a list of nearby realms and their services, and present them by relevance to the user.
  • Rely on mDNS broadcasts from the same wifi network that the device is attached to: very useful for private local realms and services.

By default, the Integrity app handles every URL that begins with https://integrity.app/. If the app is not installed on the phone, a webpage is loaded from the browser and users are prompted to visit their app store to carry out the installation. This method is called deep linking.

Note that when users scan a QR code or NFC tag, the trigger contains either a URL or the entire document itself.

Step 3: Handling the document itself

The app accesses the document and passes it to its document handler module. It has now retrieved the document and decrypts the contents if needed. The signatures are validated, before being passed to the the correct handler, depending on the document type. The handler output is then be returned to the app or view that initiated the URL or scan.

The case of Webviews for document handling

When users trigger an action descriptor, the full process may require additional parameters.

For example, to book a conference room, users must be able to choose a time and duration. For these configurations, the Integrity app relies on webviews.

If the action descriptor contains a UI URI, it will be loaded. Optionally the UI data can be inlined in the action descriptor and set statically in the webview.

The webview is sandboxed and has a very limited interface compared to the native functions of the app.

A loaded webview has the following lifecycle:

  1. Instantiation and reception of the UI URI to load

  2. URI loaded and made visible to the user

  3. The app calls the init function in the webview passing in the mandate and params from the action descriptor.

  4. The app polls the poll function to see if the the webview is done.

    • If the poll function returns a value the webview is closed and the result is used to complete the action.

    • If a cancel flag is returned the webview is closed and the action is cancelled.

    • If a null value is returned the app continues the next step.

  5. The app polls the handle function to see if there are documents that need to be handled by native functions. If a document is returned it is passed to the document handler which processes is and passes back the result to the webview via the result or error functions.

  6. After a small delay the app repeats the polling process.

Storing documents as Facts, Mandates & Receipts

When performing actions or handling documents, certain returned documents are stored by the app:

  • Facts (signed verifiable claims)
  • Mandates (entitle users to perform actions)
  • Receipts (results of such actions)

These documents are stored in a simple local SQL database. Users manage facts and mandates from their profile page in the app.

As they are documents, receipts can trigger webviews if a URI is specified. This enables users to modify past actions by performing new operations within the webview. For example, users may cancel a room booking by tapping on the receipt.

Receipts as a logbook

All receipts gathered by the app are stored in a log and listed in chronological order, based on the time for the planned action to be carried out by the user. The idea here is to present the information in a user-friendly way.

For example, room booking receipts are timestamped upon creation but presented in the logbook at the time on which the room is booked. The intent of this listing model is to make it easy for the user to find and manage current and future events.


What a realm is

A realm is an independent organizational structure that manages users and services. An organization typically sets up one realm for all its users. Whether these are employees, collaborators, consumers or temporary guests does not matter in the first place, as users’ roles are defined by the mandates they receive.

To be able to issue mandates, it is up to the realm administrator to define the roles scopes and purposes.

Mandates may be adapted to support all kinds of constraints. On top of offering any kind of permission, their validity can be limited in time or by the number of uses.

In essence, a realm is a web based application with a backend offering a simpler storage for its configuration. As long as the realm can list controllers offering services and issue mandates while providing a realm descriptor, it is usable from the Integrity app.

Controllers and services

A realm exposes an API that a controller uses for binding its services, and some metadata such as the API endpoints for the controller. It can also list the current set of services (controllers) and their endpoints to any client asking for them, as a discovery mechanism.

There is no guarantee that a controller can communicate over the network directly with a realm or vice versa, since these two functions can be separated by networks that prohibit direct connections. So all communications between the controller and the realm happen through the Integrity app (or the web interface).

When an admin manages the controller belonging to the realm, the admin device acts as the proxy for all communication necessary to make any change of configuration in either the realm or the controller.

Binding a Controller to a Realm

The controller will initially be functional enough to present a document called the Controller Descriptor which contains:

  1. the name of the controller (Label)
  2. an ActionsURI
  3. an AdminURI
  4. a BindURI
  5. the key & key purposes

The realm uses the Controller Descriptor when initializing a binding with the controller.

Using the Integrity app, the realm gets the controller descriptor, and posts back (again through the app) a controller binding document to the controller:

Created with Raphaël 2.2.0RealmRealmAppAppControllerControllerGet Controller DescriptorReview and accept Key PurposesPost Controller DescriptorReturn Controller Bindingdocument with signed cert chainPost Controller Binding

The Controller Binding document contains:

  1. The realm descriptor (same as the .well-known document published by the realm)
  2. The list of admin roles that are allowed to manage this controller
  3. A certificate chain that is a signed proof that the controller key is allowed to sign documents on behalf of the realm
  4. A service mandate that is used by the controller to interact with the realm or other services (e.g. in the case of meta-services, where a controller can in turn trigger several other actions.)

When the binding is done, the controller can perform services on behalf of the realm. However, to be fully functional, the controller should probably be configured to use other roles without permissions to manage the controller, and connect to any third party service it might make use of. So the last step is for the realm administrator to enter the administration interface of the controller, and perform necessary configuration.

A controller can be bound to many realms, in which case the procedure above has to be repeated for every realm.

Controller Admin interface Single-Sign-On

The realm admin UI displays a collection of links to the controller admin interfaces. When following one of these, a SSO token is generated in the realm, in order to be verified by the controller and automatically authenticate the user.

The token also contains an API token that the controller administration interface can use to make limited API calls to the realm for operations, such as looking up roles and publishing action descriptors.

Publishing action descriptors from a controller

The administration interface of the controller will publish the action descriptors to the realm after having added new actions or modified the existing ones.

Created with Raphaël 2.2.0RealmRealmAdminUIAdminUIControllerControllerCreate new resourceGet action descriptorsPost action descriptors

The realm can then announce these services through its service listing functions.

Meta services

From simple controllers that perform actions such as unlocking a door or booking a resource, you can construct more complex services.

An example of this would be a service to organize meetings where you want to: book a room, send out invites, manage access to the premises, and order food.

To accomplish this more complex task you can construct a controller that binds to all the necessary services. This meta service has a controller that inherits mandates to create actions on other controllers.


Integrity Connect as an Identity Provider

An Integrity identity can be used to log into other services by using an Integrity IDP. Its advantage over other similar solutions is that it will not contain any user information such as name, e-mail address, or even password.

An Integrity IDP is configured to trust one or more realms (with KYC services) and their public keys. This makes it a lot less vulnerable to huge data leaks than centralized services, which is an enormous benefit.

OpenID Connect, which is a simple identity layer on top of the OAuth 2.0 standard, is already implemented. Any Relying Party (or “RP”, i.e. an external service provider) that wants to use an Integrity IDP as their authentication system can choose to run the IDP themselves, or pick any existing IDP and add it to their login system.

A Relying Party decides what Facts are needed from the Integrity IDP in order to log in.

A Fact can be any kind of claim that the user has issued. It goes from uncertain information such as a self-signed geographical position, to verified identity claims based on official documents (passport, national ID, etc.) that got certified by a legit KYC service. In between, lies all the internet user regular info such verified email addresses or social media accounts (Facebook, Twitter, Github etc.).

Certifications and Certification Chains

Within Integrity, the Services and Realms that are created may have different levels of trust, which can be used as curation parameters.

For example, non-trusted Realms can be filtered out from the discovery feed. And on the contrary, third parties such as legit Know Your Customer (KYC) or Revocation Services may benefit from a special endorsement that allows them to be trusted upfront, throughout the Integrity platform.


All document types in the Brickchain architecture are composed of JSON-based (RFC 7159) data structures with JSON Web Signatures (RFC 7515) (JWS), and encryption using JSON Web Encryption (RFC 7516) (JWE).

All the documents described here are further documented on our developer site. What you see there is an HTML rendering of the JSON schemas describing the documents. Each document type has a link to the JSON schema representation of the document. All the schemas are also available on github.com/brickchain/json-schemas. However, the $id of the documents are prefixed with schema.brickchain.com and the first path is the version number (i.e. https://schema.brickchain.com/v2/example.json).

The document types described here represent API calls. However, the complete documents are involved in the transactions, and the documents are either signed, encrypted, or both. The receiving party validates the document and its signature upon receiving it, and then handles the validated document with its respective handler. They can also be part of multipart document, or a document can be an attachment within another document and creating a document hierarchy where each part is independently signed.

The API endpoints are defined by the controllers and the realms separately, and therefore there is no pure REST API documentation available.

The example document types described in this whitepaper serve as an illustration of the mechanics of the protocol. A more complete Brickchain documentation will be published as a separate document.

All Integrity document types are based on the Integrity Base document type.

base {
    @type        string
    @timestamp   string:date-time
    @id          string
    @certificate string
    @realm       string

In the Brickchain context, theType is always set to “https://schema.brickchain.com/v2” with the rest of the path points to the document type. A realm descriptor document thus has its type set to “https://schema.brickchain.com/v2/realm-descriptor.json”. Any document sub type is added using the fragment identifier (“#”) of the URI.


The certificate document type is an integral part of the Brickchain infrastructure. A signed certificate document can be part of a the base documents, and as such, all Brickchain documents can be certified.

The properties of the certificate schema:

certificate {
    ttl           integer
    issuer        object:jose.JsonWebKey
    subject       object:jose.JsonWebKey
    documentTypes []string
    keylevel      integer
    documentTypes []string
    allowedRules  []string

Realm Descriptor

A realm descriptor is announced through a domain name, placed in the .well-known directory of an HTTP server. The realm descriptor contains a general description and properties of the realm, but most importantly it contains the public key identifying the realm.

The properties of the realm descriptor:

realm-descriptor {
    label       string
    publicKey   object:jose.JsonWebKey
    inviteURL   string
    servicesURL string
    icon        string
    banner      string

The realm descriptor document itself is signed by the realms current key. Thus, this is the root of all trust on all the documents that are signed by the realm and its services.

Since the base document includes the base document, it also has support for certificate chains. A realm can be certified by another party to issue a certain type of documents, to gain better trust in a wider system.

Facts and Scope Requests

A fact can be issued by any actor using the Integrity platform. It can even be issued by the users themselves who may self-sign claims stating their names or geographical locations.

However, to be leveraged in a wider ecosystem, a fact can be signed by a trusted entity known for issuing verifiable claims about users (e.g. a KYC service). Trust can then be distributed by using certificate chains.

Facts can be shared to controllers by the user, when a service requires them for certain actions: i.e. a name may be required when using a room booking service. With the Integrity App a user can always choose which exact facts to share. On the other side, in order to receive a mandate from a realm, the user may also be required to share some specific facts.

fact {
    ttl       integer
    issuer    string
    label     string
    recipient object:jose.JsonWebKey

Asking the users for facts is done through a scope request which includes a list of the wanted scopes (i.e. which facts are needed) to get from the user. Since the fact is issued directly for the receipient, only the user having access to that specific key can use this fact, as the service will look at the users signature over the fact before allowing it to be used.

scope-request {
    replyTo   []string
    scopes    []object:scope
    keyLevel  integer
    contract  string


A mandate is the document that grants users the authorization to perform actions on the Integrity Platform. A mandate itself does not contain any information about which services it can be used for. The role is the important field of this document.

mandate {
    role          string
    roleName      string
    validFrom     string:date-time
    validUntil    string:date-time
    recipient     string:jose.JsonWebKey
    sender        string
    params        map[string]string

The recipient is the same as in the fact, the user the mandate is meant for. If the mandate is received by a service without the corresponding signature from the users private key, it is useless.

The parameter list is a set of predefined parameters within the mandate. The mandate can be set to a specific parameter. Any changes to that parameter will not be allowed by the UI of the service, nor the receiving controller. However, any other needed parameters to perform the action still needs to be configured by a UI. This, in conjunction with validFrom and validTo can set very specific limitations on what the user can do, and when. If further granularity is needed in time, such as specific intervals during a day, those can also be defined by a service by using the params list.

Action and Action Descriptor

An Action Descriptor is published by the realm and its controllers.

Publishing of the descriptor can be done in any way possible, through mDNS or through a tag - when the Integrity app is triggered by an action descriptor the user is typically redirected to the ActionURI. Or if a UIURI is in the descriptor, the app opens this URI in a web view so that the user can configure the action. If the action descriptor is already populated with all parameters needed for the action to be triggered, the UI might redirect the app directly to the controller and perform the action immediately.

If the service needs any facts from the user to allow the usage of a service, the list of scopes is needed to put in the action document as a list of Facts.

actiondescriptor {
    label      string
    roles      []string
    uiURI      string
    actionURI  string
    refreshURI string
    params     map[string]string
    scopes     []scope
    icon       string
    keyLevel   integer
    internal   boolean
    contract   object:contract

In order to avoid replay attacks, the client needs to generate a random string (the nonce) for each request. If possible, this nonce should not be reused at all. However, a controller has a limited cache, so this is just to be safe from replay attacks within a limited time window. Since the action is configured at a specific time, it has quite a short life span anyways (looking at @timestamp in the base document).

When the app and the UI have collected all the parameters needed to perform the action, a contract can be displayed to the user, that then signs the action document and posts it to the controller.

action {
    mandates []string
    nonce    string
    params   map[string]string
    facts    []Part
    contract string

Depending on the result of the action, the user receives one or more documents, typically a Message or a Receipt. If more than one document is sent by the Controller it will be a Multipart document.

Messages and Receipts

A message is a simple document sent to a user to display the result of an action. A message is used if there is no need to give the user a receipt, for example if there is an error or a non-important action.

message {
    title   string
    message string

A receipt is a bit more complex, since it also includes the action (the JSON encoded original document that triggered the action) and any intervals (future dates) that the receipt is valid for. The intervals are also used to represent the receipt as future events in the users logbook in the app.

    action    string
    uri       string
    jwt       string
    intervals []interval
    label     string

The URI in the receipt is pointing to a UI that can be used by the app to present a user friendly layout of the receipt content, but it could also be used to update a calendar entry, for example.

Controller Descriptor and Controller Binding

The Controller Descriptor describes the controller and lists the URIs for the API endpoints. The bindURI is the URI of what address the realm should talk to when creating the binding between the realm and the Controller, using a “Controller Binding” document.

The adminURI is the administration web interface for the controller, and the actionsURI is the address to get a list of action descriptors created by this controller.

The label is the controller’s name and the key is the public key of the controller.

Key Purposes is the information on what permissions the Controllers key wants. This will be added to the certificate chain.

controller-descriptor {
    label              string
    actionsURI         string
    adminUI            string
    bindURI            string
    key                object:jose.JsonWebKey
    KeyPurposes        []keyPurpose
    requireSetup       boolean
    addBindingEndpoint string
    icon               string

The Controller Descriptor is used by the realm to initialize a binding with the Controller. The realm posts back the Controller Binding document to the controller.

controller-binding {
    realmDescriptor       object
    adminRoles            []string
    controllerCertificate string
    mandates              []string

The controller binding document contains the realm descriptor document (which is the same as the .well-known document published by the realm), the list of admin roles, and a certificate which is the signed proof that allows the controller to sign other documents on behalf of the realm, and a service mandate that is used by the controller to interact with the realm or other controllers.