Usernotes Schema¶
Usernotes are stored on the subreddit wiki at toolbox-nxg/usernotes and toolbox-nxg/usernotes/<suffix> (NXG layout) and usernotes (classic layout).
Overview¶
Two storage layouts exist:
Layout |
Wiki pages |
Used by |
|---|---|---|
Classic (v6 blob) |
|
Original Toolbox 6.x; NXG compat mirror |
NXG (sharded) |
|
Toolbox-NXG |
NXG always reads and writes the sharded layout. When 6.x compatibility writes are enabled, NXG also maintains a v6 blob mirror on usernotes so original Toolbox clients can read current data.
NXG format¶
The NXG layout consists of a manifest page and one or more shard pages.
Manifest (toolbox-nxg/usernotes)¶
The manifest is a JSON envelope at schema version 7, continuing the classic usernotes numbering (v6 is the single-page blob; v7 is the sharded layout):
{
"format": "tbun-manifest",
"ver": 7,
"gen": 1,
"types": [ ... ],
"shards": [
{ "start": 0, "page": "s1-00000000" }
]
}
Field |
Required |
Type |
Description |
|---|---|---|---|
|
yes |
|
Format marker identifying this page as the shard manifest |
|
yes |
|
Manifest schema version |
|
yes |
integer |
Monotonic generation counter; bumped whenever the shard list changes, making page names unique |
|
yes |
|
Usernote type definitions; the canonical source for a subreddit’s note types (see |
|
yes |
|
Shard range descriptors, sorted by |
|
no |
|
Page suffixes retired by a split whose tombstone write failed; retried on the next save |
Each entry in shards is a UsernotesShardRef:
Field |
Type |
Description |
|---|---|---|
|
integer |
Inclusive uint32 lower bound of this shard’s FNV-1a username-hash range |
|
string |
Page-name suffix under |
Shard ranges are disjoint and cover the full uint32 space. Shard i covers hashes [shards[i].start, shards[i+1].start); the last shard covers through 2^32 − 1. Username hashing uses 32-bit FNV-1a over the lowercased name — the hash function is part of the storage format and must not change.
Classic format (v6 blob)¶
The classic usernotes page stores a compressed blob in a JSON envelope:
{
"ver": 6,
"constants": {
"users": ["mod1", "mod2", ...],
"warnings": ["ban", "abusewarn", ...]
},
"blob": "<base64(zlib(users JSON))>"
}
The decompressed blob is a JSON object mapping usernames to deflated user records:
{
"someuser": {
"ns": [
{
"n": "Note text",
"t": 1700000000,
"m": 0,
"l": "l,abc,def",
"w": 1
}
]
}
}
Deflated note fields¶
Field |
Description |
|---|---|
|
Note text |
|
Creation timestamp (epoch seconds — note: seconds, not milliseconds) |
|
Index into |
|
Link in compressed form: |
|
Index into |
Note types are not stored in the classic format itself. When NXG loads a v6 page it seeds types from the built-in defaults (plus any unknown type keys found in existing notes) and embeds them in the manifest on the next save.
UserNoteColor¶
Usernote type definitions are stored in the manifest’s types array and are the canonical source for a subreddit’s note types. When the manifest has no types (classic v6 subreddits that have never been saved through NXG), the built-in defaults below are used.
{
"key": "ban",
"text": "Ban",
"color": "red",
"colorDark": "#ff8f8f",
"banDuration": 7,
"autoArchiveDays": 30
}
Field |
Required |
Type |
Description |
|---|---|---|---|
|
yes |
string |
Unique identifier used to reference this type elsewhere (e.g. in |
|
yes |
string |
Human-readable display label |
|
yes |
string |
CSS color for light mode (hex string, named color, etc.) |
|
no |
string |
CSS color for dark mode; falls back to |
|
no |
integer |
When present, auto-ban is offered when leaving this note type; |
|
no |
integer |
When present, notes of this type older than this many days are archived on save; |
Built-in defaults:
key |
text |
color |
|---|---|---|
|
Good Contributor |
green |
|
Spam Watch |
fuchsia |
|
Spam Warning |
purple |
|
Abuse Warning |
orange |
|
Ban |
red |
|
Permanent Ban |
darkred |
|
Bot Ban |
black |
Schema versions¶
Version |
Layout |
Notes |
|---|---|---|
v6 |
Classic ( |
Current classic version; used by original Toolbox 6.x |
v7 (manifest) |
Sharded manifest ( |
Manifest schema version; introduced by Toolbox-NXG |
v1 (shard) |
Sharded shard pages ( |
Shard page schema version; introduced by Toolbox-NXG |
NXG always up-converts v6 data on read. The version written to the classic mirror is always v6.
Reading notes (classic format)¶
To read usernotes from the classic page:
Fetch
usernoteswiki page content.Parse JSON — the result is a
RawUsernotesBlobwithver,constants, andblobfields.Base64-decode
blob, then zlib-inflate (raw deflate) the result.Parse the inflated JSON as a
Record<string, DeflatedUser>.For each user entry, expand each deflated note by resolving
mandwagainstconstants.usersandconstants.warnings.Timestamps in the classic format are epoch seconds; multiply by 1000 to get milliseconds.
Writing notes¶
To write usernotes back, the wiki page must have moderator-only edit permissions (permlevel: 2 in the wiki page settings). Third-party tools that write to the classic usernotes page should use schema v6, preserve unknown fields, and follow the constant-pool encoding described above.
Third-party tools writing to the NXG sharded layout should treat shard boundaries as opaque and only modify shard pages via the manifest’s shard list — do not create new shard pages without updating the manifest. The gen counter in the manifest must be incremented whenever the shard list changes, and new shard page names must embed the current gen value (e.g. s{gen}-{start_hex_padded_8}).