Metadata Record Schema
This page describes MODE A — hash-only attestation, the one integration path built, verified and offered today. The 36 fields below are OUR record schema, not a list you populate. Details of these fields and relationships, and their implications, are set out in full on their own page
What Mode A asks of you: one value, a hash. Your commit path computes a cryptographic digest of the committed transaction and publishes it through a signed call. Two optional values may accompany it — the name of the algorithm you used, and a correlation reference of your own so you can find the record again in your systems. The hash function is yours: SHA-256 by default, any digest length accepted, so you need not adopt a primitive your cryptographic standards have not already approved.
Everything else in the record is produced by us, not sourced by you. Your tenant identifier, your federation identifier, the transaction id, the authenticated principal and the calling node all come from the credential that authenticated the call, never from its payload. The block and slab position, the Merkle root and the trace digest are computed by the platform. Not one of the 36 fields is supplied by your application at Mode A, so there is no field-mapping programme, no schema to extend in your core systems, and no registry for your team to coordinate with.
One field name invites a wrong reading, so it is worth naming plainly. The field called user_id records the authenticated principal — the subject of the credential that made the call. At Mode A that is the service identity your integration authenticates as. It is not a named person, not one of your customers or staff, and not something we ask you to provide. We receive no end-user identity at Mode A, because we receive nothing but a digest.
The list is published anyway, because a digest proves nothing unless every party computes it over the same fields in the same form. An auditor, a regulator or your own engineers can see exactly what the platform records and how it is digested — the fixed definition that lets a third party recompute a proof without trusting us. The encoding rules further down are OUR discipline for extending this schema, published for transparency rather than as instructions to a customer.
- Federation Id (federationId – 32-bits for the Sector’s and Pyramid’s federations)
- Enterprise Id with ITOTCCA (entnum – Big Integer)
- TransactionId (txId – UUID String)
- BusinessSector (sector – Big Integer)
- CounterSector (counterSector – Big Integer)
- Pyramid (pyramid – Big Integer)
- CounterPyramid (counterPyramid – Big Integer)
- CounterFederationId (counterFederationId – 32-bits; counterparty member-class)
- CounterEntnum (counterEntnum – Big Integer; counterparty tenant)
- DataCentre (dataCentre – String)
- CounterDataCentre (counterDataCentre – String)
- Country (country – Integer)
- CounterCountry (counterCountry – Integer)
- Database (database – 104-bits)
- Schema (schema – 104-bits)
- Table (table – 104-bits)
- Record Key (recordId – Unique 104-bits)
- Original Tx Hash (hash – 32 bytes (sha256 digest / Merkle Leaf))
- ClientNodeId (clientNodeId – String)
- TxSignStr (txSignStr – String)
- BlockId (blockId – UUID String)
- SlabId (slabId – Big Integer)
- BlockHeight (blockHeight – Big Integer)
- Amounts (REDACTED WHERE REQUIRED) (int64Amounts – List of Big Integers [JSONB])
- Exponents (REDACTED WHERE REQUIRED) (int32Exponents – List of Integers [JSONB])
- OpCodeExec (opCodeExec – Boolean)
- OpCodeCommit (opCodeCommit – Boolean)
- MerkleRootHex (merkleRootHex – Hexadecimal String)
- RequType (reqType – enum)
- IsTower (isTower – Boolean)
- CounterIsTower (counterIsTower – Boolean)
- UserId (userId – string)
- RoleId (roleId – string)
- CustomerDid (customerDid – String; the DID whose key signed the content attestation. Resolved on chain by an auditor, never from a platform table)
- ContentDigest (contentDigest – String; digest of the request body the customer submitted)
- ContentAttestation (contentAttestation – String; detached signature over that digest. It authorises nothing, so it can be retained for the life of the record and handed to an auditor)
_____________________
Customer content binding
Fields 34 to 36 differ in kind from the other thirty-three. Every other field here is something the platform observed or produced. These three are signed by a key the platform does not hold, which makes them the only input to the audit tier that the operator of this network cannot forge.
They are optional on the wire and empty for clients that have not yet adopted them. Absent and present-but-invalid are different findings and are kept apart deliberately: the first is a coverage gap, the second is substitution.
_____________________
Field encoding rules
All identifier and hash fields in this schema follow a single encoding convention; please don’t deviate per-field without amending this section.
Byte alignment. Prefer byte-aligned widths (multiples of 8) for new fields. Hashing, signing and equality comparison operate on the full container including padding bits — implementations that strip padding will produce divergent Merkle roots and break BFT replay determinism.
Allocation discipline. Identifier widths assume sequential allocation by ITOTCCA, not random generation. federationId at 32 bits permits 2^32 ≈ 4.3 x 10^9 lifetime member-class IDs sequentially, but orders of magnitude less before collision under random allocation (birthday bound). Any field whose width is below 128 bits must be allocated by a central registry, never client-generated.
String-encoded fields. Where a field is declared as a hex or UUID string (e.g. txId, blockId, merkleRootHex), the on-wire form is the canonical ASCII representation (lowercase hex; 8-4-4-4-12 UUID). The underlying value is the decoded byte array; equality is over decoded bytes, not the string form.
Quick reference for the 36 fields:
| Field | Width | Storage | Allocation |
| federationId | 32 bits | 4 bytes sequential | index assigned at onboarding; resolvable from your entnum, so you need not send it |
| counterFederationId | 32 bits | 4 bytes sequential | supplied by the sender: it names a party on another mesh and cannot be derived from yours |
| database, schema, table, recordId | 104 bits | 13 bytes | network-registry |
| hash | 32 bytes | 32 bytes (all significant, sha256 digest) | computed |
| merkleRootHex | 64 hex chars | 32 bytes decoded (canonical ASCII hex form of a sha256 digest) | computed |
| entnum, counterEntnum, sector, counterSector, pyramid, counterPyramid, slabId, blockHeight | Big Integer | variable sequential | computed |
| country, counterCountry | 32 bits | 4 bytes | member-declared, registry-verified. OPTIONAL: zero means not asserted and no check is made |
| dataCentre, counterDataCentre | String | string | network-registry |
| txId, blockId | UUID | string | 36 chars UUIDv4 |
| customerDid, contentDigest, contentAttestation | String | string | customer-signed, not platform-issued |
_____________________
Who supplies each field, field by field
Generated from ops/metadata-field-attribution.json and verified against the proto on every build, so this table cannot drift from the code.
It answers two separate questions, because running them together is what made an earlier version of this page misleading. Is the field populated at Mode A? is one question. Who supplies it, if it is populated? is a different one with a different answer — and the difference between “you declare this once at registration” and “your application sends this on every transaction” is the whole point. No field is in the second category, at any mode.
11 of the 36 carry a value at Mode A. Those 11 are populated identically in all four modes, because every mode reaches us through the same single call and we assemble the record on one shared code path afterwards. The other 25 are structurally absent at Mode A — several of them describe a written data row, and Mode A writes no data row.
| # | Field | Type | Populated at Mode A? | Who supplies it, if populated |
| 1 | tx_id | string | yes — and in all four modes | us — derived from your call |
| 2 | tx_sign_str | string | yes — and in all four modes | us — computed at signing |
| 3 | hash | bytes | yes — and in all four modes | us — computed at signing |
| 4 | sector | int64 | no | you declare it ONCE at registration — never per transaction |
| 5 | database | bytes | no | us — platform |
| 6 | schema | bytes | no | us — platform |
| 7 | table | bytes | no | us — platform |
| 8 | req_type | RequType | no — set only at B, C and D | an artefact YOU author: a WAL operation, a route entry, an outbox column |
| 9 | recordId | bytes | no | us — platform |
| 10 | block_id | string | yes — and in all four modes | us — consensus |
| 11 | block_height | int64 | yes — and in all four modes | us — consensus |
| 12 | slab_id | int64 | yes — and in all four modes | us — platform |
| 13 | client_node_id | string | yes — and in all four modes | us — the calling node’s gateway identity |
| 14 | role_id | string | no | not yet established from code; recorded as unknown rather than guessed at |
| 15 | user_id | string | yes — and in all four modes | us — the authenticated principal |
| 16 | op_code_exec | bool | no | not yet established from code; recorded as unknown rather than guessed at |
| 17 | op_code_commit | bool | no | not yet established from code; recorded as unknown rather than guessed at |
| 18 | int64Amounts | int64 | no | not yet established from code; recorded as unknown rather than guessed at |
| 19 | federationId | uint32 | yes — and in all four modes | us — from your credential |
| 20 | int32Exponents | int32 | no | not yet established from code; recorded as unknown rather than guessed at |
| 21 | pyramid | int64 | no | not yet established from code; recorded as unknown rather than guessed at |
| 22 | merkleRootHex | string | yes — and in all four modes | us — computed |
| 55 | counterPyramid | int64 | no | the sender, where a counterparty exists on another mesh |
| 56 | entnum | int64 | yes — and in all four modes | us — from your credential |
| 57 | counterSector | int64 | no | the sender, where a counterparty exists on another mesh |
| 58 | country | int32 | no | you declare it ONCE at registration — never per transaction |
| 59 | counterCountry | int32 | no | the sender, where a counterparty exists on another mesh |
| 60 | dataCentre | string | no | you declare it ONCE at registration — never per transaction |
| 61 | counterDataCentre | string | no | the sender, where a counterparty exists on another mesh |
| 62 | counterFederationId | uint32 | no | the sender, where a counterparty exists on another mesh |
| 63 | counterEntnum | int64 | no | the sender, where a counterparty exists on another mesh |
| 64 | isTower | bool | no | us — registry or deployment |
| 65 | counterIsTower | bool | no | us — registry or deployment |
| 66 | customer_did | string | no | signed by a key we do not hold |
| 67 | content_digest | string | no | signed by a key we do not hold |
| 68 | content_attestation | string | no | signed by a key we do not hold |
At Mode A — hash-only attestation, the only integration path we offer today — a customer supplies ONE required value: a content hash, in any length, under any algorithm they choose. Two optional values accompany it: the algorithm name, and a correlation reference of their own. Nothing else on this page is sourced by the customer at Mode A. The tenant boundary is stamped from a verified identity rather than taken from the payload; transaction id, block id, slab id, block height, the Merkle root and the trace hash are produced by the platform; and amounts, counterparties and semantic intent are by design never sent at all. That is not a limitation to work around — it is the privacy property Mode A exists to provide.
The remaining fields become operative only in the roadmap modes, and the counterparty fields only where a transaction genuinely has a counterparty on another mesh. Anyone reading this list as an integration checklist for Mode A would be sourcing data the platform never asks them for.
What we do NOT ask you to define. ITOTCCA allocates no country code. Your country is something you declare once when you register an identity claim; each transaction re-asserts it and the registry confirms it has not changed. Send zero and no check is made at all. The same is true of your federation identifier: it is an index we assign at onboarding, in the same way a BIC is assigned by SWIFT or an LEI by an accredited issuer, and because your entnum is stamped from your verified identity rather than taken from your payload, we can resolve it ourselves. The one identifier that must come from you is counterFederationId, which names the party you are transacting with on a different mesh — a value chosen locally cannot address someone else’s mesh, and two members choosing independently would collide.
In banking, a federation is one country, so for banks the country fields are derivable from the federation identifiers and carry no information those do not already. That is not true of every member network — the Social Housing Supply Network federates by trade or profession, where country and federation are independent axes — which is why the fields exist and are optional rather than removed.
_____________________
Beyond Mode A — the three deeper modes
Everything above is Mode A. Three deeper integration modes exist, and they differ from Mode A in one decisive respect: at B, C and D the platform begins to receive content, not only a digest of it. That changes what you are asking us to hold, so the requirements are set out separately rather than folded in here.
Mode B — change-data-capture observer. We read your database’s logical replication stream. Forensic by construction: your own write has already happened by the time we see it, so nothing can be refused.
Mode C — gateway-mediated. We sit inline, in front of your backend. The only one of the three that is preventative: it can refuse a request before your systems ever see it.
Mode D — SDK / dual-write relay. Your application writes to an outbox in the same transaction as its business write, and a relay forwards it. Carries explicit semantic intent — REVERSAL, CORRECTION, RESTATEMENT — which no other mode does.
All three have their core mechanisms built and verified end to end against a real running chain. All three are reference implementations your own team adapts, not packaged products you can install, and no customer has yet been onboarded through any of them. Mode A is what you would be buying today.
What each of B, C and D actually requires of your systems — the replication settings, the route table, the outbox schema, and what changes about custody once content crosses — is set out in full on its own page.
_____________________
What the mode decides beyond metadata: which shared levels you can use
The 36 fields above are what reaches us. The mode also decides something the field list cannot show, which is which of the network’s three shared levels a transaction can actually use. They do not all need the same thing from you.
| Shared level | What it must do | What that needs from the record | Usable at |
| Level 1 — the registry | Who is a member, in which federation, with what standing | An identity, established at REGISTRATION rather than per transaction | Every mode, Mode A included. The verified tenant identity Mode A already carries is exactly what the registry exists to establish. |
| Level 2 — gov-reg and gov-comm | What may cross, under which regime, and the record that it did | The parties, and the nature of the transaction | Mode B or deeper. A digest cannot be regime-checked. At Mode A there is nothing for Level 2 to evaluate — by construction, which is the same reason there is nothing for us to leak. |
| Level 0 — cross-border | The same question across jurisdictions | As Level 2 | Mode B or deeper, and designed rather than built. |
Mode A is therefore not shut out of the network, it is shut out of regulation. A Mode A member is a member: registered, in good standing, its records provable. What no shared tier does is evaluate its transactions, because no shared tier has been shown anything to evaluate. That is the trade Mode A makes, and it is the same trade that gives it no custody exposure at all.
Where this collides with the data-visibility ladder. Level 2 must READ a transaction to apply a regime to it. The ladder exists to stop that reading. At Rungs 1 and 2, plaintext or the platform’s own key, the platform can read and Level 2 functions fully. At Rungs 3 and 4, your key or a named partner’s, it cannot: Level 2 can still record THAT something crossed, but not evaluate WHAT crossed, and its regime function degrades to bookkeeping. This is the same trade the audit tier already makes — Rung 1 buys full cross-checking, Rung 3 buys secrecy and costs you the cross-checking.
Keyed commitments are what would dissolve it: a key-holder proves a value matches what was committed without revealing the value, so a claim about a transaction can be verified without the transaction being seen. That layer is implemented across the relevant tiers and switched on in none of them — no commitments have been written in any running deployment.
So, stated plainly rather than left to be discovered in a security review: today you can have the upper rungs of the ladder, or you can have Level 2 doing its job. Not both.
_____________________
Tx-Rails — Built by IT/OT Chain & Cloud Australia (ITOTCCA). Sydney · London.
