Link Search Menu Expand Document

The tsrct doc specification

The tsrct doc (called tdoc) is the foundational construct of tsrct, it is the means by which all information is transacted on the platform.

The tsrct doc is a plain text document that encodes information in base64 and has 3 sections separated by a .

The format of a tsrct doc is header.body.signature

  • The header is a base64 encoded JSON object with defined fields
  • The body is base64 encoded content which is the actual content being digitally signed; it can be of any type (binary or text), and the base64 encoding is used to manage it as text
  • The signature is a base64 encoded byte string that represents the private key signed signature of the header.body combination

example

Here is a published document that is represented by an immutable uri:

https://tsrct.io/9000990009900099000990009.20240220115508-9666bc1f8cb7f5d3 when you click on the “view tdoc” button on the page, you’ll see the following base64 encoded document (line breaks and color added for clarity):


eyJhbGciOiJSUzI1NiIsImNscyI6ImRvYyIsInR5cCI6InRleHQiLCJpdHMiOiIyMDI0LTAy
LTIwVDE2OjU1OjA5WiIsImxlbiI6MzEsInVpZCI6IjkwMDA5OTAwMDk5MDAwOTkwMDA5OTAw
MDkuMjAyNDAyMjAxMTU1MDgtOTY2NmJjMWY4Y2I3ZjVkMyIsInNyYyI6IjkwMDA5OTAwMDk5
MDAwOTkwMDA5OTAwMDkiLCJzaWciOiJObERYX0hiSFJiNng3aTRwTXBKTVpVc3ZhNlVJeGV5
NHFqUHlGdnJaNU9pRjFybHhWSk9PYVl1VUxNUFNfQW5yVVludmJUZ1ctU04ybUtRTWhhSnJH
Ri14eUFjalZqSDE2UVhfaFdVS2lsTXBybzA3TFY2TVZ3ZF8zcEhzTHFFYUtYbWpMVTQtWlA2
eFVFSFZTU1hfMnlGaWFZMFlWSXRnN3BUSm5DSWtIcU9uTWZoa2Q5d1VGQ01rYVpXZGJSY3dn
bGUwZmVKbS1JYV9Eb1UwTnBKbDdZOE16UlRta0R2cGFSRjNjRGhtT2dZY0k0aVMycUFTcHc1
eHhBVHE4S0NvNzJ2ZzZKVjhhbzJpTV9UZlBXS0xQSDNRdFdXNUR1alo2YUNfWmlScjRzN2NR
d3RMbnpBeFFOY2o5X2JJY2hnLVc4V05mUk0zTjV4N041dVFMUkEwU0EiLCJzaGEiOiI5aGl0
OXJxOHFlZWZGUTY5dzdERFowbUVtNlRDWnNjaXg5aGVneWlCeWNnIiwiY3R5IjoidGV4dC9w
bGFpbiIsImFjbCI6ImFjbF9wdWIiLCJsc3QiOnRydWUsInJlZiI6W3sidWlkIjoiOTAwMDk5
MDAwOTkwMDA5OTAwMDk5MDAwOSIsInNoYSI6ImxYUEZ1bkdwb3JmeDYydkROR00ySExnNVVu
cmRYdlA4elpsY2RZOWNrcTQiLCJzaWciOiJrMWFoR2VwY19tdXZYRU9zZ1k5Wm1rWDI4dmIw
MGhfSHlOVV85MlduUzJUNmhFLVNXY2xkYlpKVzNuc1Vvc09ucUtWY2RaaGdCT01mQkVSV2p6
OHZIeFFabURocEFjZmVJZlZ6aXR0NmFpWnN3ZFIwSzB3ajFWcndMdHNvOHlubmxnM3hDSFBE
UmdxWjJKSTZSVTRzQnR2VWhQR2NkRGlTR3poOUpJUU9iSzVZMDhZVHpfZGlQTld0QUhIVV90
M0NrdkltMFptY1NqZzFnZGlHR2ZDZE4tZldsZjBLQXdyeklockFESkZGTlYxaGUyazZqQ01v
Vm1NTWhJd2ZWb0RCNG5oaGw5SVN3Q3ZMczUzemxZanc4bUxXV2R4aHRrRWxXTmNVSW9qVGVw
ZndBYnUxZXZSY0hIMGUtUWZueTdJbHkzTHZiR1hSczIzZlduaUlHQ0w3VVEiLCJ0ZHMiOiJs
LVBUSDdhUTFjTWE2b0ZERGhNUk4weXQ3RDBmS0RTeERBbzM3SGtNbHZ3TDZhZWh0WTBVVFEx
ektqX3BZdGJZemJad2lfUDhENUYzbmtSRkc2Q2tlbTYzQm0yckdnX1lFaHdld0ViUWxKYXZo
MDNpTERfY1hSbktWLVMtUlFnM1J0LW9VazRBSXJaT0I1Yjlmam1NWUpOVlFmV3JDakFFTnpu
cGZzR2M4dnJERGZSQUZtOTVMN09zQzlUUElGRHNJZXRsdVpvYlppMnNfbDZxbU5TS2VISmFa
UjFJX2tTeXBHYmZtTUZrOTFJckhkSHlkU1V2X0p0UnhCejdZbWxkaFY5a2JDbFA2eTZiZTFm
bEVzTXpQZHhiU3RJUF9sSWx3Z2p3NklyR1FWRGpHNnZJUkx5RzctdS1TeGJMa25oVUdoNEJs
aUN0bVl3V1o2d1VscnRUaGcifV0sImtleSI6IjkwMDA5OTAwMDk5MDAwOTkwMDA5OTAwMDku
MjAyNDAyMTQwMDAyMzUiLCJuY2UiOjE3MDg0NDgxMDl9.aGVsbG8gd29ybGQsIGZyb20gdHN
yY3Q.gp5bbKGZRIl0yZXmbILOJB9WfVr8KcfTe3bch87HO1H7aOIppeXNeuk84Qfd11WT8C7
9oxAqepYgyhlDVxQZIzr8OmanbSQaIT7zkZrZth0K7_DQJG45s91xUjRwzQO9lKgmpvv5dRE
1u1SngQufeZEg8qKD5rEpKdf52kQe-MV8y9Cen2D8mfukO4c5Dgsa4Y1x4t8Wrc_mOlMR5WZ
jNlBIo6_bG6I2ULWcsa_tkuSrOPnTIDWJVLN34pQCTV75eCERr0adSB4fsxKBR0BUZWBNhC_
jZHFApK_X9pLs8QvD6I1603XFPq4peEFG-XDKSVC6gNyZQGXzGROqFKy69

note: the example above has a 72 column width only for readability; actual tsrct docs do not have line breaks and will throw an error if line breaks are included

The header in the above sample is:

eyJhbGciOiJSUzI1NiIsImNscyI6ImRvYyIsInR5cCI6InRleHQiLCJpdHMiOiIyMDI0LTAy
LTIwVDE2OjU1OjA5WiIsImxlbiI6MzEsInVpZCI6IjkwMDA5OTAwMDk5MDAwOTkwMDA5OTAw
MDkuMjAyNDAyMjAxMTU1MDgtOTY2NmJjMWY4Y2I3ZjVkMyIsInNyYyI6IjkwMDA5OTAwMDk5
MDAwOTkwMDA5OTAwMDkiLCJzaWciOiJObERYX0hiSFJiNng3aTRwTXBKTVpVc3ZhNlVJeGV5
NHFqUHlGdnJaNU9pRjFybHhWSk9PYVl1VUxNUFNfQW5yVVludmJUZ1ctU04ybUtRTWhhSnJH
Ri14eUFjalZqSDE2UVhfaFdVS2lsTXBybzA3TFY2TVZ3ZF8zcEhzTHFFYUtYbWpMVTQtWlA2
eFVFSFZTU1hfMnlGaWFZMFlWSXRnN3BUSm5DSWtIcU9uTWZoa2Q5d1VGQ01rYVpXZGJSY3dn
bGUwZmVKbS1JYV9Eb1UwTnBKbDdZOE16UlRta0R2cGFSRjNjRGhtT2dZY0k0aVMycUFTcHc1
eHhBVHE4S0NvNzJ2ZzZKVjhhbzJpTV9UZlBXS0xQSDNRdFdXNUR1alo2YUNfWmlScjRzN2NR
d3RMbnpBeFFOY2o5X2JJY2hnLVc4V05mUk0zTjV4N041dVFMUkEwU0EiLCJzaGEiOiI5aGl0
OXJxOHFlZWZGUTY5dzdERFowbUVtNlRDWnNjaXg5aGVneWlCeWNnIiwiY3R5IjoidGV4dC9w
bGFpbiIsImFjbCI6ImFjbF9wdWIiLCJsc3QiOnRydWUsInJlZiI6W3sidWlkIjoiOTAwMDk5
MDAwOTkwMDA5OTAwMDk5MDAwOSIsInNoYSI6ImxYUEZ1bkdwb3JmeDYydkROR00ySExnNVVu
cmRYdlA4elpsY2RZOWNrcTQiLCJzaWciOiJrMWFoR2VwY19tdXZYRU9zZ1k5Wm1rWDI4dmIw
MGhfSHlOVV85MlduUzJUNmhFLVNXY2xkYlpKVzNuc1Vvc09ucUtWY2RaaGdCT01mQkVSV2p6
OHZIeFFabURocEFjZmVJZlZ6aXR0NmFpWnN3ZFIwSzB3ajFWcndMdHNvOHlubmxnM3hDSFBE
UmdxWjJKSTZSVTRzQnR2VWhQR2NkRGlTR3poOUpJUU9iSzVZMDhZVHpfZGlQTld0QUhIVV90
M0NrdkltMFptY1NqZzFnZGlHR2ZDZE4tZldsZjBLQXdyeklockFESkZGTlYxaGUyazZqQ01v
Vm1NTWhJd2ZWb0RCNG5oaGw5SVN3Q3ZMczUzemxZanc4bUxXV2R4aHRrRWxXTmNVSW9qVGVw
ZndBYnUxZXZSY0hIMGUtUWZueTdJbHkzTHZiR1hSczIzZlduaUlHQ0w3VVEiLCJ0ZHMiOiJs
LVBUSDdhUTFjTWE2b0ZERGhNUk4weXQ3RDBmS0RTeERBbzM3SGtNbHZ3TDZhZWh0WTBVVFEx
ektqX3BZdGJZemJad2lfUDhENUYzbmtSRkc2Q2tlbTYzQm0yckdnX1lFaHdld0ViUWxKYXZo
MDNpTERfY1hSbktWLVMtUlFnM1J0LW9VazRBSXJaT0I1Yjlmam1NWUpOVlFmV3JDakFFTnpu
cGZzR2M4dnJERGZSQUZtOTVMN09zQzlUUElGRHNJZXRsdVpvYlppMnNfbDZxbU5TS2VISmFa
UjFJX2tTeXBHYmZtTUZrOTFJckhkSHlkU1V2X0p0UnhCejdZbWxkaFY5a2JDbFA2eTZiZTFm
bEVzTXpQZHhiU3RJUF9sSWx3Z2p3NklyR1FWRGpHNnZJUkx5RzctdS1TeGJMa25oVUdoNEJs
aUN0bVl3V1o2d1VscnRUaGcifV0sImtleSI6IjkwMDA5OTAwMDk5MDAwOTkwMDA5OTAwMDku
MjAyNDAyMTQwMDAyMzUiLCJuY2UiOjE3MDg0NDgxMDl9

when decoded, it provides the following JSON:

{
  "alg": "RS256",
  "cls": "doc",
  "typ": "text",
  "its": "2024-02-20T16:55:09Z",
  "len": 31,
  "uid": "9000990009900099000990009.20240220115508-9666bc1f8cb7f5d3",
  "src": "9000990009900099000990009",
  "sig": "NlDX_HbHRb6x7i4pMpJMZUsva6UIxey4qjPyFvrZ5OiF1rlxVJOOaYuULMPS_AnrUYnvbTgW-SN2mKQMhaJrGF-xyAcjVjH16QX_hWUKilMpro07LV6MVwd_3pHsLqEaKXmjLU4-ZP6xUEHVSSX_2yFiaY0YVItg7pTJnCIkHqOnMfhkd9wUFCMkaZWdbRcwgle0feJm-Ia_DoU0NpJl7Y8MzRTmkDvpaRF3cDhmOgYcI4iS2qASpw5xxATq8KCo72vg6JV8ao2iM_TfPWKLPH3QtWW5DujZ6aC_ZiRr4s7cQwtLnzAxQNcj9_bIchg-W8WNfRM3N5x7N5uQLRA0SA",
  "sha": "9hit9rq8qeefFQ69w7DDZ0mEm6TCZscix9hegyiBycg",
  "cty": "text/plain",
  "acl": "acl_pub",
  "lst": true,
  "ref": [
    {
      "uid": "9000990009900099000990009",
      "sha": "lXPFunGporfx62vDNGM2HLg5UnrdXvP8zZlcdY9ckq4",
      "sig": "k1ahGepc_muvXEOsgY9ZmkX28vb00h_HyNU_92WnS2T6hE-SWcldbZJW3nsUosOnqKVcdZhgBOMfBERWjz8vHxQZmDhpAcfeIfVzitt6aiZswdR0K0wj1VrwLtso8ynnlg3xCHPDRgqZ2JI6RU4sBtvUhPGcdDiSGzh9JIQObK5Y08YTz_diPNWtAHHU_t3CkvIm0ZmcSjg1gdiGGfCdN-fWlf0KAwrzIhrADJFFNV1he2k6jCMoVmMMhIwfVoDB4nhhl9ISwCvLs53zlYjw8mLWWdxhtkElWNcUIojTepfwAbu1evRcHH0e-Qfny7Ily3LvbGXRs23fWniIGCL7UQ",
      "tds": "l-PTH7aQ1cMa6oFDDhMRN0yt7D0fKDSxDAo37HkMlvwL6aehtY0UTQ1zKj_pYtbYzbZwi_P8D5F3nkRFG6Ckem63Bm2rGg_YEhwewEbQlJavh03iLD_cXRnKV-S-RQg3Rt-oUk4AIrZOB5b9fjmMYJNVQfWrCjAENznpfsGc8vrDDfRAFm95L7OsC9TPIFDsIetluZobZi2s_l6qmNSKeHJaZR1I_kSypGbfmMFk91IrHdHydSUv_JtRxBz7YmldhV9kbClP6y6be1flEsMzPdxbStIP_lIlwgjw6IrGQVDjG6vIRLyG7-u-SxbLknhUGh4BliCtmYwWZ6wUlrtThg"
    }
  ],
  "key": "9000990009900099000990009.20240214000235",
  "nce": 1708448109
}

note: the above JSON is pretty printed; the actual JSON does not have any line breaks or non-significant spaces

The header is a JSON document with a set of defined fields that have a specific purpose. Headers vary by the type of artifact being represented, but for the purposes of this discussion, only the doc type of cls need apply.

Common header fields

The common header fields across all header types are:

  • alg: (required) always set to RS256
  • cls: (required) the ‘class’ of the document, doc is used for publishing documents; there are other options for more advanced use cases (such as sha, ddx, etc.) to be discussed later
  • typ: (required) the type of cls; when the cls is doc, the valid values are text, json, data, or blob based on the type of content in the body
  • its: (required) the issue timestamp of the document
  • len: (required) the length of the content, when counting the base64 characters
  • uid: (required) the globally unique id of the document; it is always prefixed with the uid of the originator (src)
  • src: (required) the globally unique id of the source; this is a 25 digit id representing either a user, an organization, or an entity
  • sig: (required) the private key signature of the base64 encoded body of the document
  • sha: (required) the sha256 hash of the base64 encoded body of the tsrct document
  • cty: (required) the content type of the content, when decoded from base64
  • acl: (required) the visibility of the document, either acl_pub for a public document or acl_pri for a private document; private documents posted on tsrct require a jwt auth header to access
  • lst: (optional) the listability of the document. defaults to false; if true, the document will be shown in a list of public documents originated by src, if false public documents cannot be listed and their uid is required for access; this has no effect in the case of a private document (acl=acl_pri)
  • key: (required) the key id that has been used to sign this document; in the above example, navigating to https://tsrct.io/9000990009900099000990009.20240214000235 will show you the public key counterpart of the private key used to sign this content
  • nce: (required) the nonce of this document, which is the unix seconds since epoch, especially important if the document is to be presented to the tsrct cloud to store, or as part of a transaction flow; this must be within a few seconds of now, else document validation will fail
  • ref: (optional) the array of prior documents that are referred to by this document; this allows the creation of a set of backpointed references; the components of each item in the ref array are:
    • uid: (required) the uid of the referred document
    • sha: (required) the sha 256 hash of the document taken from the header of the document
    • sig: (required) the sig of the document taken from the header of the document
    • tds: (required) the tsrct document signature, taken from the footer of the document
      • if any of the above items are inconsistent with the actual document, validation will fail

A note about the sha field in the header:

  • tsrct uses the sha256 of the base64 text representation of the body
  • so the calculation of the sha256 is not calcSha256(bodyBytes[]) but rather calcSha256(getBytes(Base64.encode(bodyBytes[])))
  • this is done so that runtime checks are easier to perform when downloading the tdocs for validation, since this saves an extra decoding step in the middle

Depending on the value of the cls field, various other fields can be populated, shown below

Other header fields for documents (cls:doc)

Documents are the most common use case for tsrct, and so have a few extra features that make them usable in various contexts.

A description of the type of document ('cls': 'doc') is indicated by the typ field:

  • blob: the body is a binary object like an image or a pdf document
  • data: the body is text (like a csv) but represents a structured set of data conforming to a schema
  • json: the body is a json document
  • text: the body is plain text, like xml, or json, or yaml

NOTE: the content type cty field must accurately represent the actual content of the document, even when typ is specified for 'cls': 'doc'.

If cls is doc (i.e. the header is for a tsrct doc indicating a document), the following extra fields are allowed in addition to the common fields above:

  • cid: (optional) the correlation id of the document; if used, the seq sequence field must be also present
  • dsc: (optional) a short description of the document
  • seq: (optional) the sequence number of the document; if used, the cid correlation id field must be also present
  • geo: (optional) geo point with the following format based on the geo pt format:
    • lat: (required) the latitude
    • lon: (required) the longitude
  • scm: (optional) schema of the content, if applicable; this a url pointing to a schema definition, such as a schema.org schema link
  • mtd: (optional) any arbitrary metadata in key and value pairs in a JSON object

body

The body in the above example is:

aGVsbG8gd29ybGQsIGZyb20gdHNyY3Q

when decoded, the body is the following text:

hello world, from tsrct

the body is always the url-safe, non-padded base64 encoded representation of the bytes contained in the body.

signature

The signature in the above document is:

gp5bbKGZRIl0yZXmbILOJB9WfVr8KcfTe3bch87HO1H7aOIppeXNeuk84Qfd11WT8C79oxAq
epYgyhlDVxQZIzr8OmanbSQaIT7zkZrZth0K7_DQJG45s91xUjRwzQO9lKgmpvv5dRE1u1Sn
gQufeZEg8qKD5rEpKdf52kQe-MV8y9Cen2D8mfukO4c5Dgsa4Y1x4t8Wrc_mOlMR5WZjNlBI
o6_bG6I2ULWcsa_tkuSrOPnTIDWJVLN34pQCTV75eCERr0adSB4fsxKBR0BUZWBNhC_jZHFA
pK_X9pLs8QvD6I1603XFPq4peEFG-XDKSVC6gNyZQGXzGROqFKy69

The private key used to sign the document is the same as the one represented in the key field in the header

The signature is the base64 encoded signature using the following method in pseudo code: sig = privateKeySignature((base64(header) + “.” + base64(body)).getBytes(“UTF-8”))

Other examples

Some other examples of documents are:

Thinking about the document

see more in the different use cases