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 encodedJSON
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 theheader.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
header
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 toRS256
cls
: (required) the ‘class’ of the document,doc
is used for publishing documents; there are other options for more advanced use cases (such assha
,ddx
, etc.) to be discussed latertyp
: (required) thetype
ofcls
; when thecls
isdoc
, the valid values aretext
,json
,data
, orblob
based on the type of content in the bodyits
: (required) the issue timestamp of the documentlen
: (required) the length of the content, when counting the base64 charactersuid
: (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 entitysig
: (required) the private key signature of the base64 encoded body of the documentsha
: (required) the sha256 hash of the base64 encoded body of the tsrct documentcty
: (required) the content type of the content, when decoded from base64acl
: (required) the visibility of the document, eitheracl_pub
for a public document oracl_pri
for a private document; private documents posted on tsrct require a jwt auth header to accesslst
: (optional) the listability of the document. defaults tofalse
; iftrue
, the document will be shown in a list of public documents originated bysrc
, iffalse
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 tohttps://tsrct.io/9000990009900099000990009.20240214000235
will show you the public key counterpart of the private key used to sign this contentnce
: (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 failref
: (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 theref
array are:uid
: (required) the uid of the referred documentsha
: (required) the sha 256 hash of the document taken from the header of the documentsig
: (required) the sig of the document taken from the header of the documenttds
: (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 rathercalcSha256(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 documentdata
: the body is text (like a csv) but represents a structured set of data conforming to a schemajson
: the body is a json documenttext
: 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, theseq
sequence field must be also presentdsc
: (optional) a short description of the documentseq
: (optional) the sequence number of the document; if used, thecid
correlation id field must be also presentgeo
: (optional) geo point with the following format based on the geo pt format:lat
: (required) the latitudelon
: (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- note: this can also refer to a tsrct document that contains the schema definition, such as:
https://api.tsrct.io/2222222222222222222222222.ddx-corp-user-name-schema-v1/body
- note: tsrct doesn’t perform schema validation; if you include a document that doesn’t conform to the indicated schema, its upto the consuming system to validate the content
- note: this can also refer to a tsrct document that contains the schema definition, such as:
mtd
: (optional) any arbitrary metadata in key and value pairs in aJSON
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:
- example with an image here:
https://tsrct.io/9000990009900099000990009.20240220115155-620047d909ddaca6
Thinking about the document
see more in the different use cases