First-Class Plugin Architecture

Plugins are the primary extension mechanism in Hive. Each plugin contributes models, migrations, validators, triggers, jobs, and queries as a single coherent vertical slice — not scattered patches across core files. The four built-in plugins cover authentication, knowledge management, spaced repetition, and lexicon management.

What a plugin can register

The Plugin base class (in include/hive-api/hive/api/Plugin.hpp) provides all registration methods. After startup, close_for_changes() is called — the plugin becomes immutable at runtime.

Registration methodWhat it registersUsed by
register_model(def, validator, repository_factory) An entity: ModelDefinition (columns, CRUD ops, UI hints, cache config, group) + its IValidator + repository factory. The REGISTER_MODEL(model, Model, MODEL) macro wires all three. HTTP route generation, frontend rendering, persistence, validation
register_migrations(MigrationScriptsPtr) Ordered migration scripts (V{n}__name, embedded SQL) per database dialect — REGISTER_MIGRATIONS(Plugin, SQLite) selects by configured database_type Applied at startup in dependency order with SHA-256 chain-hash integrity
register_trigger(TriggerPtr) Before / InsteadOf / After / Around hook for one or more CRUD operations Trigger pipeline (steps 6–9 of request lifecycle)
register_job(JobPtr) A named cron-scheduled background task CronScheduler (4 worker threads)
register_query(QueryPtr) Named reusable SQL query — complex lookups shared across validators/triggers/jobs Called via call_query(query_name, request) from triggers and jobs
register_library_file(path) + apps Static frontend assets (JS/CSS libraries) and named frontend apps the plugin ships (e.g. slip_box, dictionary, repetition) Served by WebEndpointsGenerator; listed on the frontend landing page

Plugin Lifecycle & Dependency Resolution

Main.cpp: register plugin factories
   new CorePlugin(), new SlipBoxPlugin(), new RepetitionPlugin(), new DictionaryPlugin()
          │
          ▼
PluginRegistry::get_plugin_names_sorted_by_dependencies()
   Topological sort of declared dependency graph
   Throws CyclicDependencyException   — if A→B→A detected
   Throws MissingDependencyException  — if declared dep not registered
   Result: [core, slip_box, dictionary, repetition]
          │
          ▼
For each plugin in sorted order:
  SqliteDatabaseMigration::apply_all()
    · Iterate registered V{n}__name migration scripts (embedded SQL)
    · Compute SHA-256 per script + rolling chain_hash
    · Apply only new migrations (compare to schema history)
    · On hash mismatch: throw — startup aborts
          │
          ▼
For each plugin in sorted order:
  plugin.register_models()     → ModelDefinitionRegistry
  plugin.register_validators() → ValidatorRegistry
  plugin.register_triggers()   → TriggerRegistry (sorted by priority)
  plugin.register_jobs()       → JobRegistry
  plugin.register_queries()    → QueryRegistry
  plugin.close_for_changes()   → Plugin becomes read-only at runtime
          │
          ▼
HTTP route generation (one pass over ModelDefinitionRegistry)
Cron scheduler start (4 threads, ScheduledJobEntry per job)

Plugin dependency graph

core ◄── slip_box ◄── repetition
core ◄── dictionary

Core Plugin — Platform Foundation

The Core plugin provides platform-critical infrastructure required by every domain plugin. It is always loaded first and cannot be disabled. Source: src/hive-api/hive/plugins/core/. It is the only plugin that already ships its migrations in two SQL dialectsCoreSQLiteMigrationScripts and CorePostgreSQLMigrationScripts.

Tables registered (13 models, 14 migrations)

TablePurpose
userUser accounts — username, password hash, email, display name, role, status, last login
team / team_memberTeam grouping and membership
access_tokenShort-lived bearer tokens — hashed, with expiry and revocation
refresh_tokenSession refresh tokens — hashed, rotating with rotation chain
login_sessionTracked login sessions linking access + refresh tokens
auth_logAuthentication events — login success/failure, logout, token refresh
api_logAPI request log (can be compiled out via MINDNET_ENABLE_API_LOG=OFF, the default)
super_admin_logPrivileged operations log — only accessible to SuperAdmin
historyCRUD operation history for all models (written by global After trigger; toggle via MINDNET_ENABLE_HISTORY)
errorServer-side error tracking — type, message, timestamp
job_entryPersisted job definitions — name, cron, enabled, last_run, next_run
job_runJob execution telemetry — start, end, status, output string

Key enums (Core domain)

enum class UserRole : int {
    Guest      = 0,
    Reader     = 1,
    Editor     = 2,
    Reviewer   = 3,
    Admin      = 4,
    SuperAdmin = 5,
    System     = 100
};

enum class UserStatus : int {
    Pending     = 0,
    Active      = 1,
    Deactivated = 2,
    Banned      = 3,
    Suspended   = 4,
    Deleted     = 5
};

enum class RegistrationMode : int {
    Free                  = 0,
    RequiresAdminApproval = 1,
    AdminAddsUsers        = 2
};

Core validators (13)

UserValidator, TeamValidator, TeamMemberValidator, AccessTokenValidator, RefreshTokenValidator, LoginSessionValidator, AuthLogValidator, ApiLogValidator, SuperAdminLogValidator, HistoryValidator, ErrorValidator, JobEntryValidator, JobRunValidator — enforcing authentication logic, role hierarchy, token lifecycle, and admin-only access to operational logs.

Core plugin SQL schema (real excerpt — V1)

-- V1__create_user (from CoreSQLiteMigrationScripts.cpp)
CREATE TABLE user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created_at DATETIME,
    updated_at DATETIME,

    username TEXT NOT NULL UNIQUE,
    password_hash TEXT,
    email TEXT,

    display_name TEXT,
    profile_text TEXT,

    role INTEGER NOT NULL DEFAULT 0,   -- UserRole (Guest=0 … System=100)
    status INTEGER NOT NULL,           -- UserStatus (Pending=0 … Deleted=5)

    last_login DATETIME
);

CREATE INDEX idx_user_last_login ON user(last_login);
CREATE INDEX idx_user_status ON user(status);
CREATE INDEX idx_user_role ON user(role);

Core plugin triggers

Trigger namePhaseTableOperationsPurpose
HistoryCommonTrigger After "*" (all models) CRUDL Writes a JSON snapshot of every operation to the history table. Priority 1000 (runs last); skips history, api_log, auth_log, super_admin_log and failed operations

Core plugin scheduled jobs

Job classDefault scheduleEnabledPurpose
CleanupJob@dailyYesDeletes old api_log, history (Read/List operations), login_session, and access_token records past per-type age thresholds (default 30 days)
CleanupHistoryOrphansJob@dailyYesRemoves history entries whose referenced record no longer exists
VacuumJob@monthlyNoSQLite VACUUM — compacts the database file; run_once_when_missed=false
TestJobconfigurableYesDebug/test job for scheduler verification

Core plugin custom queries

CleanupSQLiteQuery · VacuumSQLiteQuery · CleanupHistoryOrphansSQLiteQuery — the SQL behind the maintenance jobs, registered as named queries so jobs stay database-agnostic.


Slipbox Plugin — Knowledge Graph

A rich knowledge management system inspired by the Zettelkasten / Slip Box methodology. Designed for structured note-taking, knowledge graphs, and linked writing. Depends on: core. Source: src/hive-plugin-slip-box/. In the current CMake configuration it is gated behind ALLOW_LEGACY_PLUGINS (with MINDNET_ENABLE_SLIPBOX_PLUGIN=ON once legacy plugins are allowed).

Tables registered (26 tables, 33 migrations)

TablePurpose
notePrimary knowledge unit — title, hierarchy (parent, path, depth), importance, difficulty
contentNote body (Markdown) stored separately, with fulltext search support
mapLogical grouping container for notes
linkDirectional connections between notes
tag / tag_typeTagging with typed tag definitions
propertyKey/value properties attached to notes
flagNote flagging (importance, review needed, etc.)
pinned_notePer-user pinned notes list
source / urlBook / web references
wanted_notePlaceholder for planned-but-not-yet-written notes
ideaQuick idea capture (fleeting notes)
project / taskStructured work tracking
termTerm index over note content
question / test / test_attempt / test_attempt_answerSelf-testing over the knowledge base
collection / collection_item / map_collection / map_collection_itemCurated collections of notes and maps
alertSystem alerts
annotationPersonal annotations on notes

Slipbox SQL schema (real excerpt)

-- from SlipBoxSQLiteMigrationScripts.cpp
CREATE TABLE note (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created_at DATETIME,
    updated_at DATETIME,

    map_id INTEGER NOT NULL,
    parent_note_id INTEGER,
    content_id INTEGER UNIQUE,
    source_id INTEGER,
    alias_for_note_id INTEGER,

    title TEXT NOT NULL,
    sibling_order INTEGER NOT NULL,
    importance INTEGER DEFAULT 0,
    difficulty INTEGER DEFAULT 0,

    -- hierarchical metadata
    path TEXT,               -- e.g. '/000001/000045/000099'
    depth INTEGER DEFAULT 0, -- 0=root, 1=child, ...

    FOREIGN KEY (map_id) REFERENCES map(id),
    FOREIGN KEY (parent_note_id) REFERENCES note(id),
    FOREIGN KEY (content_id) REFERENCES content(id),
    FOREIGN KEY (source_id) REFERENCES source(id)
);

Bundled JS libraries

The Slipbox plugin registers these static JS/CSS files for the frontend note editor:

  • markdown-it.min.js — Markdown renderer
  • markdown-it-emoji.min.js — emoji extension
  • highlight.min.js — syntax highlighting

Slipbox triggers (10)

Trigger classPhasePurpose
BeforeCreateNoteTriggerBeforePrepare hierarchy fields (sibling order, content linkage) before persisting a new note
AfterCreateUpdateNoteTriggerAfterMaintain derived data after note create/update
UpdateNotePathAndDepthAfterTriggerAfterRecompute path and depth for the note subtree when the parent changes (backed by UpdateNotePathAndDepthSQLiteQuery)
BeforeUpdateContentTrigger / AfterUpdateContentTriggerBefore / AfterContent lifecycle — link parsing (ContentLinkParser), link synchronisation (LinkResolver/LinkSynchronizer), term maintenance around content edits
InsteadOfReadNoteNavigationTriggerInsteadOfVirtual read powering graph exploration / note navigation (previous, next, parent, children, links)
InsteadOfListTermFulltextTriggerInsteadOfFulltext term search replacing the standard list operation
InsteadOfListTagTypeFulltextTriggerInsteadOfFulltext tag-type search
AfterCreateTestAttemptTrigger / AfterCreateTestAttemptAnswerTriggerAfterSelf-testing flow — select question IDs (GetQuestionIdsSQLiteQuery) and score attempts

Slipbox custom queries (7)

Query classPurpose
FindNotesInMapSQLiteQueryReturn all notes belonging to a map (tree rendering)
FindPreviousAndNextNoteSQLiteQueryResolve previous/next note for sequential navigation
FindNextSiblingOrderSQLiteQueryCompute the next sibling_order value under a parent
UpdateNotePathAndDepthSQLiteQueryBulk-update hierarchical path/depth for a subtree
FindTermsSQLiteQuery / FindTagTypesSQLiteQueryFulltext lookups backing the InsteadOf list triggers
GetQuestionIdsSQLiteQuerySelect question IDs for a new test attempt

Slipbox validators (29)

One validator per model — NoteValidator, ContentValidator, MapValidator, LinkValidator, TagValidator, TagTypeValidator, PropertyValidator, FlagValidator, PinnedNoteValidator, SourceValidator, UrlValidator, WantedNoteValidator, IdeaValidator, ProjectValidator, TaskValidator, TermValidator, QuestionValidator, TestValidator, TestAttemptValidator, TestAttemptAnswerValidator, CollectionValidator, CollectionItemValidator, MapCollectionValidator, MapCollectionItemValidator, AlertValidator, AnnotationValidator, NoteNavigationValidator, TermFulltextValidator, TagTypeFulltextValidator.

Slipbox jobs

Job classDefault schedulePurpose
HtmlExportJobConfigurableExports the entire Slipbox knowledge graph as a static HTML website with navigation, link graph, and note index. Output can be deployed as a static site.
Graph exploration
Graph exploration — Slipbox Plugin
List nodes
Note list view with column selector

Repetition Plugin — Spaced Repetition

SuperMemo-style spaced repetition system for systematic knowledge review and memorisation scheduling. Depends on: slip_box (and transitively core). Source: src/hive-plugin-repetition/.

Tables registered (13 tables, 24 migrations)

TablePurpose
r_sessionA review session — user, map, algorithm, selected items
r_reviewIndividual review — grade (0–5), response data, latency, timestamps
r_session_metric / r_session_newPer-session metrics and new-item tracking
r_global_setting / r_user_settingGlobal and per-user repetition configuration
r0_state, r2_state, r4_statePer-item scheduling state for the simpler algorithm variants
r18_statePer-item scheduling state for the R18 algorithm
r18_perf_aggAggregated performance statistics for R18
r18_prediction_logPrediction telemetry — model forecasts vs actual recall
r18_adaptive_parametersAdaptive per-user parameters of the R18 model

SQL schema (real excerpt)

-- from RepetitionSQLiteMigrationScripts.cpp
CREATE TABLE r_review (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created_at DATETIME,
    updated_at DATETIME,

    user_id INTEGER NOT NULL,
    map_id INTEGER NOT NULL,
    r_session_id INTEGER,

    algorithm INTEGER NOT NULL,

    note_id INTEGER,

    review_date DATETIME,
    grade INTEGER CHECK (grade BETWEEN 0 AND 5),
    response_data TEXT, -- e.g. JSON: {"selected": [1, 3]}
    notes TEXT,

    started_at DATETIME, -- when started answering
    ended_at DATETIME,   -- when finished
    latency_ms INTEGER   -- response time in ms
    ...
);

Repetition triggers (4)

Trigger classPhasePurpose
RSessionBeforeCreateTriggerBeforeSelect the items for a new review session (backed by GetRSessionSelectedItemsSQLiteQuery)
RReviewAfterCreateTriggerAfterAfter each graded review, update the per-item scheduling state (interval, next due date)
AfterCreateDeleteFlagRepetitionTriggerAfterReact to Slipbox flag create/delete — include/exclude items from repetition
AfterUpdateContentSemanticVersionTriggerAfterTrack semantic content versions so reviews reference the content revision actually studied

Custom queries

Query classPurpose
GetRSessionSelectedItemsSQLiteQuerySelect the due/new items for a session according to the chosen algorithm

Validators (10)

RSessionValidator, RReviewValidator, RGlobalSettingValidator, RUserSettingValidator, R0StateValidator, R2StateValidator, R4StateValidator, R18StateValidator, R18PerfAggValidator, R18PredictionLogValidator

Frontend app

  • Review session interface (card-by-card) with grade buttons 0–5
  • Response latency tracking (latency_ms)
  • Integrates with Slipbox notes and Dictionary terms as review material
  • Served from /web/app_repetition.html

Note: the Repetition plugin is gated behind ALLOW_LEGACY_PLUGINS and MINDNET_ENABLE_REPETITION_PLUGIN (OFF by default) in the current CMake configuration.


Dictionary Plugin — Lexicon Management

A full-featured lexicon and dictionary system with fulltext search, visit analytics, understanding tracking, review integration, and a dedicated 19-language frontend app. Depends on: core. Source: src/hive-plugin-dictionary/. This is the most actively developed domain plugin and the only one enabled in the default CMake configuration (MINDNET_ENABLE_DICTIONARY_PLUGIN=ON).

Tables registered (20 tables, 29 migrations)

TablePurpose
dictionary_mapThematic map — grouping container (with emoji support in the frontend)
dictionary_termPrimary term — title, disambiguation, definition, status, importance, difficulty, is_root
dictionary_term_aliasAlternate names / spellings for a term
dictionary_linkInter-term links — since 2026 terms may link across different maps
dictionary_source / dictionary_source_typeReference sources with typed classification
dictionary_url / dictionary_url_typeExternal URL references with typed classification
dictionary_term_visitVisit history per term, user, and map — feeds metrics and history windows
dictionary_term_understandingPer-user understanding level per term
dictionary_noteNotes attached to dictionary entities (positioned)
dictionary_searchSaved/advanced searches
dictionary_review / dictionary_state_18Term review flow with R18-style scheduling state
dictionary_tag / dictionary_tag_typeTyped tagging of terms
dictionary_flagTerm flags
dictionary_index / dictionary_index_typeTyped term indexes
dictionary_pinned_termPer-user pinned terms

SQL schema (real excerpt — V2, V3)

-- from DictionarySQLiteMigrationScripts.cpp
CREATE TABLE dictionary_term (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created_at DATETIME,
    updated_at DATETIME,

    dictionary_map_id INTEGER NOT NULL,
    title TEXT NOT NULL,
    disambiguation TEXT,
    definition TEXT,
    status INTEGER NOT NULL,
    importance INTEGER NOT NULL,
    difficulty INTEGER NOT NULL,

    UNIQUE(dictionary_map_id, title, disambiguation),

    FOREIGN KEY(dictionary_map_id)
        REFERENCES dictionary_map(id)
);

CREATE INDEX idx_dictionary_term_map
    ON dictionary_term(dictionary_map_id);
CREATE INDEX idx_dictionary_term_title
    ON dictionary_term(title);

CREATE TABLE dictionary_term_visit (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created_at DATETIME,
    updated_at DATETIME,

    dictionary_term_id INTEGER NOT NULL,
    user_id INTEGER NOT NULL,
    dictionary_map_id INTEGER NOT NULL,

    FOREIGN KEY(dictionary_term_id)
        REFERENCES dictionary_term(id),
    FOREIGN KEY(user_id) REFERENCES user(id),
    FOREIGN KEY(dictionary_map_id)
        REFERENCES dictionary_map(id)
);

Dictionary triggers (15)

Trigger classPhasePurpose
InsteadOfListDictionaryTermFulltextTriggerInsteadOfFulltext term search with three-level autocomplete relevance scoring (exact > prefix > substring, titles ranked above aliases)
InsteadOfListDictionaryTermSearchTriggerInsteadOfAdvanced term search (backed by FindDictionaryTermsViaAdvancedSearchSQLiteQuery)
InsteadOfListDictionaryTermAliasesFulltextTriggerInsteadOfFulltext search over aliases, merged into autocomplete results
InsteadOfReadDictionaryOlderTermTrigger / InsteadOfReadDictionaryNewerTermTriggerInsteadOfPrevious/next term navigation as virtual reads
InsteadOfReadListDictionaryTermMetricsTriggerInsteadOfVirtual term-metrics model — visit counts and usage analytics per term
InsteadOfListDictionaryTermsForReviewTriggerInsteadOfSelect terms due for review (spaced-repetition integration)
DictionaryReviewAfterCreateTriggerAfterUpdate dictionary_state_18 scheduling state after each review
BeforeCreateDictionaryNoteTriggerBeforeCompute next note position (backed by FindNextDictionaryNotePositionSQLiteQuery)
InsteadOfListDictionarySearchesFulltextTrigger, …FlagsFulltextTrigger, …TagTypeFulltextTrigger, …IndexTypeFulltextTrigger, …SourceTypeFulltextTrigger, …UrlTypeFulltextTriggerInsteadOfFulltext list overrides for the auxiliary dictionary models

Dictionary custom queries (13)

FindDictionaryTermsSQLiteQuery, FindDictionaryTermsViaAdvancedSearchSQLiteQuery, FindDictionaryTermAliasesSQLiteQuery, FindDictionaryTermMetricsSQLiteQuery, FindDictionaryTermsForReviewSQLiteQuery, FindDictionaryOlderNewerTermsSQLiteQuery, FindNextDictionaryNotePositionSQLiteQuery, FindDictionarySearchesSQLiteQuery, FindDictionaryFlagsSQLiteQuery, FindDictionaryTagTypesSQLiteQuery, FindDictionaryIndexTypesSQLiteQuery, FindDictionarySourceTypesSQLiteQuery, FindDictionaryUrlTypesSQLiteQuery

Dictionary validators (34)

One validator per model plus dedicated validators for every virtual/fulltext model — from DictionaryTermValidator and DictionaryMapValidator through DictionaryTermMetricValidator, DictionaryTermForReviewValidator, DictionaryState18Validator, and the *Fulltext validator family. Includes input hygiene checks such as enforcing trimmed text columns.

Dedicated frontend app (19 languages)


Build Your Own Plugin

The plugin system is the intended path for all domain extensions. The four built-in plugins (core, slip_box, repetition, dictionary) are the authoritative structural templates.

1

Create the module skeleton

Create src/hive-plugin-<your-domain>/ mirroring the structure of an existing plugin (the Dictionary plugin is the most complete template).

src/hive-plugin-myplugin/
└── hive/plugins/myplugin/
    ├── MyPluginFactory.cpp
    ├── models/          — BaseModel subclasses + ModelDefinition
    ├── validators/      — IValidator per model
    ├── triggers/        — optional Before/After/InsteadOf triggers
    ├── jobs/            — optional scheduled jobs
    └── migrations/
        └── MyPluginSQLiteMigrationScripts.cpp
            (embedded V1__…, V2__… SQL scripts)
2

Implement the plugin factory

Real pattern from RepetitionPluginFactory.cpp — the factory creates an api::Plugin with name, description, frontend apps, and explicit dependencies, then registers all contributions:

api::PluginPtr MyPluginFactory::create(
    std::shared_ptr<api::RepositoryFactory>& repository_factory) const
{
    auto plugin = std::make_shared<api::Plugin>(
        MY_PLUGIN_NAME,
        "My domain",
        std::vector<std::string>{"my_app"},      // frontend apps
        std::vector<std::string>{"slip_box"}     // explicit deps
    );  // an implicit dependency on "core" is always added

    REGISTER_MIGRATIONS(MyPlugin, SQLite)

    // REGISTER_MODEL(model, Model, MODEL) expands to:
    // plugin->register_model(models::MODEL_DEFINITION,
    //     std::make_shared<validators::ModelValidator>(),
    //     repository_factory);
    REGISTER_MODEL(my_model, MyModel, MY_MODEL)

    plugin->register_trigger(
        std::make_shared<triggers::MyAfterCreateTrigger>());
    plugin->register_query(
        std::make_shared<queries::FindMyThingsSQLiteQuery>());
    plugin->register_job(std::make_shared<jobs::MyCleanupJob>());

    return plugin;
}
3

Define the model + migration

Define column constants in the ORM area, a BaseModel subclass, and a ModelDefinition with group, title column, allowed operations, and column flags (MANDATORY | TEXT, FOREIGN_KEY with automatic _id inference, etc.). Ship the matching CREATE TABLE as an embedded migration script:

add_migration("V1__create_my_model.sql", R"(
CREATE TABLE my_model (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created_at DATETIME,
    updated_at DATETIME,
    name TEXT NOT NULL,
    owner_id INTEGER,
    FOREIGN KEY(owner_id) REFERENCES user(id)
);
)");
4

Wire into the build and startup

Add the plugin subdirectory in the top-level CMakeLists.txt (optionally behind a feature flag like the built-in MINDNET_ENABLE_*_PLUGIN options), link the plugin library into hive_app in src/hive-app/CMakeLists.txt, and register the factory in Main.cpp following the existing plugin registration pattern.

5

Verify end-to-end

After startup, check the complete chain:

  1. Migrations applied — check migration table in SQLite
  2. GET /api/v1/model_definitionmy_model appears
  3. CRUD endpoints accessible at /api/v1/my_model
  4. Frontend renders CRUD screens automatically
  5. Validators reject invalid requests with correct status codes
  6. Triggers fire in correct order — check history table
  7. Jobs appear in job_entry table with correct cron
Full Developer Guide with Code Examples →

Ready to extend Hive with your domain?

The Developer Guide provides complete C++ code examples for validators, triggers, jobs, and the ModelDefinition fluent builder API.