Pagoda (formerly Airone) is a flexible Single Source of Truth (SSoT) system designed to manage structured data with dynamic schemas, complex relationships, and granular access control. The system allows organizations to define custom data models, create data instances, manage relationships between entries, and enforce fine-grained permissions at the entity, entry, and attribute levels.
This page provides a high-level introduction to the Pagoda system architecture and core concepts. For detailed information about:
Sources: README.md1-23 CHANGELOG.md1-50
Pagoda is a metadata management platform that enables:
Unlike traditional CMDBs or fixed-schema databases, Pagoda allows users to dynamically create and modify data structures through a web interface or API.
Sources: README.md8-14 entry/models.py1-100
Architecture Overview
The system follows a layered architecture with clear separation between:
All write operations that modify entries return HTTP 202 (Accepted) and execute asynchronously via the job system, ensuring consistency and enabling complex operations like webhooks and triggers.
Sources: pyproject.toml32-75 airone/settings_common.py entry/api_v2/views.py1-100
Domain Model Explanation
The domain model separates schema from data:
Entity defines the structure (like a database table), EntityAttr defines attribute schemas (like column definitions)Entry is a data instance (like a table row), Attribute is an attribute instance, AttributeValue stores versioned valuesACLBase, enabling granular permissionsJob handles asynchronous operations, TriggerParent configures event-driven actionsThis separation allows schema changes without data migration and supports temporal queries through AttributeValue.is_latest.
Sources: entry/models.py33-567 entity/models.py acl/models.py job/models.py
Request Lifecycle Details
Job instance with JobOperation typeThis pattern ensures:
Sources: entry/api_v2/views.py111-124 entry/tasks.py1-100 job/models.py
Pagoda supports 16 attribute types defined in AttrType enum:
| Type | Enum Value | Description | Storage |
|---|---|---|---|
STRING | 2 | Short text | AttributeValue.value |
TEXT | 4 | Long text | AttributeValue.value |
OBJECT | 8 | Reference to Entry | AttributeValue.referral |
NAMED_OBJECT | 16 | Entry reference with label | value + referral |
ARRAY_STRING | 32 | String array | Multiple AttributeValue children |
ARRAY_OBJECT | 64 | Entry array | Multiple children with referral |
ARRAY_NAMED_OBJECT | 128 | Named entry array | Multiple children with value + referral |
BOOLEAN | 256 | Boolean flag | AttributeValue.boolean |
GROUP | 512 | Reference to Group | AttributeValue.group |
DATE | 1024 | Date value | AttributeValue.date |
ROLE | 2048 | Reference to Role | AttributeValue.role |
ARRAY_GROUP | 4096 | Group array | Multiple children with group |
ARRAY_ROLE | 8192 | Role array | Multiple children with role |
DATETIME | 16384 | DateTime with timezone | AttributeValue.datetime |
NUMBER | 32768 | Numeric value | AttributeValue.value (as string) |
ARRAY_NUMBER | 65536 | Numeric array | Multiple children |
Array types use a parent-child pattern where AttributeValue.STATUS_DATA_ARRAY_PARENT marks the parent, and child values are linked via parent_attrv foreign key.
Sources: airone/lib/types.py entry/models.py33-87 entry/tests/test_model.py101-134
Search Capabilities
The search system provides:
Simple Search: Keyword search across entry names and attribute values
Advanced Search: Multi-attribute filtering with complex conditions using FilterKey enum:
EMPTY: Find entries with empty attribute valuesNON_EMPTY: Find entries with non-empty valuesTEXT_CONTAINED: Substring matchTEXT_NOT_CONTAINED: NegationDUPLICATED: Find duplicate attribute valuesNested Document Structure: Each entry is indexed with nested attr[] and referrals[] arrays for efficient querying
Permission Filtering: Results are filtered by ACLBase.is_readable flags
Join Attributes: Include attributes from referenced entries in search results
Sources: airone/lib/elasticsearch.py94-400 entry/services.py entry/api_v2/views.py311-450
All data-modifying operations execute asynchronously through the Job system:
Job Operations
Key job types defined in JobOperation enum:
CREATE_ENTRY: Create new entry (async to run triggers, webhooks)EDIT_ENTRY: Update entry attributesDELETE_ENTRY: Soft delete entryIMPORT_ENTRY: Bulk import from YAML/CSVEXPORT_ENTRY: Export entries to fileEXPORT_SEARCH_RESULT: Export search resultsREGISTER_REFERRALS: Update ES for entries that reference a changed entryINVOKE_TRIGGER: Execute trigger actionsNOTIFY_CREATE_ENTRY: Send webhook for creationNOTIFY_UPDATE_ENTRY: Send webhook for updateNOTIFY_DELETE_ENTRY: Send webhook for deletionJobs support:
job.is_canceled() checks)job.text field)Sources: job/models.py entry/tasks.py1-150 airone/lib/job.py
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | React 19, TypeScript, Material-UI 6 | Single-page application |
| Backend | Django 4.2, Python 3.12, DRF 3.15 | API server and business logic |
| Database | MySQL 8.0 | Relational data storage |
| Search | Elasticsearch 7.17 | Full-text search and indexing |
| Queue | RabbitMQ, Celery 5.5 | Asynchronous task processing |
| Validation | Pydantic 2.10, Zod | Runtime type validation |
| Storage | AWS S3 / Local | File storage for exports |
| Package Manager | uv, npm | Python and Node.js dependencies |
| Monitoring | Datadog APM, Flower | Application monitoring |
Deployment Configuration
Sources: pyproject.toml32-102 airone/settings_common.py .github/workflows/build-core.yml1-50
The system maintains a strict separation between schema (Entity, EntityAttr) and data (Entry, Attribute, AttributeValue). This enables:
AttributeValue.is_latest flagAll major objects inherit from acl.models.ACLBase, which provides:
is_public flag for public/private resourcesUser, Group, and RoleAll write operations follow this pattern:
The system supports backend and frontend plugins:
@register_job_task decoratorpagoda-plugin-sdk package for plugin developmentSources: entry/models.py567-577 acl/models.py plugin/sdk airone/lib/job.py
To begin working with Pagoda:
For deploying to production, see Development and Deployment.
Sources: README.md1-23 CHANGELOG.md1-50
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.