Skip to main content

Metadata Discovery Architecture

Framework M utilizes a Code-First Metadata Discovery system that automatically exposes DocType configuration to the frontend and other consumers through a generalized, secure, and extensible API.

Core Philosophy

Metadata is defined directly on the DocType as an internal Meta class. The framework automatically indexes, whitelists, and serves this metadata, ensuring the UI remains in sync with the backend requirements without manual "bridge" code.

1. Discovery & Registration

Framework M supports a modular discovery model designed for both rapid local development and strictly managed production environments.

Modular Entry-Points (Production)

In production, the framework avoids expensive filesystem crawling. Instead, it relies on explicit app registration through Python entry-points. Every app or library providing DocTypes must register itself in its pyproject.toml:

[project.entry-points."framework_m.apps"]
my_custom_app = "my_custom_app.plugin:app"

The framework's hydrate_apps(container) orchestrator scans these entry-points at startup. This "Ignition Switch" ensures that app containers are loaded and bootstrap tasks (like metadata decoration) are executed before the metadata engine is finalized.

Selective Loading (INSTALLED_APPS)

To restrict the scope of metadata discovery (e.g., for multi-tenant isolation or micro-service partitioning), you can set the INSTALLED_APPS environment variable. When set, the registry will only load and serve DocTypes from the specified packages, even if other apps are installed in the environment.

Monolithic Crawling (Development)

For local prototyping, if no entry-points or INSTALLED_APPS are found, the framework falls back to a recursive crawl of the current project directory. This enables an "it just works" experience where creating a new DocType file immediately reflects in the UI.

2. Automated Extraction

The BaseDocType provides a get_meta_flags() helper that automatically collects "simple" attributes (bool, str, int, float) and the specialized ui_meta dictionary from the internal Meta class. This ensures all UI and behavioral flags are immediately available for the discovery engine.

class Invoice(DocType):
class Meta:
show_in_desk = True # Automatically collected as a flag
label = "Tax Invoice" # Automatically collected as a flag
ui_meta = { # Unified UI configuration
"icon": "FileText",
"group": "Accounts",
"fields": {
"items": {
"ui_widget": "grid",
"grid_columns": ["item_name", "qty", "rate"]
}
}
}

2. Property Overrides & Merging

While Pydantic Field(json_schema_extra=...) is the primary source of truth for individual field metadata, the framework supports Centralized UI Meta Merging. The MetadataRouter extracts the ui_meta.fields dictionary and merges it into the final JSON Schema properties.

Priority Order

  1. Developer Explicit Overrides: Meta.ui_meta.fields (Highest priority)
  2. App Decorations: MetadataDecoratorRegistry (Augmented fields)
  3. Pydantic Model Schema: Field(json_schema_extra=...) (Base priority)

This centralization allows for complex UI configurations (like spreadsheet grid columns) to be kept separate from the domain validation logic.

2. Dynamic Whitelist (Security)

To prevent "Discovery Escalation" and Information Leakage, the metadata discovery API only exposes and filters flags that are explicitly registered in the Metadata Whitelist.

Safe-by-Default

By default, the MetaRegistry automatically whitelists all properties defined in the DocTypeMetaConfig DTO (such as show_in_desk, api_resource, cross_tenant_aggregation, ui_meta, etc.).

This guarantees that the core metadata schema acts as the single source of truth, eliminating the need to manually update a hardcoded whitelist whenever a new core framework feature is added.

Extensibility for Apps

Custom apps and plugins can "publish" their own metadata flags to the discovery API during their initialization phase:

# In your app's initialization
from framework_m_core.registry import MetaRegistry

registry = MetaRegistry.get_instance()
registry.register_meta_key("wms_enabled")
registry.register_meta_key("pos_sync_priority")

3. Discovery API

The discovery API (/api/meta/doctypes) provides a generalized filtering mechanism. It accepts any whitelisted metadata key as a query parameter and returns a flexible metadata dictionary.

Examples

  • Filter by Sidebar Visibility: GET /api/meta/doctypes?show_in_desk=true
  • Filter for Submittable Records: GET /api/meta/doctypes?is_submittable=true

Unified Response Structure

The API returns a metadata dictionary for each DocType:

{
"name": "Todo",
"label": "To-Do",
"module": "Personnel",
"metadata": {
"show_in_desk": true,
"api_resource": true,
"cross_tenant_aggregation": false,
"ui_meta": {
"icon": "FileText",
"group": "Accounts",
"order": 100,
"list": {
"columns": [
{ "field": "name", "label": "ID", "width": 120 },
{ "field": "customer", "label": "Customer" }
]
}
}
}
}

4. UI Meta Property Reference

Below are the standard properties supported by the Framework M Desk shell. These can be defined directly in your DocType's Meta.ui_meta dictionary.

NamespacePropertyDescription
RootlabelSidebar navigation label (overrides DocType name).
iconNavigation icon name (e.g., Lucide icon names).
groupSidebar module grouping/category.
orderSorting order within the sidebar group (lower is higher).
create_schemaIf false, the Migration Engine skips this DocType (for Virtual DocTypes).
listcolumnsArray of { field, label, width, sortable } for the table.
defaultSort{ field, direction } (e.g., "asc", "desc").
fieldsui_widgetUI component hint (e.g., "grid", "multiselect", "password").
grid_columnsList of field names to display in a spreadsheet-style grid.
watchField name to trigger reactive re-fetching (for Dynamic Links).
formsectionsArray of { label, fields, collapsible } for layout.
tabsArray of { label, sections } for tabbed layouts.
additional_viewskanban{ groupByField, cardTitleField, cardSubtitleField }.
tree{ parentField, labelField, orderField }.
calendar{ startField, endField, titleField, colorField }.

5. UI Shell Integration

The @framework-m/desk shell utilizes this API to build its navigation dynamically. By fetching with ?show_in_desk=true, the shell maintains a lean sidebar that only shows DocTypes intended for user interaction, while still allowing the "Omni-Search" and "Info" hovers to use the expanded metadata dictionary.

[!IMPORTANT] Always register your meta keys in the MetaRegistry if you want them to be filterable or visible in the basic listing API.