Skip to content

Identifiers

Every term in Intuition has a 32-byte identifier, written as 0x followed by 64 hexadecimal digits. Identifiers are derived from content. No server, counter, or contract assigns them.

Derivation

Each identifier is a keccak256 hash of packed values (abi.encodePacked):

TermIdentifier
Atomkeccak256(ATOM_SALT, keccak256(data))
Triplekeccak256(TRIPLE_SALT, subjectId, predicateId, objectId)
Counter-triplekeccak256(COUNTER_SALT, tripleId)

The salts are protocol constants. Each one is the keccak256 hash of its own name, for example keccak256("ATOM_SALT"). The MultiVault contract uses the same derivation, so an identifier computed in an application is the identifier the contract registers.

Deriving identifiers in code

Use @0xintuition/ids. Do not reimplement the derivation.

import {
  calculateAtomId,
  calculateCounterTripleId,
  calculateTripleId,
} from '@0xintuition/ids';

const atomId = calculateAtomId('hello');
// 0xa0e157e5fa1b17d3b54ec73622ce3317296920a06502661617613d59f58e947e

const tripleId = calculateTripleId(
  calculateAtomId('hello'),
  calculateAtomId('is a'),
  calculateAtomId('greeting'),
);
const counterTripleId = calculateCounterTripleId(tripleId);

The package also exports the salts (ATOM_SALT, TRIPLE_SALT, and COUNTER_SALT), validators such as isValidAtomId and isValidTripleId, and the display helper shortenHex. The validators check only that a value is a well-formed 32-byte identifier. They do not check how it was derived.

Text and bytes

calculateAtomId accepts a string. How the string is interpreted depends on its form.

InputInterpreted as
A string that is valid hexadecimal with a 0x prefixRaw bytes
Any other stringUTF-8 text

The two inputs below are therefore the same atom, because 0x68656c6c6f is the UTF-8 encoding of hello:

calculateAtomId('hello');
calculateAtomId('0x68656c6c6f');
// Both: 0xa0e157e5fa1b17d3b54ec73622ce3317296920a06502661617613d59f58e947e

Whitespace and normalization

The derivation hashes exactly the bytes it is given. Leading and trailing whitespace, letter case, Unicode normalization form, and, in a JSON document, key order and spacing all change the result. Normalize input before you derive an identifier, and apply the same normalization everywhere.

Why identifiers do not change

Because an identifier depends only on content:

  • Publishing a term onchain does not change its identifier.
  • Two parties who describe the same thing with the same data produce the same identifier without coordinating.
  • A node can store a term before it is registered onchain. When the registration is indexed, it carries the same identifier, so the node keeps one record for the term.

The derivation is fixed. Changing it would give every term a new identifier, which would split the graph. Treat the functions in @0xintuition/ids as the definition.