Complete Feature Reference

Every layer of Hive documented in technical detail — from the ModelDefinition fluent API and ColumnDefinitionFlag bitmask through the trigger pipeline, LRU cache, cron scheduler, and frontend ESM module architecture.

ModelDefinition — The Central API

struct ModelDefinition is the single source of truth that drives REST route generation, SQLite persistence, validator dispatch, trigger pipeline, and frontend schema rendering. Defined per model inside a plugin's registration code.

Fluent builder API

ModelDefinition md("note", "slip_box");
md.set_group("Slip Box", 100)
  .set_all_rest_operations()     // C+R+U+D+L
  .set_title_column("title")
  .set_columns({
    {"title",     MANDATORY | TEXT},
    {"content",   TEXTAREA},
    {"map_id",    FOREIGN_KEY},  // auto → Integer
    {"parent_note_id", FOREIGN_KEY},
    {"is_public", BOOL | MUTABLE},
    {"importance", INTEGER}
  });

Every call returns ModelDefinition& for chaining. set_columns() automatically prepends id, created_at, updated_at as the first three columns.

All configuration options

MethodEffect
set_group(name, order)Frontend navigation grouping and ordering
set_all_rest_operations()Enables C+R+U+D+L for this model
set_rest_operations("crl")Enable only Create, Read, List
set_title_column("field")Primary display field for FK label resolution
allow_reader_write()Lets Reader role write (overrides default)
set_readonly()No mutations allowed via API
set_virtual_table(true)Model has no real DB table (trigger-driven)
set_no_table(true)Model without any table at all
set_cache_enabled(false)Disable LRU cache for this model
set_cached_after_create(false)Skip caching new records
add_custom_list_action(...)Adds a frontend action button on list view
add_custom_read_action(...)Adds a frontend action button on read view
add_custom_create_action(...)Adds a frontend action button on create view

ColumnDefinition — Bitmask Flags

Each column is defined by a name and an integer bitmask of ColumnDefinitionFlag values. The constructor automatically infers types from naming conventions.

Auto-inference rules

Column nameAuto behaviour
idInteger, primary key, mandatory, unique, auto, readonly
created_atDateTime, mandatory, auto, readonly
updated_atDateTime, auto (updated on every write)
*_id suffixAutomatically Integer type + foreign key set to prefix

Type flags (mutually exclusive)

FlagSQLite type / UI widget
TEXTTEXT column, single-line input
TEXTAREATEXT column, multi-line textarea
INTEGERINTEGER column, number input
BOOLINTEGER(0/1), checkbox in UI
DATETIMEDATETIME column, formatted display
REALREAL column, decimal number
BLOBBLOB column (binary data, roadmap)

Behaviour flags (combinable)

FlagBitMeaning
MANDATORY1<<0Field required in create/update
UNIQUE1<<1Must be unique across all records
FOREIGN_KEY1<<2Auto-derives FK from _id suffix
AUTO1<<3Server-managed, not sent by client
HIDDEN1<<4Hidden in frontend list/form views
READONLY1<<5Cannot be mutated via API
MUTABLE1<<6Explicitly mutable (overrides defaults)
INTERNAL1<<7Internal field, stripped from responses

Usage example

// Combine any flags with bitwise OR:
{"title",       MANDATORY | TEXT},
{"content",     TEXTAREA | MUTABLE},
{"parent_id",   FOREIGN_KEY | HIDDEN},
{"token_hash",  MANDATORY | UNIQUE | INTERNAL},
{"enabled",     BOOL | MUTABLE}
  .set_default_value(true)
  .set_description("Whether the job is active.")

Crudl Enum — REST Operation Control

The hive::essential::Crudl enum is used everywhere to name operations: in ModelDefinition::allowed_rest_operations, trigger phase selectors, validator method names, history records, and CustomAction buttons.

Create (C = 1)

POST /api/v1/<model>
Runs validator can_create(), then Before triggers, repository create(), After triggers. Returns new record ID.

📖

Read (R = 2)

GET /api/v1/<model>/:id
Runs can_read(). Checks ModelCache before hitting IRepository::read(). Cache TTL default 1 week.

✏️

Update (U = 3)

PUT /api/v1/<model>/:id
Runs can_update(db, token, new_ef, old_ef). Both new and old entity fields are passed for diff-based validation.

🗑️

Delete (D = 4)

DELETE /api/v1/<model>/:id
Runs can_delete(). After trigger writes to history table via HistoryCommonTrigger.

📋

List (L = 5)

GET /api/v1/<model>
Runs can_list(db, token, filter). Supports page_number, page_size, sort, order, and field-level filter params.

🔑

String shorthand

set_rest_operations("crl") — each char maps: 'c'→Create, 'r'→Read, 'u'→Update, 'd'→Delete, 'l'→List. set_all_rest_operations() is equivalent to "crudl".


IValidator — The Security Gate

Every model registered via Plugin::register_model() carries a shared pointer to an IValidator instance. Validators run before any trigger or persistence — they are the last line of defence.

Interface definition

class IValidator {
public:
  // Is the user allowed to create this record?
  virtual OperationResult can_create(
    DbPtr& db,
    AccessTokenContext& token,
    entity_fields& ef) const = 0;

  virtual OperationResult can_read(
    DbPtr& db,
    AccessTokenContext& token,
    identification id) const = 0;

  virtual OperationResult can_update(
    DbPtr& db,
    AccessTokenContext& token,
    entity_fields& ef,
    entity_fields& old_fields) const = 0;

  virtual OperationResult can_delete(
    DbPtr& db,
    AccessTokenContext& token,
    identification id) const = 0;

  virtual OperationResult can_list(
    DbPtr& db,
    AccessTokenContext& token,
    string_map& filter) const = 0;
};

OperationResult

struct OperationResult {
  int status;      // 0 = OK, 403/405/... = error
  std::string error; // human-readable reason

  bool ok() const { return status == 0; }
  bool ko() const { return !ok(); }
  explicit operator bool() const noexcept;
};

// Convenience macros:
ok_result {}
status_403_forbidden {403, "You are not authorized..."}
status_405_unsupported_operation {405, "..."}

AccessTokenContext

struct AccessTokenContext {
  identification user_id;
  std::string msg;
  int status;   // HTTP status: 200=ok, 401=...
  bool system;  // true = system token

  bool ok() const;
  bool is_system() const;
};

Validators use token.ok() to check authentication and token.user_id for ownership checks. Cross-model queries via IValidator::get_validator(model_name).

Core Plugin validators (13)

UserValidator, TeamValidator, TeamMemberValidator, HistoryValidator, ApiLogValidator, AccessTokenValidator, RefreshTokenValidator, LoginSessionValidator, AuthLogValidator, SuperAdminLogValidator, JobEntryValidator, JobRunValidator, ErrorValidator


Three-Phase Trigger Pipeline

Triggers extend behaviour around every CRUD operation without touching the core orchestration layer. They carry a priority, a set of target operations (Crudl bitmask), and a phase.

Before (Phase 0)

Runs before the repository call. Can inspect and modify incoming entity_fields. Can abort the operation by returning a non-OK result. Used for: computed defaults, soft locks, pre-normalisation.

run_before_or_after(
  operation,      // Crudl
  stack_depth,    // recursion guard
  validation_result,
  action_result,  // out param
  def,            // ModelDefinition
  user_id, id,
  fields,         // mutable
  old_fields,     // for update
  query_params)
🔀

InsteadOf (Phase 2)

Completely replaces the default CRUD behaviour. Returns std::optional — if non-empty, the result is used instead of the repository call. Used for: fulltext search, virtual entities, derived reads.

// For Create:
std::optional<pair<int, OperationResult>>
run_instead_of_create(...);

// For Read:
std::optional<pair<entity_fields, OperationResult>>
run_instead_of_read(...);

// For List:
std::optional<pair<vector<entity_fields>,
  OperationResult>>
run_instead_of_list(...);

After (Phase 1)

Runs after successful persistence. Cannot abort the operation. Used for: history records, derived data maintenance, fulltext index updates, cross-model synchronisation, analytics.

Example — HistoryCommonTrigger:

// After ANY CRUDL on ANY table ("*")
// Priority: 1000 (runs last)
// Serializes operation data as JSON
// Skips: history, api_log, auth_log,
//        super_admin_log
// Skips if action_result.ko()

Trigger metadata

class Trigger : public AbstractTriggerJob {
  Trigger(
    const std::string& name,
    const std::string& description,
    int priority,                         // lower = runs first
    std::set<hive::essential::Crudl> operations,
    TriggerPhase phase,
    const std::string& table             // "*" = all tables
  );
};

Cron Scheduler — CronScheduler Architecture

Internal structure

// Fixed thread pool of 4 worker threads
class CronScheduler {
  struct ScheduledJobEntry {
    JobPtr  job;
    std::string cron;        // expression
    time_t  next_run;
    i64     job_id;          // job_entry.id
    std::string job_name;
    bool    enabled;
    bool    running;
    time_t  last_started_at;
    time_t  last_enabled_check;
    cronq::JobConfig job_config;
  };

  // Methods:
  void scheduler_loop();
  void load_jobs_from_db();
  void compute_initial_next_runs();
  void run_job(ScheduledJobEntry&);
  void insert_job_run();
  void update_job_run();
  void update_next_run_in_db();
};

Job class

class Job : public AbstractTriggerJob {
  Job(
    const std::string& job_name,
    const std::string& job_description,
    const std::string& cron_expression,
    bool enabled_by_default = true,
    bool run_once_when_missed = true
  );

  // Override to implement job logic:
  virtual std::string run(
    cronq::JobConfig& job_config) = 0;

  // Query access (same as Trigger):
  nlohmann::json call_query(
    const std::string& query_name,
    nlohmann::json& request);
};

JobConfig

// Access per-job configuration:
auto [val, err] = job_config.get_string("key");
auto val2 = job_config.get_int_or_default(
  "days", 30);
auto hash = job_config.get_sha256();
// (SHA-256 of entire config content)

Built-in jobs

Job classPluginCronDefault enabledDescription
CleanupJobcore@dailyYesDeletes old api_log, history (R/L ops), login_session, access_token (default: 30 days threshold per type)
CleanupHistoryOrphansJobcore@dailyYesRemoves history entries whose referenced record_id no longer exists
VacuumJobcore@monthlyNoRuns SQLite VACUUM; run_once_when_missed=false (skips if missed)
TestJobcoreconfigurableYesDebug/test job for scheduler verification
HtmlExportJobslip_boxconfigurableYesExports entire Slipbox as navigable static HTML files
DictionaryHtmlExportJobdictionaryconfigurableYesExports Dictionary as static HTML

ModelCache — LRU Cache with TTL

Cache characteristics

  • Algorithm: LRU (Least Recently Used)
  • TTL: 1 week by default (set_ttl_ms())
  • Capacity by count: 10 000 entries (read_cache_capacity_size)
  • Capacity by bytes: 1 GB (read_cache_capacity_bytes)
  • Thread-safe: std::shared_mutex (shared reads, exclusive writes)
  • Key: CacheKey { table_name, record_id }

Stats tracked

struct CacheStats {
  size_t hits;
  size_t misses;
  size_t evicted_lru;   // evicted by LRU policy
  size_t expired;       // evicted by TTL
  size_t shrinks;       // forced shrinks
};

Cache API

class ModelCache {
  // Read from cache (shared lock):
  bool get(table, id, out_row);

  // Write to cache (exclusive lock):
  void put(table, id, row);

  // Invalidate on update/delete:
  void invalidate(table, id);

  // Full flush:
  void clear();

  // Force-shrink to target size:
  void shrink_to(size_t target, ByteUnit);

  // Configuration:
  void set_ttl_ms(int64_t ms);
  void set_capacity_size(size_t n);
  void set_capacity_bytes(size_t bytes);

  // Diagnostics:
  void print_info();
  CacheStats information_no_lock() const;
};

Cache is disabled per-model via ModelDefinition::set_cache_enabled(false). Disabled automatically for frequently-written models like api_log, history.


Authentication System in Detail

Token lifecycle

ParameterDefaultConfig key
Access token lifetime15 minaccess_token_expires_in
Refresh token lifetime30 days (43200 min)refresh_token_expires_in
Rotation threshold7 days (10080 min)refresh_token_rotation_threshold_in

Security details

  • Tokens stored as SHA-256 hash (via OpenSSL), never plaintext
  • Token rotation: refresh token replaced when remaining lifetime < rotation threshold
  • Revocation: is_revoked flag + revoked_at timestamp per token
  • refresh_token.replaced_by_id and rotated_from_id maintain rotation chain
  • Login session links access token + refresh token in login_session table
  • Token purpose: Session / Api / Service

Auth endpoints

// Login — returns access + refresh tokens
POST /api/v1/auth/login
{
  "username": "admin",
  "password": "secret"
}
// Response:
{
  "user_id": 1,
  "access_token": "...",
  "access_token_expires_at": 1234567890,
  "refresh_token": "..."
}

// Refresh (proactive in frontend: 60s before expiry)
POST /api/v1/auth/refresh_token
{ "refresh_token": "..." }

// Logout
POST /api/v1/auth/logout
Authorization: Bearer <access_token>
{ "refresh_token": "..." }

// Change password
POST /api/v1/auth/change_password
{ "old_password": "...",
  "new_password": "..." }

// Register
POST /api/v1/auth/register
{ "username": "...", "password": "...",
  "email": "..." }

UserRole and UserStatus enums

enum UserRole {
  Guest     = 0,   // unauthenticated
  Reader    = 1,   // read-only
  Editor    = 2,   // can create/update
  Reviewer  = 3,   // can review
  Admin     = 4,   // full control
  SuperAdmin= 5,   // platform admin
  System    = 100  // internal system user
};
enum UserStatus {
  Pending     = 0,  // awaiting approval
  Active      = 1,  // normal access
  Deactivated = 2,  // soft-disabled
  Banned      = 3,  // policy violation
  Suspended   = 4,  // temporary
  Deleted     = 5   // logical delete
};

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

Vanilla JS Frontend — ESM Module Architecture

The frontend is pure ES Modules — no bundler, no transpilation, no npm. Load in a browser and it works. Served directly by Hive's WebEndpointsGenerator under /web/.

📡

api.js — HTTP client layer

Core functions: apiFetch(url, options), list_entities(entity, params, page, size), list_all_entities(entity) (auto-pages up to 1000), read_entity(entity, id), put_entity(model, id, json), post_entity(model, json), delete_entity(entity, id).

Model schema cached in localStorage for 3 hours (CACHE_TTL_MS). FK title labels cached 24 h via getTitleCache / setTitleCache.

🔐

auth.js — Token management

Functions: login(username, password), logout(), register(data), refreshToken(), changePassword(old, new), configure_get(). Tokens stored in localStorage under access_token, refresh_token, access_token_expires_at.

apiFetch() calls ensureFreshAccessToken() before every request — refreshes if expiry < 60 s away.

🖼

crud.js — CRUD renderers

renderEntityList(entity) — sortable table with column selector, per-field filter inputs, page-size selector (5/10/20/50/100), and ±2 page navigation (First/Prev/[pages]/Next/Last + "Go to" input).

renderEntityForm(entity, data) — auto-generates form: enums → <select>, booleans → checkbox, datetime → text with parseDateTimeToUnix(), FK → number input.

renderEntityRead(entity, id) — detail table with FK label resolution via resolveForeignKeyValue() and clickable FK links. Shows CustomAction buttons grouped by model.

🗂

schemas.js + state.js

filterColumnsForForm(fields) — filters columns for form display (excludes auto fields). toLabel(name) — converts snake_case to Title Case for UI labels.

state.js tracks: currentPage, pageSize, totalPages, entitySchemas (the full model_definition response). Column visibility stored in localStorage via getHiddenColumns / setHiddenColumns.

🗺

explore.js — Graph view

Slipbox-specific graph exploration. Uses InsteadOfReadNoteNavigationTrigger as the data source for the note navigation virtual model. Renders interactive graph of notes, links, and relationships.

📚

Domain app modules

app_slip_box.js — full note editor with Markdown rendering via markdown-it.min.js + syntax highlighting (highlight.min.js) + emoji support (markdown-it-emoji.min.js) registered by the Slip Box plugin.

app_dictionary.js — term browser with three-level autocomplete: exact title > exact alias > prefix title > prefix alias > substring.

app_repetition.js — spaced-repetition review session UI with card rating.


Migration Integrity System

File naming format

// Regex: V(\d+)__([a-zA-Z0-9_]+)\.sql
// Example names:
V1__create_user.sql
V2__create_team.sql
V13__add_job_entry_configuration.sql

// Migrations stored as C++ raw string
// literals inline in MigrationScripts.cpp

Integrity checks

  • Each migration carries a SHA-256 checksum of its SQL content
  • A chain hash is computed over the ordered sequence of all checksums
  • Both are verified at startup — any modification to an applied migration causes startup failure
  • Migrations are applied transactionally: failure rolls back cleanly
  • Applied migrations tracked in the migration table

Multi-database readiness

The Core plugin already ships two migration script implementations:

  • CoreSQLiteMigrationScripts — V1–V14 in SQLite dialect
  • CorePostgreSQLMigrationScripts — V1–V14 in PostgreSQL dialect

This signals active work toward multi-DB support via the planned IDatabase / IStatement / IMigration interface layer (see Roadmap).

Migration counts

PluginMigrations
CoreV1–V14 (14)
Slip BoxV1–V33 (33)
DictionaryV1–V29 (29)
RepetitionV1–V23 (23)

Planned Enhancements

🗄

Multi-Database Support

Define IDatabase, IStatement, IMigration interfaces. Implement PostgresDatabase, PostgresStatement. RepositoryFactory selects DB type at runtime from database_type config. Core plugin PostgreSQL migrations already exist.

📦

C++20/23 Modules

Migrate from #include headers to C++20 module units (.ixx files). Expected: faster compilation, better encapsulation, cleaner dependency graph. Incremental per-module approach planned.

🔍

Complex REST Filtering

JSON filter expressions passed as query param: {"and":[{"title":{"like":"%go%"}},{"deleted":{"eq":false}}]}. Operators: eq, neq, lt, gt, lte, gte, in, like, is_null. Logical: and, or, not.

🤖

AI Integration

Integrate AI capabilities directly into the Hive platform — likely as a plugin-level extension point compatible with the existing trigger/query mechanism.

📂

File Storage

New file table with content-addressable storage (files/ab/cdef1234... path structure). Dual BLOB/path storage. SHA-256 integrity, MIME type, encryption, compression metadata.

📋

OpenAPI Specification

Auto-generate OpenAPI 3.x spec from model metadata at runtime. Enables standard client SDK generation, Swagger UI, and API testing tooling without manual spec maintenance.

🔒

Password Hashing

Replace current hash_sha_256(password) with Argon2id (preferred) or bcrypt/scrypt/PBKDF2 for proper password key derivation with salt and work factor.

🐳

Docker Support

Containerised deployment with official Dockerfile and docker-compose.yml. Planned alongside PostgreSQL support for cloud-native deployments.

See it all in context

The architecture page shows how all these features compose into a single coherent request/response pipeline.