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
- Developer Explicit Overrides:
Meta.ui_meta.fields(Highest priority) - App Decorations:
MetadataDecoratorRegistry(Augmented fields) - 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.
| Namespace | Property | Description |
|---|---|---|
| Root | label | Sidebar navigation label (overrides DocType name). |
icon | Navigation icon name (e.g., Lucide icon names). | |
group | Sidebar module grouping/category. | |
order | Sorting order within the sidebar group (lower is higher). | |
create_schema | If false, the Migration Engine skips this DocType (for Virtual DocTypes). | |
list | columns | Array of { field, label, width, sortable } for the table. |
defaultSort | { field, direction } (e.g., "asc", "desc"). | |
fields | ui_widget | UI component hint (e.g., "grid", "multiselect", "password"). |
grid_columns | List of field names to display in a spreadsheet-style grid. | |
watch | Field name to trigger reactive re-fetching (for Dynamic Links). | |
form | sections | Array of { label, fields, collapsible } for layout. |
tabs | Array of { label, sections } for tabbed layouts. | |
additional_views | kanban | { 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
MetaRegistryif you want them to be filterable or visible in the basic listing API.