Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Database Schema

Mastic uses wasm-dbms for persistent storage inside the Directory and User canisters. Because wasm-dbms manages its own stable memory, ic-stable-structures cannot be used alongside it in these canisters. Configuration values that would normally live in stable cells are stored in a shared settings key-value table instead.

The Federation Canister does not use wasm-dbms; it uses ic-stable-structures directly.

Shared Settings Table

Both the Directory and User canisters include a settings table with the same schema. Each row maps an integer key to a polymorphic value (SettingValue, backed by the wasm-dbms Value type).

ColumnTypeConstraintDescription
keyUINT16PRIMARY KEYSetting identifier
valueSettingValueTyped value for the entry

The Settings table and its helper methods (set_config_key, get_required_settings_value, get_settings_value, get_as_principal) live in the db-utils crate and are shared by both canisters.

Directory Canister

Directory Canister Settings Keys

KeyConstantValue TypeDescription
0SETTING_FEDERATION_CANISTERBLOBPrincipal of the Federation canister

moderators Table

ColumnTypeConstraintDescription
principalPrincipalPRIMARY KEYThe moderator’s principal
created_atUINT64Timestamp when added

users Table

ColumnTypeConstraintDescription
principalPrincipalPRIMARY KEYThe user’s principal
handleTEXTUNIQUE, validatedUser’s unique handle
canister_idNullable<Principal>UNIQUEUser Canister ID
canister_statusCanisterStatusActive, CreationPending, …
created_atUINT64Timestamp when registered

The handle column uses HandleSanitizer (trims whitespace, lowercases, strips leading @) and HandleValidator (enforces the handle rules). See the Handle Validation page for the full specification.

tombstones Table

Retains deleted handles to block immediate re-registration and to keep an audit trail. handle uses the same sanitizer/validator pair as the users.handle column; see the Handle Validation spec.

ColumnTypeConstraintDescription
handleTEXTPRIMARY KEY, sanitized, validatedHandle of the deleted user
principalPrincipalPrincipal of the deleted user
deleted_atUINT64Timestamp when the user was deleted

reports Table

Stores user-submitted moderation reports. See the Reports spec for validation rules on reason and target_status_uri.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
reporterPrincipalPrincipal of the reporter
target_canisterPrincipalReported user’s canister principal
target_status_uriNullable<Text>validatedURI of the reported status, if any
reasonTEXTsanitized, validatedFree-form reason
stateReportStateOpen, Resolved, Dismissed
created_atUINT64INDEXSubmission timestamp
resolved_atNullable<Uint64>Timestamp when the report was resolved
resolved_byNullable<Principal>Moderator who resolved the report

User Canister

User Canister Settings Keys

KeyConstantValue TypeDescription
0SETTING_FEDERATION_CANISTERBLOBPrincipal of the Federation canister
1SETTING_OWNER_PRINCIPALBLOBPrincipal of the canister owner
2SETTING_PUBLIC_URLTEXTPublic URL of the Mastic instance
3SETTING_DIRECTORY_CANISTERBLOBPrincipal of the Directory canister

profiles Table

Single-row table holding the owner’s profile.

ColumnTypeConstraintDescription
principalPrincipalPRIMARY KEYOwner’s principal
handleTEXTUNIQUE, validatedUser’s unique handle
display_nameNullable<Text>Display name
bioNullable<Text>Biography
avatar_dataNullable<Blob>Avatar image data
header_dataNullable<Blob>Header / banner data
created_atUINT64Account creation time
updated_atUINT64Last profile update

statuses Table

See the Status Content spec for full validation rules on content, spoiler_text, and in_reply_to_uri.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
contentTEXTvalidatedStatus body
visibilityVisibilityPublic, Unlisted, FollowersOnly, Direct
like_countUINT64Cached Like count
boost_countUINT64Cached Announce (boost) count
in_reply_to_uriNullable<Text>INDEX, validatedURI of the replied-to status
spoiler_textNullable<Text>sanitized, validatedOptional content warning / spoiler
sensitiveBooleanWhether clients should hide behind a CW
edited_atNullable<Uint64>Timestamp of the last edit
created_atUINT64INDEXCreation timestamp (indexed for feed ordering)

inbox Table

Stores inbound ActivityPub activities.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
activity_typeActivityTypeActivity discriminator (Create, Follow, …)
actor_uriTEXTvalidatedOriginating actor’s URI
object_dataJSONActivity object payload
is_boostBooleantrue when entry is an Announce (boost)
original_status_uriNullable<Text>validatedURI of the boosted status
created_atUINT64INDEXReception timestamp (indexed for feed ordering)

follow_requests Table

ColumnTypeConstraintDescription
actor_uriTEXTPRIMARY KEYRequester’s actor URI
created_atUINT64Timestamp when follow request received

followers Table

ColumnTypeConstraintDescription
actor_uriTEXTPRIMARY KEYFollower’s actor URI
created_atUINT64Timestamp when follow accepted

following Table

ColumnTypeConstraintDescription
actor_uriTEXTPRIMARY KEYFollowed actor’s URI
statusFollowStatusPending or Accepted (rejected entries are deleted)
created_atUINT64Timestamp when follow was requested

liked Table

ColumnTypeConstraintDescription
status_uriTEXTPRIMARY KEY, validatedURI of the liked status
created_atUINT64Timestamp when the status was liked

blocks Table

ColumnTypeConstraintDescription
actor_uriTEXTPRIMARY KEY, validatedURI of the blocked actor
created_atUINT64Timestamp when block was added

mutes Table

ColumnTypeConstraintDescription
actor_uriTEXTPRIMARY KEY, validatedURI of the muted actor
created_atUINT64Timestamp when mute was added

bookmarks Table

ColumnTypeConstraintDescription
status_uriTEXTPRIMARY KEY, validatedURI of the bookmarked status
created_atUINT64Timestamp when bookmark was set

boosts Table

Tracks Announce activities emitted by the user. Each boost is paired with a wrapper row in the statuses table.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
status_idUINT64FK → statuses.idWrapper status row
original_status_uriTEXTvalidatedURI of the boosted status
created_atUINT64INDEXTimestamp when the boost was emitted

The same Snowflake is reused as boosts.id, boosts.status_id, the wrapper statuses.id, and the wrapper’s feed.id — making the wrapper status URL <actor>/statuses/<snowflake> also the canonical id of the emitted Announce activity. One sequence increment per boost; one URL that dereferences both the wrapper status and the boost activity.

media Table

See the Media Attachments spec for full validation rules on media_type, description, and blurhash.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
status_idUINT64FK → statuses.id, INDEXParent status
media_typeTEXTvalidatedMIME-like media type
descriptionNullable<Text>sanitized, validatedAlt-text description
blurhashNullable<Text>validatedBlurhash preview string
bytesBLOBRaw media bytes
created_atUINT64Creation timestamp

edit_history Table

previous_spoiler_text uses the same sanitizer/validator pair as statuses.spoiler_text; see the Status Content spec.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
status_idUINT64FK → statuses.id, INDEXStatus this entry belongs to
previous_contentTEXTContent before the edit
previous_spoiler_textNullable<Text>sanitized, validatedSpoiler text before the edit
edited_atUINT64INDEXTimestamp of the edit

hashtags Table

Local per-user index of hashtags referenced by the user’s statuses.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
tagTEXTUNIQUE, validatedSanitized, lowercase tag (without #)
created_atUINT64Timestamp when the hashtag was first seen

The tag column uses HashtagSanitizer (trims whitespace, lowercases, strips leading #) and HashtagValidator. See the Hashtag Validation page for the full specification.

status_hashtags Table

Join table between statuses and hashtags. Uses a surrogate id primary key because the underlying storage layer does not support composite primary keys; uniqueness of (status_id, hashtag_id) is enforced by the application layer.

ColumnTypeConstraintDescription
idUINT64PRIMARY KEYSnowflake ID
status_idUINT64FK → statuses.id, INDEXStatus
hashtag_idUINT64FK → hashtags.id, INDEXHashtag

Up to four hashtags featured on the user’s profile. The tag column uses the same HashtagSanitizer / HashtagValidator pair as the hashtags table.

ColumnTypeConstraintDescription
tagTEXTPRIMARY KEY, validatedSanitized, lowercase tag
positionUINT8UNIQUE (0..=3)Display position
created_atUINT64Timestamp when tag was featured

pinned_statuses Table

Up to five statuses pinned on the user’s profile.

ColumnTypeConstraintDescription
status_idUINT64PRIMARY KEY, FK → statuses.idPinned status
positionUINT8UNIQUE (0..=4)Display position
pinned_atUINT64Timestamp when status was pinned

profile_metadata Table

Up to four custom fields shown on the user’s profile. See the Profile Metadata spec for full validation rules.

ColumnTypeConstraintDescription
positionUINT8PRIMARY KEY (0..=3)Position in the list
nameTEXTsanitized, validatedField name
valueTEXTsanitized, validatedField value

Custom Data Types

The following custom types are used in the schema and stored as compact single-byte discriminants:

Visibility

Maps to did::common::Visibility.

ValueVariant
0Public
1Unlisted
2FollowersOnly
3Direct

ActivityType

Maps to activitypub::ActivityType.

ValueVariant
0Create
1Update
2Delete
3Follow
4Accept
5Reject
6Like
7Announce
8Undo
9Block
10Add
11Remove
12Flag
13Move

FollowStatus

ValueVariant
0Pending
1Accepted

ReportState

ValueVariant
0Open
1Resolved
2Dismissed

Persistence

All tables are created during canister init. Data survives canister upgrades because wasm-dbms stores everything in stable memory. The post_upgrade function does not need to re-register the schema.