Controller Contract Specification

A controller contract defines the complete interface for a controller: what it accepts, what it returns, what capabilities it needs, and how it should be monitored.

Overview

Every controller in Magic must have a contract. The contract is a YAML or JSON file that declares:

  • Identity: Name and version
  • Input Schema: JSON Schema for request validation
  • Output Schema: JSON Schema for response validation
  • Capabilities: What system resources the controller needs
  • Resources: CPU, memory, and timeout limits
  • Observability: Logging and metrics requirements

The runtime enforces these contracts at boundaries. Requests that don't match the input schema are rejected. Responses that don't match the output schema trigger errors. Capability usage without declaration is denied.

Contract Schema

The complete contract schema:

contract.schema.yaml
# Required fields
name: string                    # Controller identifier (PascalCase)
version: string                 # Semantic version (1.0.0)

# Input/Output (required)
input:                          # JSON Schema object
  type: object
  required: []
  properties: {}

output:                         # JSON Schema object
  type: object
  required: []
  properties: {}

# Capabilities (optional, default: none)
capabilities: []                # List of capability declarations

# Resources (optional, has defaults)
resources:
  cpu: 0.5                      # CPU cores (default: 0.5)
  memory: 256Mi                 # Memory limit (default: 256Mi)
  timeout: 30s                  # Execution timeout (default: 30s)

# Observability (optional)
observability:
  log_fields: []                # Fields to include in structured logs
  metrics: []                   # Custom metrics to emit

Input & Output Schemas

Input and output schemas use JSON Schema (draft-07). The runtime validates all requests against the input schema and all responses against the output schema.

Input Schema

Example: Customer lookup input
input:
  type: object
  required:
    - customer_id
  properties:
    customer_id:
      type: string
      format: uuid
      description: The customer's unique identifier
    include_orders:
      type: boolean
      default: false
      description: Whether to include recent orders

Output Schema

Example: Customer lookup output
output:
  type: object
  required:
    - customer
    - request_id
  properties:
    customer:
      type: object
      required: [id, email, created_at]
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        created_at:
          type: string
          format: date-time
    orders:
      type: array
      items:
        $ref: "#/definitions/Order"
    request_id:
      type: string

Schema Definitions

You can use $ref to reference shared definitions within your contract or from external schema files.

Capabilities

Capabilities declare what system resources a controller needs. The runtime uses a default-deny model: if a capability isn't declared, it can't be used.

Capability Description Options
db:read Structured read queries (self.db.query, self.db.query_one) tables (allowlist)
db:write Structured write operations (self.db.insert, self.db.update, self.db.delete) tables (allowlist)
db:rawsql Raw SQL escape hatch (self.db.raw). Denied by default in production policy. Requires explicit policy approval.
http:egress Make outbound HTTP requests allow (domain list)
secrets:read Access secrets by key keys (allowlist)
queue:publish Publish to message queues queues (allowlist)
queue:subscribe Consume from queues queues (allowlist)
cache:read Read from cache prefix (key prefix)
cache:write Write to cache prefix, max_ttl
fs:temp Temporary file access max_size

Capability Examples

Detailed capability declarations
capabilities:
  # Simple capability (no options)
  - db:read

  # Database with table restrictions
  - db:write:
      tables:
        - customers
        - orders

  # HTTP egress with domain allowlist
  - http:egress:
      allow:
        - api.stripe.com
        - hooks.slack.com
        - "*.internal.company.com"

  # Secrets with key restrictions
  - secrets:read:
      keys:
        - STRIPE_API_KEY
        - SLACK_WEBHOOK_URL

  # Queue publishing
  - queue:publish:
      queues:
        - order-events
        - notifications

  # Cache with prefix and TTL limit
  - cache:write:
      prefix: "customer:"
      max_ttl: 3600

Capability Audit

All capability usage is audit-logged with the request's X-Request-ID. Enterprise deployments can stream these logs to SIEM systems.

Resource Limits

Resource limits constrain controller execution to prevent runaway processes.

Resource Default Min Max
cpu 0.5 cores 0.1 2.0
memory 256Mi 64Mi 2Gi
timeout 30s 1s 300s
Resource configuration
resources:
  cpu: 1.0          # 1 CPU core
  memory: 512Mi     # 512 MB RAM
  timeout: 60s      # 1 minute timeout

Observability

Observability configuration ensures consistent logging and metrics across controllers.

Log Fields

Specify input fields to include in structured logs:

Log field configuration
observability:
  log_fields:
    - customer_id      # Always log customer_id
    - order_id         # Always log order_id

Custom Metrics

Declare custom Prometheus metrics the controller will emit:

Metrics configuration
observability:
  metrics:
    - name: invoice_sync_duration_seconds
      type: histogram
      description: Time spent syncing invoices
      buckets: [0.1, 0.5, 1.0, 5.0, 10.0]

    - name: invoice_sync_total
      type: counter
      description: Total invoice sync operations
      labels: [status, customer_tier]

Complete Examples

Example 1: Invoice Sync Controller

invoice_sync.contract.yaml
name: InvoiceSync
version: 1.2.0

input:
  type: object
  required:
    - customer_id
    - invoice_data
  properties:
    customer_id:
      type: string
      format: uuid
    invoice_data:
      type: object
      required: [amount, currency, line_items]
      properties:
        amount:
          type: integer
          minimum: 0
        currency:
          type: string
          enum: [USD, EUR, GBP]
        line_items:
          type: array
          minItems: 1
          items:
            type: object
            required: [description, amount]
            properties:
              description:
                type: string
                maxLength: 200
              amount:
                type: integer

output:
  type: object
  required:
    - sync_id
    - status
    - request_id
  properties:
    sync_id:
      type: string
    status:
      type: string
      enum: [queued, completed, failed]
    stripe_invoice_id:
      type: string
    request_id:
      type: string

capabilities:
  - db:read:
      tables: [customers, invoices]
  - db:write:
      tables: [invoices, invoice_events]
  - http:egress:
      allow:
        - api.stripe.com
  - secrets:read:
      keys: [STRIPE_API_KEY]
  - queue:publish:
      queues: [invoice-events]

resources:
  cpu: 0.5
  memory: 256Mi
  timeout: 30s

observability:
  log_fields:
    - customer_id
    - sync_id
  metrics:
    - name: invoice_sync_duration_seconds
      type: histogram
    - name: invoice_sync_total
      type: counter
      labels: [status]

Example 2: Customer Lookup Controller (Read-Only)

customer_lookup.contract.yaml
name: CustomerLookup
version: 1.0.0

input:
  type: object
  required: [customer_id]
  properties:
    customer_id:
      type: string
      format: uuid

output:
  type: object
  required: [customer, request_id]
  properties:
    customer:
      type: object
      required: [id, email]
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        name:
          type: string
    request_id:
      type: string

capabilities:
  - db:read:
      tables: [customers]
  - cache:read:
      prefix: "customer:"

resources:
  cpu: 0.25
  memory: 128Mi
  timeout: 5s

observability:
  log_fields: [customer_id]

Contract Validation

Validate contracts before deployment:

CLI validation
# Validate a single contract (via docker compose)
docker compose exec runtime magic validate ./controllers/invoice_sync/contract.yaml

# Validate all contracts in directory
docker compose exec runtime magic validate ./controllers/

# Validate with strict mode (warnings become errors)
docker compose exec runtime magic validate --strict ./controllers/

The validator checks:

  • Schema syntax (valid YAML/JSON)
  • Required fields present
  • JSON Schema validity for input/output
  • Capability syntax
  • Resource limits within bounds
  • No conflicting declarations