Skip to content

Patterns

Auto-generated from knowledge base TOML files by docs_gen.py. Do not edit manually; run dazzle docs generate to regenerate.

Patterns are reusable DSL recipes that combine multiple constructs into proven solutions. Each pattern includes a complete, copy-paste-ready example demonstrating how entities, surfaces, workspaces, and services work together for common use cases.


Adapter Result

Result type for adapter operations using success/failure pattern instead of exceptions for expected errors.

Syntax

AdapterResult[T] = Success with data or Failure with error

result = await adapter.get_data(id)
if result.is_success:
    data = result.data  # Access the data
else:
    error = result.error  # Handle the error

Example

async def fetch_customer_data(customer_id: str):
    result = await crm_adapter.get_customer(customer_id)

    if result.is_success:
        return result.data

    # Handle specific error types
    if result.error.status_code == 404:
        return None  # Customer not found

    # Re-raise unexpected errors
    raise result.error

# Or use unwrap_or for defaults:
data = result.unwrap_or(default_data)

# Or map the result:
names = result.map(lambda data: data["name"])

Related: External Adapter, Error Normalization


Audit Trail

Track who changed what and when

Example

# Entity with full audit fields
entity Document "Document":
  id: uuid pk
  title: str(200) required
  content: text
  # Audit fields
  created_by: ref User required
  created_at: datetime auto_add
  updated_by: ref User optional
  updated_at: datetime auto_update
  version: int=1

surface document_history "Document History":
  uses entity Document
  mode: view
  section main:
    field title
    field version
  section audit "Audit Trail":
    field created_by.name "Created By"
    field created_at "Created At"
    field updated_by.name "Last Updated By"
    field updated_at "Last Updated At"

Business Logic Pattern

Complete entity with state machine, invariants, computed fields, and access rules

Example

# Complete v0.7.0 Entity with Business Logic
entity Ticket "Support Ticket":
  id: uuid pk
  ticket_number: str(20) unique
  title: str(200) required
  description: text required
  status: enum[open,in_progress,resolved,closed]=open
  priority: enum[low,medium,high,critical]=medium
  created_by: ref User required
  assigned_to: ref User
  resolution: text
  created_at: datetime auto_add
  updated_at: datetime auto_update

  # Computed field: days since opened
  days_open: computed days_since(created_at)

  # State machine: ticket lifecycle
  transitions:
    open -> in_progress: requires assigned_to
    in_progress -> resolved: requires resolution
    in_progress -> open
    resolved -> closed
    resolved -> in_progress
    closed -> open: role(manager)

  # Invariants: data integrity rules
  # IMPORTANT: Use single = for equality (not ==)
  invariant: status != resolved or resolution != null
  invariant: status != closed or resolution != null
  invariant: priority != critical or assigned_to != null

  # Access rules: visibility and permissions
  access:
    read: created_by = current_user or role(agent) or role(manager)
    write: role(agent) or role(manager)

  # Indexes for performance
  index status, priority
  index assigned_to

Related: State Machine, Invariant, Computed Field, Access Rules


Cedar Rbac

Role-based entity access using permit/forbid/audit blocks with NIST SP 800-162 alignment. Supports separation of duty, least privilege, default-deny, and audit trail requirements.

Example

# Cedar-style RBAC for a medical clinic
entity Prescription "Prescription":
  id: uuid pk
  patient: ref Patient required
  prescriber: ref Doctor required
  medication: str(200) required
  dosage: str(100) required
  status: enum[draft,active,dispensed,cancelled]=draft

  # Permit: who CAN perform actions
  permit:
    read: role(doctor) or role(pharmacist) or role(nurse)
    prescribe: role(doctor)
    dispense: role(pharmacist)
    cancel: role(doctor) or role(pharmacist)

  # Forbid: separation of duty overrides
  forbid:
    prescribe: role(pharmacist)
    dispense: role(doctor)

  # Audit: compliance logging
  audit:
    read: role(admin)
    prescribe: role(compliance_officer)
    dispense: role(compliance_officer)
    cancel: role(compliance_officer)

  transitions:
    draft -> active: role(doctor)
    active -> dispensed: role(pharmacist)
    active -> cancelled: role(doctor) or role(pharmacist)

Related: Access Rules, Role Based Access, Business Logic Pattern, Rbac Validation


Multi-Tenant Rbac

Field-condition RBAC for multi-tenant or ownership-scoped access. Combines role gates with row-level filters. See Runtime Evaluation Model for how these rules are enforced at each tier.

Example

# Multi-tenant school management — teachers see only their school's data
entity Student "Student":
  id: uuid pk
  name: str(200) required
  school: ref School required
  grade: int required

  # Pure role gate: admins see everything (Tier 1 — fast rejection)
  permit:
    list: role(admin)
    read: role(admin)

  # Field-condition filter: teachers see only their school (Tier 2 — row filter)
  permit:
    list: school = current_user.school
    read: school = current_user.school

  # Write access: teachers can update their school's students
  permit:
    update: school = current_user.school
    create: role(teacher) or role(admin)

  # Nobody outside admin can delete
  forbid:
    delete: role(teacher)

# Ownership-scoped: users see only their own records
entity Timesheet "Timesheet":
  id: uuid pk
  employee: ref User required
  hours: decimal required
  submitted: bool = false

  # Owner sees their own timesheets
  permit:
    list: employee = current_user
    read: employee = current_user
    update: employee = current_user

  # Managers see all (pure role gate)
  permit:
    list: role(manager)
    read: role(manager)

  # Only managers can delete
  permit:
    delete: role(manager)

Related: Cedar Rbac, Runtime Evaluation Model, Access Rules


Crud

Complete create-read-update-delete interface for an entity

Example

# Complete CRUD for Task entity
entity Task "Task":
  id: uuid pk
  title: str(200) required
  description: text optional
  status: enum[todo,in_progress,done]=todo
  created_at: datetime auto_add

surface task_list "Tasks":
  uses entity Task
  mode: list
  section main:
    field title
    field status
  ux:
    purpose: "View and manage all tasks"
    sort: created_at desc
    filter: status

surface task_detail "Task Details":
  uses entity Task
  mode: view
  section main:
    field title
    field description
    field status
    field created_at

surface task_create "New Task":
  uses entity Task
  mode: create
  section main:
    field title
    field description

surface task_edit "Edit Task":
  uses entity Task
  mode: edit
  section main:
    field title
    field description
    field status

Dashboard

Workspace aggregating multiple data views with metrics

Example

# Team dashboard with metrics and activity
workspace team_dashboard "Team Dashboard":
  purpose: "Real-time team overview and key metrics"

  # Urgent items needing attention
  urgent_items:
    source: Task
    filter: priority = high and status != done
    sort: due_date asc
    limit: 5
    action: task_edit
    empty: "All caught up!"

  # Recent completions
  recent_done:
    source: Task
    filter: status = done
    sort: completed_at desc
    limit: 10
    display: timeline

  # Key performance metrics
  metrics:
    aggregate:
      total: count(Task)
      completed: count(Task where status = done)
      in_progress: count(Task where status = in_progress)
      completion_rate: round(count(Task where status = done) * 100 / count(Task), 1)

Domain Service Pattern

Custom business logic with DSL declaration and stub implementation

Example

# DSL declaration in app.dsl
entity Invoice "Invoice":
  id: uuid pk
  total: decimal(10,2) required
  country: str(2) required
  vat_amount: decimal(10,2) optional

service calculate_vat "Calculate VAT for Invoice":
  kind: domain_logic
  input:
    invoice_id: uuid required
    country_code: str(2)
  output:
    vat_amount: decimal(10,2)
    breakdown: json
  guarantees:
    - "Must not mutate the invoice record"
    - "Must raise domain error if config incomplete"
  stub: python

# Generate stub file:
# $ dazzle stubs generate --service calculate_vat

# Implement in stubs/calculate_vat.py:
def calculate_vat(invoice_id: str, country_code: str | None = None) -> CalculateVatResult:
    invoice = db.get_invoice(invoice_id)
    country = country_code or invoice.country

    # VAT rates by country
    rates = {"GB": 0.20, "DE": 0.19, "FR": 0.20, "US": 0.0}
    rate = rates.get(country, 0.0)

    return {
        "vat_amount": float(invoice.total) * rate,
        "breakdown": {
            "rate": rate,
            "country": country,
            "net": float(invoice.total),
            "gross": float(invoice.total) * (1 + rate)
        }
    }

Ejection Pattern

Generate standalone application code from DAZZLE specification for custom deployment

Example

# Step 1: Add ejection configuration to dazzle.toml
[ejection]
enabled = true

[ejection.backend]
framework = "fastapi"
models = "sqlalchemy"
async_handlers = true

[ejection.frontend]
framework = "react"
api_client = "tanstack_query"

[ejection.testing]
contract = "schemathesis"
unit = "pytest"

[ejection.ci]
template = "github_actions"

[ejection.output]
directory = "generated"


# Step 2: Run the generated application
cd generated
docker compose -f docker-compose.dev.yml up

# Step 3: Run the generated tests
cd generated
pytest tests/

# Optional: Generate OpenAPI spec
dazzle specs openapi -o openapi.yaml

# Generated file structure:
generated/
├── README.md
├── docker-compose.yml
├── docker-compose.dev.yml
├── Makefile
├── .gitignore
├── backend/
│   ├── main.py
│   ├── models/
│   │   └── {entity}.py
│   ├── schemas/
│   │   └── {entity}.py
│   ├── routes/
│   │   └── {entity}.py
│   ├── guards/
│   │   └── {entity}_guards.py
│   ├── validators/
│   │   └── {entity}_validators.py
│   └── access/
│       └── {entity}_access.py
├── frontend/
│   └── src/
│       ├── types/
│       ├── schemas/
│       ├── hooks/
│       └── components/
├── tests/
│   ├── conftest.py
│   ├── contract/
│   └── unit/
└── .github/
    └── workflows/
        ├── ci.yml
        ├── contract.yml
        └── deploy.yml

Related: Ejection, Ejection Config, Ejection Adapter, Openapi Generation


Error Category

High-level error categories for routing and handling decisions in the GraphQL layer.

Syntax

ErrorCategory.AUTHENTICATION  # Redirect to login
ErrorCategory.AUTHORIZATION   # Show forbidden message
ErrorCategory.VALIDATION      # Show field-level errors
ErrorCategory.RATE_LIMIT      # Implement backoff
ErrorCategory.TIMEOUT         # Retry or show timeout
ErrorCategory.NOT_FOUND       # Show 404 message
ErrorCategory.EXTERNAL_SERVICE # Show service unavailable
ErrorCategory.INTERNAL        # Log and show generic error

Example

# Use categories to route error handling:
def handle_adapter_error(normalized: NormalizedError):
    match normalized.category:
        case ErrorCategory.AUTHENTICATION:
            return redirect_to_login()
        case ErrorCategory.RATE_LIMIT:
            return show_retry_message(normalized.retry_after)
        case ErrorCategory.VALIDATION:
            return show_field_errors(normalized.field_errors)
        case _:
            return show_generic_error(normalized.user_message)

Related: Error Normalization, Error Severity


Error Normalization

System for converting diverse external API errors into a consistent format for GraphQL responses.

Syntax

from dazzle_dnr_back.graphql.adapters import (
    normalize_error,
    NormalizedError,
    ErrorCategory,
    ErrorSeverity,
)

normalized = normalize_error(error, service_name="hmrc")

Example

from dazzle_dnr_back.graphql.adapters import (
    normalize_error,
    ErrorCategory,
    ErrorSeverity,
)

try:
    result = await hmrc_adapter.get_vat_obligations(vrn)
except AdapterError as e:
    normalized = normalize_error(e, request_id="req-123")

    # Access normalized error properties
    print(normalized.code)           # "HMRC_RATE_LIMIT_EXCEEDED"
    print(normalized.category)       # ErrorCategory.RATE_LIMIT
    print(normalized.severity)       # ErrorSeverity.WARNING
    print(normalized.user_message)   # "Too many requests. Please try again in 30 seconds."
    print(normalized.retry_after)    # 30.0

    # Convert to GraphQL error extensions
    extensions = normalized.to_graphql_extensions()
    raise GraphQLError(normalized.user_message, extensions=extensions)

Related: External Adapter, Adapter Result, Error Category


Error Severity

Error severity levels for logging and alerting decisions.

Syntax

ErrorSeverity.INFO      # Expected errors (validation, not found)
ErrorSeverity.WARNING   # Recoverable errors (rate limits, timeouts)
ErrorSeverity.ERROR     # Unexpected errors needing attention
ErrorSeverity.CRITICAL  # System errors requiring immediate action

Example

# Log based on severity:
def log_error(normalized: NormalizedError):
    log_data = normalized.to_log_dict()

    match normalized.severity:
        case ErrorSeverity.INFO:
            logger.info("Expected error", extra=log_data)
        case ErrorSeverity.WARNING:
            logger.warning("Recoverable error", extra=log_data)
        case ErrorSeverity.ERROR:
            logger.error("Unexpected error", extra=log_data)
        case ErrorSeverity.CRITICAL:
            logger.critical("System error", extra=log_data)
            alert_on_call_team(normalized)

Related: Error Normalization, Error Category


External Adapter

Abstract base class for integrating with external APIs (HMRC, banks, payment providers, etc.) with built-in retry logic, rate limiting, and error normalization.

Syntax

from dazzle_dnr_back.graphql.adapters import (
    BaseExternalAdapter,
    AdapterConfig,
    AdapterResult,
)

class MyServiceAdapter(BaseExternalAdapter[AdapterConfig]):
    async def get_data(self, id: str) -> AdapterResult[dict]:
        return await self._get(f"/api/data/{id}")

Example

from dazzle_dnr_back.graphql.adapters import (
    BaseExternalAdapter,
    AdapterConfig,
    RetryConfig,
    RateLimitConfig,
    AdapterResult,
)

class HMRCAdapter(BaseExternalAdapter[AdapterConfig]):
    """Adapter for HMRC VAT API."""

    def __init__(self, bearer_token: str):
        config = AdapterConfig(
            base_url="https://api.service.hmrc.gov.uk",
            timeout=30.0,
            headers={"Authorization": f"Bearer {bearer_token}"},
            retry=RetryConfig(max_retries=3, base_delay=1.0),
            rate_limit=RateLimitConfig(requests_per_second=4),
        )
        super().__init__(config)

    async def get_vat_obligations(
        self, vrn: str, from_date: str, to_date: str
    ) -> AdapterResult[list[dict]]:
        """Fetch VAT obligations for a business."""
        return await self._get(
            f"/organisations/vat/{vrn}/obligations",
            params={"from": from_date, "to": to_date, "status": "O"},
        )

Related: Error Normalization, Adapter Result, Graphql Bff Pattern


Graphql Bff Pattern

Backend-for-Frontend using GraphQL as the API layer between UI and backend services

Example

# GraphQL schema is auto-generated from your entities:
# entity Task → GraphQL type Task with Query/Mutation

# Inspect the generated schema:
# $ dazzle dnr inspect --schema

# Mount GraphQL endpoint in your app:
from dazzle_dnr_back.graphql import mount_graphql
mount_graphql(app, backend_spec)

# Query example:
query {
  tasks(status: "todo") {
    id
    title
    status
  }
}

# Mutation example:
mutation {
  createTask(input: {title: "New task"}) {
    id
    title
  }
}

Graphql Schema Inspection

CLI command to inspect the auto-generated GraphQL schema from entity specs.

Syntax

# Display GraphQL SDL
dazzle dnr inspect --schema

# Get schema info as JSON
dazzle dnr inspect --schema --format json

Example

$ dazzle dnr inspect --schema
📊 GraphQL Schema

type Query {
  task(id: ID!): Task
  tasks(status: String, limit: Int): [Task!]!
}

type Mutation {
  createTask(input: TaskInput!): Task!
  updateTask(id: ID!, input: TaskInput!): Task!
  deleteTask(id: ID!): Boolean!
}

type Task {
  id: ID!
  title: String!
  status: String!
  createdAt: DateTime!
}

input TaskInput {
  title: String!
  status: String
}

Related: Graphql Bff Pattern, External Adapter


Kanban Board

Status-based workflow visualization

Example

# Kanban-style task board
entity Task "Task":
  id: uuid pk
  title: str(200) required
  status: enum[backlog,todo,in_progress,review,done]=backlog
  priority: enum[low,medium,high]=medium
  assigned_to: ref User optional

workspace kanban "Task Board":
  purpose: "Visual workflow management"

  backlog:
    source: Task
    filter: status = backlog
    sort: priority desc
    display: grid
    action: task_edit

  in_progress:
    source: Task
    filter: status = in_progress
    sort: priority desc
    display: grid

  review:
    source: Task
    filter: status = review
    sort: priority desc
    display: grid

  done:
    source: Task
    filter: status = done
    sort: completed_at desc
    limit: 20
    display: grid

Llm Cognition Pattern

Entity design optimized for LLM understanding with intent, semantic tags, archetypes, and example data

Example

# v0.7.1 LLM-Optimized Entity Design

# First, define reusable archetypes
archetype Timestamped:
  created_at: datetime auto_add
  updated_at: datetime auto_update

archetype Auditable:
  created_by: ref User
  updated_by: ref User

# Main entity with full LLM cognition features
entity Invoice "Invoice":
  intent: "Represent a finalized billing request from vendor to customer"
  domain: billing
  patterns: lifecycle, line_items, audit
  extends: Timestamped, Auditable

  id: uuid pk
  invoice_number: str(50) unique required
  status: enum[draft,sent,paid,overdue,cancelled] = draft
  customer: ref Customer required
  items: has_many InvoiceItem cascade
  subtotal: decimal(10,2) required
  tax_amount: decimal(10,2)
  total: decimal(10,2) required
  due_date: date required
  paid_at: datetime

  # Computed fields
  days_until_due: computed days_since(due_date)
  is_overdue: computed due_date < today and status != paid

  # State machine
  transitions:
    draft -> sent: requires customer
    sent -> paid: requires paid_at
    sent -> overdue: auto after 30 days
    sent -> cancelled: role(admin)
    overdue -> paid: requires paid_at
    * -> cancelled: role(admin)

  # Invariants with messages
  invariant: total = subtotal + tax_amount
    message: "Total must equal subtotal plus tax"
    code: INVOICE_TOTAL_MISMATCH

  invariant: status != paid or paid_at != null
    message: "Paid invoices must have a payment date"
    code: INVOICE_MISSING_PAYMENT_DATE

  # Access rules
  access:
    read: role(accountant) or role(admin) or customer = current_user.company
    write: role(accountant) or role(admin)

  # Example data
  examples:
    {invoice_number: "INV-2024-001", status: draft, subtotal: 1000.00, tax_amount: 200.00, total: 1200.00}
    {invoice_number: "INV-2024-002", status: sent, subtotal: 500.00, tax_amount: 100.00, total: 600.00}
    {invoice_number: "INV-2024-003", status: paid, subtotal: 750.00, tax_amount: 150.00, total: 900.00}

entity InvoiceItem "Invoice Line Item":
  intent: "Single line item on an invoice with quantity and pricing"
  domain: billing
  patterns: line_items
  extends: Timestamped

  id: uuid pk
  invoice: belongs_to Invoice
  description: str(500) required
  quantity: int required
  unit_price: decimal(10,2) required
  line_total: computed quantity * unit_price

  invariant: quantity > 0
    message: "Quantity must be positive"
    code: ITEM_INVALID_QUANTITY

  examples:
    {description: "Consulting hours", quantity: 10, unit_price: 150.00}
    {description: "Software license", quantity: 5, unit_price: 99.00}

Related: Intent, Archetype, Domain Patterns, Examples, Invariant Message, Relationships


Master Detail

Parent-child relationship with nested views

Example

# Project with nested tasks
entity Project "Project":
  id: uuid pk
  name: str(200) required
  status: enum[active,completed,archived]=active

entity Task "Task":
  id: uuid pk
  title: str(200) required
  project: ref Project required
  status: enum[todo,done]=todo

surface project_list "Projects":
  uses entity Project
  mode: list
  section main:
    field name
    field status

surface project_detail "Project":
  uses entity Project
  mode: view
  section info:
    field name
    field status
  section tasks "Tasks":
    uses entity Task
    filter: project = this
    field title
    field status

Notifications

Alert users to important events

Example

# Surface with attention signals
surface order_list "Orders":
  uses entity Order
  mode: list

  section main:
    field order_number
    field customer.name
    field status
    field total

  ux:
    purpose: "Process and fulfill orders"

    # Payment failed - critical
    attention critical:
      when: payment_status = failed
      message: "Payment failed!"
      action: order_retry_payment

    # Shipping delayed
    attention warning:
      when: days_since(shipped_at) > 5 and status = shipped
      message: "Delayed shipment"

    # New order
    attention notice:
      when: status = new and created_at > today
      message: "New order today"

Role Based Access

Persona variants controlling scope and capabilities

Example

# Role-based access with personas
surface ticket_list "Support Tickets":
  uses entity Ticket
  mode: list

  section main:
    field subject
    field status
    field assigned_to.name

  ux:
    purpose: "Manage support tickets by role"

    # Admins see everything, can reassign
    for admin:
      scope: all
      action_primary: ticket_assign
      show_aggregate: total, open, resolved_today

    # Agents see assigned + unassigned
    for agent:
      scope: assigned_to = current_user or assigned_to = null
      action_primary: ticket_respond

    # Customers see only their tickets
    for customer:
      scope: created_by = current_user
      hide: internal_notes, assigned_to
      read_only: true

Search Filter

Full-text search with faceted filtering

Example

# Searchable product catalog
entity Product "Product":
  id: uuid pk
  name: str(200) required
  description: text
  category: enum[electronics,clothing,home,other]
  price: decimal(10,2)
  in_stock: bool=true

surface product_catalog "Products":
  uses entity Product
  mode: list

  section main:
    field name
    field category
    field price
    field in_stock

  ux:
    purpose: "Browse and find products"
    search: name, description
    filter: category, in_stock
    sort: name asc
    empty: "No products match your search"

Settings Archetype

System-wide configuration using the settings semantic archetype for admin-only singleton entities

Example

# v0.10.3: System-wide settings entity
entity AppSettings "Application Settings":
    archetype: settings
    id: uuid pk
    timezone: timezone = "UTC"          # IANA timezone (e.g., "Europe/London")
    default_currency: str(3) = "GBP"
    maintenance_mode: bool = false
    max_upload_size_mb: int = 10
    support_email: email

# What happens automatically:
# 1. Entity is marked as singleton (is_singleton = true)
# 2. Admin-only access rules are applied
# 3. Settings surface is auto-generated (if not explicitly defined):
#    - Name: app_settings_settings
#    - Mode: edit
#    - Access: admin only
#    - Route: /admin/settings/app_settings
#
# The singleton ensures only one record exists in the database.
# First access will create the record; subsequent accesses update it.

Related: Tenant Archetype, Tenant Settings Archetype, Timezone Field


Tenant Archetype

Multi-tenant root entity that defines tenant boundaries and automatically injects tenant FK into other entities

Example

# v0.10.3: Multi-tenant application
entity Company "Company":
    archetype: tenant
    id: uuid pk
    name: str(200) required
    slug: str(50) required unique
    plan: enum[free,pro,enterprise] = free
    created_at: datetime auto_add

# Other entities automatically get tenant FK injected
entity Contact "Contact":
    id: uuid pk
    name: str(200) required
    email: email required
    # company: ref Company  <-- auto-injected by archetype expander

entity Project "Project":
    id: uuid pk
    name: str(200) required
    status: enum[active,completed,archived] = active
    # company: ref Company  <-- auto-injected

# What happens automatically:
# 1. Company is marked as tenant root (is_tenant_root = true)
# 2. All other entities (except archetype: settings) get company FK
# 3. Tenant admin surface is auto-generated:
#    - Name: company_admin
#    - Mode: list
#    - Access: admin only
#    - Route: /admin/tenants

Related: Settings Archetype, Tenant Settings Archetype


Tenant Settings Archetype

Per-tenant configuration using tenant_settings archetype for tenant-scoped singleton entities

Example

# v0.10.3: Complete multi-tenant app with settings
entity Company "Company":
    archetype: tenant
    id: uuid pk
    name: str(200) required
    slug: str(50) required unique

entity CompanySettings "Company Settings":
    archetype: tenant_settings
    id: uuid pk
    company: ref Company              # Explicit tenant ref
    timezone: timezone                # Override system default
    logo_url: url
    invoice_prefix: str(10) = "INV"
    default_payment_terms: int = 30   # days

# System-wide settings (not tenant-scoped)
entity SystemSettings "System Settings":
    archetype: settings
    id: uuid pk
    timezone: timezone = "UTC"
    maintenance_mode: bool = false

# What happens automatically:
# 1. CompanySettings is marked as singleton (per-tenant)
# 2. Tenant admin access rules are applied
# 3. Settings surface is auto-generated:
#    - Name: company_settings_settings
#    - Mode: edit
#    - Access: tenant admin only
#    - Route: /settings/company_settings
#
# Note: SystemSettings is NOT tenant-scoped because it has
# archetype: settings (not tenant_settings)

Related: Settings Archetype, Tenant Archetype, Timezone Field


User Archetype

Core user entity with built-in authentication fields for local auth and OAuth/SSO support

Example

# v0.10.4: User entity with automatic auth fields
entity User "User":
    archetype: user
    id: uuid pk
    email: email required unique
    name: str(200) required
    avatar_url: url optional

# What gets auto-injected:
# - password_hash: str(255) optional      # For local auth
# - email_verified: bool = false
# - email_verify_token: str(100) optional
# - password_reset_token: str(100) optional
# - password_reset_expires: datetime optional
# - is_active: bool = true
# - last_login: datetime optional
# - auth_provider: enum[local,google,apple,github] = local
# - auth_provider_id: str(255) optional    # External provider ID
# - created_at: datetime auto_add

# Auto-generated surfaces (admin-only):
# - user_list: List all users
# - user_view: View user details
# - user_create: Create new user
# - user_edit: Edit user

# Important: User entity is system-wide (not tenant-scoped)
# Use UserMembership for tenant-user relationships

Related: User Membership Archetype, Tenant Archetype


User Membership Archetype

User-tenant relationship with per-tenant personas (roles) for multi-tenant applications

Example

# v0.10.4: Complete multi-tenant user management
entity Company "Company":
    archetype: tenant
    id: uuid pk
    name: str(200) required
    slug: str(50) required unique

entity User "User":
    archetype: user
    id: uuid pk
    email: email required unique
    name: str(200) required

entity UserMembership "User Membership":
    archetype: user_membership
    id: uuid pk
    # Optionally define explicit refs (otherwise auto-injected):
    # user: ref User required
    # company: ref Company required

# What gets auto-injected:
# - user: ref User required              # Link to user
# - company: ref Company required        # Link to tenant
# - personas: json = []                  # ["admin", "member", "billing"]
# - is_primary: bool = false             # Primary membership flag
# - invited_by: ref User optional        # Who sent the invite
# - invited_at: datetime auto_add        # When invited
# - accepted_at: datetime optional       # When accepted

# Auto-generated surfaces (admin-only):
# - user_membership_list: List all memberships
# - user_membership_edit: Edit membership (assign personas)

# Usage: Assign personas to control per-tenant access
# personas: ["admin"]           -> Full tenant admin
# personas: ["member"]          -> Standard member
# personas: ["billing", "member"] -> Member with billing access

Related: User Archetype, Tenant Archetype