Djogi
A Model-first, Postgres-native distributed data engine for Rust. Define your schema as structs; the ORM, migrations, audit trail, planned admin surface, planned shell bindings, and in-memory cache predicates all derive from one declaration. Your web framework of choice handles HTTP — Djogi owns the data tier end-to-end.
Most Rust data libraries solve one slice — a query builder, or migrations, or an audit log. Djogi treats the data tier as the primary derivation target: every concern that touches your model definition (typed queries, schema evolution, change tracking, RLS, model projections via visages, in-memory cache, cross-runtime predicate algebra) lives in one integrated stack rather than a dozen disjoint crates you wire together yourself. Djogi still aims to do one thing well — that thing is data management.
Define your data schema as Rust structs, and Djogi derives the surrounding data machinery — ORM, migrations, audit trail, JSONB schema handling, with admin UI and shell bindings planned as later opt-in surfaces. One definition, one data-layer derivation chain.
Djogi does not own routing, middleware, or rendering. The view layer belongs to whichever Rust web framework the adopter chooses — developers write ordinary handlers for that framework, not Djogi abstractions. Axum is the best-covered integration today (opt in with the axum feature flag), but Djogi's core is web-framework-agnostic: Warp, Actix, Rocket, Poem, or any other Rust web framework can drive Djogi models through their own per-framework feature flag or through manual wiring. The rendering layer is similarly the developer's choice — Dioxus, Askama, Maud, or whatever fits.
Djogi is therefore not a full application framework. It is a Postgres-native data layer and model runtime with optional tooling built on top.
Design north star: Define the model once, derive everything else — but make every derivation explicit and typed. No hidden middleware chains, no implicit magic, no convention-as-code. Typed and explicit, but never unnecessarily verbose.
Performance contract: Djogi must make efficient Postgres forms expressible in-framework for common production workloads. Raw SQL remains available, but it should be the escape hatch for unusual SQL shape — not the normal way to recover performance lost to the ORM.
Repository test contract: Integration tests in this repository must exercise Djogi's typed surface by default. Raw SQL, pool access, and direct driver calls are deliberate escape hatches that require the bypass harness documented in docs/spec/raw-sql-escape-hatches.md.
Public framework rule: Djogi may be informed by demanding real-world applications, but this repository documents requirements only in product-agnostic systems language. Domain workflows, business policy, and app-specific integrations belong in application crates or separate companion crates, not in Djogi core.
Postgres only. Djogi targets PostgreSQL exclusively. This is not a temporary limitation — it is a permanent design decision. Postgres provides the features Djogi's derivation chain depends on: JSONB with path operators for typed schema fields, database-native ID generation via HeeRanjId (heerid_next(), heerid_next_desc(), ranjid_next(), ranjid_next_desc(), and batch siblings), advisory locks for migration safety, RETURNING clauses, row locks, rich indexing options, strong constraint semantics, and transactional DDL. Abstracting over multiple databases would mean giving up these capabilities or reimplementing them poorly. Every query Djogi generates, every migration it emits, every Jsonb<T> filter it compiles targets Postgres directly — no lowest-common-denominator SQL.
Status
Current release: v0.1.0-alpha.0 (public alpha; beta remains gated by Phase 8.5)
Shipped — usable today:
-
Phase 1 —
#[model]proc macro,Modeltrait (get/create/save/delete/refresh_from_db),ModelDescriptor+inventoryregistration, typed field + PK injection, and deliberately gated raw-SQL escape hatches. See the models guide. -
Phase 2 — lazy
QuerySet<T>, typedFieldRef<M, V>lookups (eq/gte/in_list/contains/ …),Conditiontree + SQL emitter, terminal reads (fetch_all/fetch_one/first/count/exists), ordering + pagination +DISTINCT/DISTINCT ON, programmatic{Model}Filter+filter_struct, bulkupdate/delete. See the queries guide. -
Phase 3 —
ForeignKey<T>,OneToOneField<T>, typed relation paths, explicit eager loading (prefetch/select_related), reverse accessor macros, and explicit-throughManyToManysupport. See the relations guide. -
Phase 4 —
DjogiContext+atomic()scope, savepoint nesting,on_commitcallbacks,save(&mut self)withRETURNING *rehydration, transactional outbox (#[model(events)]), expression IR (Expr<T>: arithmetic, field-vs-field, CASE/WHEN,EXISTS, typedOuterRef<M, V>correlated subqueries), typed aggregates + annotations (count/sum/avg/min/maxwithFILTER(WHERE)), row locks (select_for_update/nowait/skip_locked),DjogiError::LockConflict+is_transientclassification +retry_on_conflict, and write-path convenience (get_or_create,update_or_create,in_bulk,bulk_create,bulk_update,bulk_upsert,create_or_find,bulk_upsert_by_descriptor, scoped-sequence numbering via#[field(sequence_within = "...")]). See the transactions guide, expressions guide, and outbox guide. -
Phase 4.5 — opt-in transport visages derived from every
#[model]struct.#[field(expose(public, self_view, admin, export))](scalar form) or#[field(expose(public = "PeerProjection"))](relation form) generates four audience-specific Rust types per model ({Model}Public,{Model}SelfView,{Model}Admin,{Model}Export) with unconditionalserde::Serialize/Deserializederives. Default exposure isinternal— absent annotation means the field does NOT appear in any transport visage. Scalar-only visages emitimpl From<&Model>; relation-nesting visages emitimpl TryFrom<&Model>withError = VisageError::UnresolvedRelation.FieldDescriptor.visage_mapis populated so later phases can consume visage metadata without re-parsing attributes. See the visages guide. -
Phase 5-Zero — SQL substrate swap:
sqlxandheeranjid-sqlxremoved from the entire codebase. Runtime replaced bytokio-postgres+postgres-types+deadpool-postgres.DjogiPoolwrapsdeadpool_postgres::Pool;DjogiContextdispatches over pool-backed and transaction-backed connections viaPgConnection.#[djogi_test]harness uses puretokio_postgresbootstrap withheeranjid::postgres_schema::install_schema+seed_default_node.DjogiError::Sqlxand itssqlx::Errorpayload give way toDjogiError::Dbwith the opaqueDbErrornewtype;SqlState-based classification still feedsis_transient()/is_lock_error()/retry_on_conflict. Raw SQL remains available only through the hidden sealed bypass traits described in raw SQL escape hatches.FromPgRowreplacessqlx::FromRowin every macro-emitted decode impl;FromJoinedPgRowhandles prefix-keyed joined-row decode forselect_related;FromRowTuple+try_get_scalar+try_get_tuplecover positional tuple decode for raw queries (arities 1..=8). All 96#[sqlx::test]integration tests migrated to#[djogi_test]; per-test DB creation, HeeRanjID schema install, seed, andheer.node_idsetup all handled by the macro's bootstrap. -
Phase 5 — Postgres-native model layer on the tokio-postgres substrate:
Tracked<T>explicit dirty-tracking wrapper with selective column writes;#[field(version)]optimistic locking with version predicate in everysave()andDjogiError::LockConflicton conflict;#[derive(DjogiEnum)]typed Postgres enum codec + inventory metadata;Jsonb<T>with unknown-field preservation viaIndexMap<String, serde_json::Value>, flat.path::<V>("dot.path")querying, and#[derive(JsonbSchema)]typed deep-path tree;Vec<V>array fields withcontains/contained_by/overlap/lennative operators;#[model(tenant_key = "...")]RLS policy DDL side-channel +ctx.set_tenant(...)+_insecurely()bypass methods; advisory rationale warnings emitted by#[field(outbox)]and other attributes at macro expansion; outbox worker primitives +Publishertrait +NOTIFYreference publisher;QuerySet::streamcursor-backed streaming terminal. See the tracked fields, optimistic locking, enums, JSONB, arrays, and tenancy guides. -
Phase 5.5 — Narrow authentication substrate on top of Phase 4's
DjogiContextand Phase 5's tenant-keyed RLS. PluggableDjogiAuthtrait (not sealed — third-party providers first-class; object-safe soArc<dyn DjogiAuth>works); value-typedAuthContextwithuser_id: HeerId/tenant_id: Option<String>/scopes: Vec<String>/ext: HashMap<String, String>;DjogiContext::with_authconsuming builder +set_authmutating form for use insideatomic()closures;PasswordHashtyped column with transparentpostgres_types::{ToSql, FromSql}delegation + Argon2id hasher behindauth-argon2feature flag (constant-timeverifyreturnsboolto prevent timing leaks); automaticset_tenantintegration — every CRUD / QuerySet op on a#[model(tenant_key)]model auto-issuesensure_tenant_set(auth.tenant_id)before execution, with per-tenant-id tracking (applied_tenant_id: Option<String>) that re-issuesSET LOCALwhen auth changes mid-transaction; grep-abletracing::warn!whenauth.tenant_id.is_none()on a tenant-keyed model withctx.with_no_tenant_scope()explicit opt-out for admin / batch flows;_insecurelybypass methods emit searchable warn logs with caller location via#[track_caller];AuthError#[non_exhaustive]enum bridges toDjogiError::Auth(AuthError)via#[from]. See the authentication guide. -
Phase 6 — Typed spatial surface behind the
spatialfeature flag.GeoPoint { lat, lon }value type with coordinate validation (latitude in-90.0..=90.0, longitude in-180.0..=180.0), Haversine distance helper, WKTDisplay(POINT(lon lat)per OGC), and transparentpostgres_types::{ToSql, FromSql}via a manual 25-byte EWKB codec forGEOGRAPHY(Point, 4326)— no new dependencies. Typed query surface onFieldRef<M, GeoPoint>:within_km(center, km)emitsST_DWithin(col, ST_Point($lon, $lat)::geography, $meters)and routes throughCondition::Exprfor IR uniformity with the Phase 4 expression substrate;order_by_distance(center)emitsST_Distance(col, ST_Point($lon, $lat)::geography) ASCwith the primary-key column appended unconditionally as a deterministic tiebreak (pagination-safe even when rows are equidistant).IndexSpecgainsrequires_out_of_transaction: boolandextension_dependency: Option<&'static str>;MigrationShapecontract helper proves the descriptor encodes sufficient information for Phase 7's migration emitter. Phase 6 locks SRID to 4326; non-4326 work goes throughctx.raw_execute(...)+FieldSqlType::Custom. See the spatial guide. -
Phase 6.5 — Grouped aggregation as a default feature + spatial polish under the
spatialflag. Default-feature grouping: three-stage type-state (QuerySet<T>→GroupedQuerySet<T, K>→GroupedAnnotatedQuerySet<T, K, A>) driven by sealedIntoGroupKeyTuple/IntoAggregateTupletraits across arities 1–4;group_by/rollup/cube/group_by_setsentry points;.annotate(...)emits window aggregates withOVER ()when called on an ungrouped queryset and plainGROUP BYaggregates when called on a grouped one;AggregateExpr<V>::over(Window)+.distinct()with illegal combinations caught asDjogiError::UnsupportedAggregate; synthetic__djogi_agg_Naliases withDjogiError::AnnotationAliasCollisionon user collisions. Spatial polish: sixGeographyValuetypes (GeoPoint,LineString,Polygon,MultiPoint,MultiLineString,MultiPolygon) with manual EWKB codecs and OGC simple-features validation; shape predicates on anyFieldRef<M, G: GeographyValue>(contains,intersects,touches,within) with function-aware cast discipline (ST_Intersectskeeps geography, the other three cast to::geometry+$n::bytea::geometryper PostGIS 3.x overload availability);bounded_byGiST-indexable bbox prefilter +distance_tofirst-classExpr<f64>; three spatial grouping entry points sharing aSpatialGroupSourceenum (group_by_region/count_by_regionusingST_Coversfor a geography-native JOIN;cluster_by_proximityemittingST_ClusterDBSCAN(...) OVER ()inside a subquery so the outerGROUP BY cluster_idbypasses the "window functions not allowed in GROUP BY" rule;bucket_by_cellfor geohash precision bucketing);#[djogi_test(extensions = [...])]auto-provisions Postgres extensions per test with byte-level identifier validation (no regex). Zero new runtime dependencies. See the query aggregation guide and spatial guide. -
Phase 7-Zero — Indexing + apps substrate under the Phase 7 migration engine.
IndexSpecv3 contract withIndexKind::{NonUnique, UniqueConstraint, UniqueIndex},IndexTarget::{Columns, Expression},IndexColumnSpec { name, opclass, order, nulls },predicate(raw SQL) +include+nulls_not_distinct+requires_out_of_transaction+extension_dependency; five §6.4 escalation triggers unify on aforces_unique_indexhelper;PkType::HeerIdDesc+PkType::RanjIdDescvariants against heeranjid 0.3.0. Field-level#[field(unique, index, index_method, nulls_not_distinct)]grammar; model-level#[model(indexes(index(...), unique(...), unique_index(...)))]grammar with per-columnIndexColumnSpec, expression targets, concurrently, partial/covering indexes. Deterministicindex_namegeneration withbyte-levelidentifier validation (no regex). Apps subsystem:djogi::apps! { #[app(database = "…")] pub struct Vehicles; }macro with type-path-valued#[model(app = Vehicles)], convention-sealedApptrait,AppRegistry::all()runtime registry ((database, label)identity, synthetic global bucket), lifecycle markers (renamed_fromfor rename,tombstonefor retirement with compile-fail guard on active-model references) +moved_from_apphistorical metadata,AppRegistry::cross_app_edges() / cross_app_cycles()FK graph walker withAppIdentity, and theAppDiagnosticenum carrying D004 / D010 contracts for Phase 7 consumption. See the apps guide. -
Phase 7-Zero-2 — Visage query surface + PK refinements. Default PK flip:
#[model]with nopkattribute now yieldsHeerIdRecencyBiased(newest-first index scans without a secondary descending index); built-ins re-exported underdjogi::typesasHeerId/HeerIdRecencyBiased/RanjId/RanjIdRecencyBiased(plus opt-inSerial). PK trait split:PrimaryKey(required, all PK kinds) +PrimaryKeyDbGen(DB-sourced, all built-ins + opt-in custom) +PrimaryKeyClientGen(client-side, custom-only). Custom PK macro:djogi::primary_key! { pub struct ULID(uuid::Uuid); sql_type = "UUID"; default_sql = "gen_random_uuid()"; … }— adopters integrate any ID scheme (UUIDv4, ULID, Snowflake, bespoke types) via a 4-line declaration +#[model(pk = X)]. Ambient PK: all built-in and custom PK kinds are usable in any field position, not just the PK slot.bulk_createretrofit:PrimaryKeyDbGen::generate_many(ctx, n)dispatched perpk_kind— one round-trip for N IDs, then INSERT with explicitidvalues. Visage query surface (§5j): every{Model}Public/SelfView/Admin/Exportbecomes a first-class query entity with its ownfilter(...)entry,{Visage}Fieldsaccessor type, SELECT narrowing (only the visage's exposed columns), and compile-time FK / reverse-FK / M2M boundary enforcement. Reverse-FK accessors returnVisageQuerySet<ChildVisage>with a typed FK predicate; M2M accessors returnVisageQuerySet<PeerVisage>lowering to anEXISTS (...)correlated subquery against the through table (with table-qualifiedOuterRefto disambiguate framework-column collisions). Newexpose(...)grammar with->traversals: ID-only (no->), narrow peer (-> Visage), or full-struct (-> ModelStruct) optionally with nestedexpose(...).Option<ForeignKey<T>>/Option<OneToOneField<T>>lifted from Phase 4.5 deferral and project asOption<PeerVisage>. Visage querysets are read-only (mutations through the source model).serde::{Serialize, Deserialize}re-exported throughdjogi::prelude::*so adopter code does not need a separateserdeline inCargo.tomlforJsonb<T>payload schemas. See the visages guide and primary keys spec. -
Phase 7 — Migration system. Schema differ, deterministic SQL emitter, runner with advisory-locked transactional / non-transactional segment dispatch, ledger (
djogi_schema_migrations) withV1:<sha256-hex>checksum verification,apply/rollback/fake/baseline/repair/verifylibrary APIs (applyships with thedjogi migrations applyCLI dispatcher (including--fake/--reasonflags);verify,repair, andbaselineship as CLI dispatchers too; therollbackCLI dispatcher is deferred),build.rsdrift diagnostics + per-bucket pending JSON staging. CLI commands:djogi migrations apply(djogi migrate applyalias) +compose+status+verify+repair+baseline+attune(--record/--squash --from <ver>with--publish),djogi db reset(triple-gated: localhost + non-production profile + explicit--yes; URL paths are percent-decoded and validated against the strict Postgres-identifier grammar before splicing into DDL — defence-in-depth against URL-injection),djogi db seed(--database <name>selects BOTH the seed directory and the per-database connection target via path-splice; idempotentdjogi_seed_runsledger + checksum-drift refusal +--allow-non-localhostopt-in),djogi docs(deterministic per-model Markdown reference pages from the descriptor inventory; field table includes aDefaultcolumn populated from the PK strategy via the projection mirror). Filenames:V<YYYYMMDDHHMMSS>__<slug>.sdjqlplus.down.sdjql. Per(database, app)bucket; cross-database FKs rejected at projection time. Out-of-order policy gates default toRejecton production / CI andAllowWithDiagnosticon dev.attune --squashlocalhost gate uses byte-level libpq + URL parsers (no regex). Everydb/migrationssubcommand obeys a uniform exit-code matrix:0success,1runtime error (config / network / SQL / replay),2refusal (policy gate OR clap-style argument validation) — bundling both refusal classes under2so CI scripts can treat any2as a soft "operator must intervene" skip without disambiguating gate-vs-arg. See the migrations guide and migrations spec.
Recovery after crash: If
djogi migrations applyis interrupted mid-migration (SIGKILL, OOM, power loss), the ledger records partial progress. Re-rundjogi migrations apply— the runner detects the prior attempt and directs you to the appropriate repair command. For partial non-transactional progress (e.g.CREATE INDEX CONCURRENTLYcompleted but later steps did not), usedjogi migrations repair resume-partial.Existing-database adoption: If your database schema already exists (from a prior tool, manual DDL, or restored backup), use
djogi migrations apply --fake --reason "schema pre-exists from prior tooling"to mark pending migrations as applied without executing their SQL. The reason is persisted to the ledger audit trail. Verify the live schema matches the target state withdjogi migrations verifyor manual inspection before faking. If--fakeis interrupted after recording the ledger row, re-running reportsVersionAlreadyApplied(exit 2). Reconcile a stale snapshot withdjogi migrations attuneorrepair snapshot-rebuild.
- Phase 7.5 — Live-migration substrate + protected-data field attributes.
live_migrate::{plan, plan_file, state, classify}substrate withOnlineSafetyClassificationenum (OnlineSafe/FastLockDestructiveGuarded/ExpandContract/OfflineOnly,#[non_exhaustive]);pg_volatilityintrospection module; backfill engine with chunk-loop SQL pattern and plan state tracking; spatial cfg gating.#[field(protect = "...")]and related field-level RLS / audit attributes;ProtectedFieldMetadatadescriptor surface; macro parsing via raw attribute walking. EXCLUSION + stored-generated descriptor extension:ExclusionConstraintSpec,GeneratedColumnSpec,ExclusionElementtypes;ColumnSchema.generated,TableSchema.exclusion_constraintssnapshot fields with#[serde(default)]for back-compat;SchemaOperation::{AddExclusionConstraint, DropExclusionConstraint}; classifier routes EXCLUDE-on-populated and stored-generated-add toOfflineOnly(Pg18 has no NOT VALID for EXCLUDE); empty-table fast-path; macro cross-attribute validation rejectsgenerated+defaulttogether with span-precise diagnostic.djogi liveCLI commands are defined (show/status/run/resume/finalize/abandon+ daemon-mode resume), but are currently stub/deferred and not released as shipped surface in v0.1.0-alpha.0.
In flight / coming — partially merged or design-locked, but not release-complete:
- Phase 8 — 8α–8ζ implementation clusters are on
main; 8η predicate/cache correctness and Phase 8.5 publish housekeeping remain before the beta publish gate. See the implementation plan. - Phase 9 — Rhai shell, analyzer, and model-aware operational tooling (deferred; not in the alpha shipped surface).
- Phase 10 — Maahi admin console (Dioxus full-stack; visage-scope-driven RBAC; planned opt-in via
djogi = { features = ["admin"] }once thedjogi-maahicrate carve-out ships).
What Djogi Owns
| Concern | Owner | Djogi's role |
|---|---|---|
| HTTP routing, middleware, request/response | Your Rust web framework of choice | Djogi does not own the request lifecycle. Integrations are adapters; Axum is the best-covered example today, and other frameworks are wired through adapters or manually. |
| DB driver, connection pooling, row mapping | tokio-postgres + deadpool-postgres + postgres-types |
Djogi wraps the driver into a typed ORM layer (Model, QuerySet, FromPgRow, ConditionBuilder). Raw SQL and pool access remain available through the deliberate bypass harness. |
| ID generation | HeeRanjId | Calls the HeeRanjId generator family (heerid_next(), heerid_next_desc(), ranjid_next(), ranjid_next_desc(), plus generate_ids(...) / generate_ranjids(...) batch helpers) and owns nothing else |
| Async runtime | Tokio | Uses it; does not configure it |
| Model ↔ DB mapping | Djogi | Proc macro, field types, QuerySet, relations |
| Migrations | Djogi | Differ, generation, snapshot management |
| Shell tooling | Djogi (opt-in) | Rhai REPL and model-aware operational tooling for Phase 9 (deferred in v0.1.0-alpha.0) |
| CRUD logging | Djogi | Audit trail, JSON-aware diffing |
| JSONB schema fields | Djogi | Jsonb<T> — typed schemas over JSONB columns |
| Admin tooling | Djogi (planned opt-in) | Phase 10 Maahi operational UI derived from ModelDescriptor; not shipped in v0.1.0-alpha.0 |
Core Dependencies
| Dependency | Role | Notes |
|---|---|---|
axum |
HTTP layer, routing, middleware | Opt-in via the axum feature flag; the best-covered framework integration today. Not a core dependency — Djogi's core runs without it. Other web frameworks integrate through their own per-framework feature flags or manual wiring. |
tokio-postgres + deadpool-postgres + postgres-types |
Async Postgres driver + connection pool + type codecs | Postgres 18+ only |
heeranjid (postgres feature) |
Distributed IDs + schema bootstrap | HeerId / RanjId codecs, the underlying desc variants used by Djogi's recency-biased PK surface, plus install_schema / seed_default_node helpers |
tokio |
Async runtime | Standard |
serde / serde_json |
Serialization | Model ↔ JSON for shell and API |
rhai |
Embedded scripting shell | Rust-adjacent syntax, sandboxable |
figment |
Layered configuration | env vars + Djogi.toml |
clap |
CLI subcommands | djogi migrations apply/compose/status/attune, djogi db reset/seed, djogi docs, and related shipped subcommands |
inventory |
Compile-time app/model registration | Cross-crate model discovery |
syn + quote + proc-macro2 |
Proc macro infrastructure | Powers #[derive(Model)] |
time |
Datetime field types | Preferred over chrono — cleaner API, no CVE history |
heeranjid |
Primary key generation | External crate — Djogi builds its public PK surface (HeerIdRecencyBiased default, HeerId, RanjId, RanjIdRecencyBiased) on top of HeeRanjId |
Explicitly excluded: SeaORM/SeaQuery, Diesel, chrono, random UUID (v4) as default PK.
Project Layout
my-app/
src/
main.rs
apps/
vehicles/
mod.rs # djogi::register_app!(VehiclesApp)
models.rs # #[derive(Model)] structs + ManyToMany impls
routes.rs # web-framework handlers (Axum shown as the example; any Rust web framework works)
people/
mod.rs
models.rs
routes.rs
build.rs # drift detection + migration generation
Djogi.toml
seeds.rhai
migrations/ # git submodule — managed by pipeline
schema_snapshot.json
0001_initial_up.sdjql
0001_initial_down.sdjql
djogi/ # framework library crate
djogi-macros/ # proc macro crate (separate crate — required by Rust)
djogi-cli/ # standalone `djogi` binary
djogi-shell/ # Rhai engine + model bindings
Local Development
The .scripts/ directory is a git submodule pointing to
TarunvirBains/scripts. Clone
with submodules to pick it up:
# or, if you already cloned:
Reproduce CI locally with:
Specification
The full framework specification lives in docs/spec/:
| Doc | Covers |
|---|---|
| Models & Field System | #[derive(Model)], field types, annotations, dirty tracking |
| Architecture Principles | Public requirement translation, single-responsibility, framework boundaries |
| Query API | QuerySet, conditions, programmatic filters, ConditionBuilder |
| JSONB Schema Fields | Jsonb<T>, unknown field preservation, validation, subfield queries |
| Relations | ForeignKey, ManyToMany, explicit through models |
| Primary Keys | Djogi PK surface: HeerIdRecencyBiased default, ascending HeerId, RanjId, RanjIdRecencyBiased, generation patterns |
| Migrations | Build-time drift detection, schema snapshots, differ |
| Logging | Three-database architecture, CRUD audit trail, event tracing |
| Configuration & CLI | Djogi.toml, the djogi CLI, app registration, and web framework integration (axum shown as the concrete example) |
| Shell | Rhai REPL, transactions, import/export, seed scripts |
| Maahi (Admin Console) | Dioxus full-stack admin with visage-scope-driven RBAC, multi-tenancy, six-action permissions, M2M inlines, and the inline-bulk approval threshold |
| Scope & Boundaries | What belongs in Djogi vs an app crate or companion crate |
| Research Areas | Open implementation questions by subsystem |
| Raw SQL Escape Hatches | Raw SQL as djogi's unsafe, bypass attribute, justification comments, and pin-test policy |
| Design Decisions | Full decision log |
Background
- The Agentic Shift — why Model-first frameworks exist