Skip to content

Deployment Guide

This guide covers deploying Mistaber in production environments, from containerized deployments to cloud platforms and CI/CD integration.

Prerequisites

Before deploying, ensure you have:

  • Python 3.10+ available in your target environment
  • Clingo 5.6.0+ ASP solver
  • Access to the ontology files
  • Sufficient memory (2GB minimum recommended)

Docker Deployment

Dockerfile for Mistaber

Create a production-ready Dockerfile:

# Dockerfile
FROM python:3.11-slim-bookworm

# Install system dependencies for clingo
RUN apt-get update && apt-get install -y --no-install-recommends \
    gringo \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /app

# Copy dependency files first for better layer caching
COPY pyproject.toml ./
COPY README.md ./

# Install Python dependencies
RUN pip install --no-cache-dir clingo click lark pyyaml

# Copy application code
COPY mistaber/ ./mistaber/

# Install mistaber package
RUN pip install --no-cache-dir -e .

# Create non-root user for security
RUN useradd --create-home --shell /bin/bash mistaber && \
    chown -R mistaber:mistaber /app
USER mistaber

# Set default ontology path
ENV MISTABER_ONTOLOGY=/app/mistaber/ontology

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD python -c "from mistaber.engine import HsrsEngine; print('healthy')" || exit 1

# Default command
ENTRYPOINT ["mistaber"]
CMD ["--help"]

Multi-Stage Build for Smaller Images

For production deployments where image size matters:

# Dockerfile.production
# Build stage
FROM python:3.11-slim-bookworm AS builder

RUN apt-get update && apt-get install -y --no-install-recommends \
    gringo \
    build-essential

WORKDIR /build
COPY pyproject.toml README.md ./
COPY mistaber/ ./mistaber/

RUN pip install --no-cache-dir build && \
    python -m build --wheel

# Runtime stage
FROM python:3.11-slim-bookworm AS runtime

# Install only runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    gringo \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy wheel from builder
COPY --from=builder /build/dist/*.whl ./

# Install the package
RUN pip install --no-cache-dir *.whl clingo && rm *.whl

# Copy ontology separately (can be mounted as volume)
COPY mistaber/ontology/ ./ontology/

# Non-root user
RUN useradd --create-home mistaber && chown -R mistaber:mistaber /app
USER mistaber

ENV MISTABER_ONTOLOGY=/app/ontology
ENTRYPOINT ["mistaber"]

Docker Compose Setup

For development and testing with multiple services:

# docker-compose.yml
version: '3.8'

services:
  mistaber:
    build:
      context: .
      dockerfile: Dockerfile
    image: mistaber:latest
    container_name: mistaber-engine
    volumes:
      # Mount ontology for live updates during development
      - ./mistaber/ontology:/app/mistaber/ontology:ro
      # Mount custom extensions
      - ./extensions:/app/extensions:ro
    environment:
      - MISTABER_ONTOLOGY=/app/mistaber/ontology
      - MISTABER_FORMAT=json
    # Keep container running for interactive use
    command: ["tail", "-f", "/dev/null"]
    healthcheck:
      test: ["CMD", "python", "-c", "from mistaber.engine import HsrsEngine; print('ok')"]
      interval: 30s
      timeout: 10s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 512M

  # Optional: API wrapper service
  mistaber-api:
    build:
      context: .
      dockerfile: Dockerfile.api
    image: mistaber-api:latest
    container_name: mistaber-api
    ports:
      - "8080:8080"
    environment:
      - MISTABER_ONTOLOGY=/app/mistaber/ontology
    depends_on:
      mistaber:
        condition: service_healthy
    volumes:
      - ./mistaber/ontology:/app/mistaber/ontology:ro

  # Optional: Redis for query caching
  cache:
    image: redis:7-alpine
    container_name: mistaber-cache
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  redis-data:

Building and Running

# Build the image
docker build -t mistaber:latest .

# Run a query
docker run --rm mistaber:latest -o /app/mistaber/ontology query "world(X)"

# Run interactively
docker run -it --rm mistaber:latest /bin/bash

# Run with mounted ontology (for development)
docker run --rm -v $(pwd)/mistaber/ontology:/app/mistaber/ontology:ro \
    mistaber:latest query "world(X)"

# Using Docker Compose
docker compose up -d
docker compose exec mistaber mistaber query "world(X)"

Volume Mounts for Ontology

For production deployments, mount the ontology as a read-only volume to enable updates without rebuilding:

# Production deployment with external ontology
docker run -d \
    --name mistaber-prod \
    -v /data/mistaber-ontology:/app/ontology:ro \
    -e MISTABER_ONTOLOGY=/app/ontology \
    mistaber:latest

Environment Variables

Variable Description Default
MISTABER_ONTOLOGY Path to ontology directory ./mistaber/ontology
MISTABER_FORMAT Default output format (text, json) text
MISTABER_DEBUG Enable debug output (0, 1) 0
MISTABER_CACHE_TTL Query cache TTL in seconds 300

Cloud Deployment

AWS Deployment

EC2 Instance Setup

  1. Launch an EC2 instance:

    • AMI: Amazon Linux 2023 or Ubuntu 22.04 LTS
    • Instance type: t3.medium (2 vCPU, 4GB RAM) minimum
    • Storage: 20GB gp3 SSD
  2. Install dependencies:

    # Amazon Linux 2023
    sudo dnf install python3.11 python3.11-pip git
    
    # Ubuntu 22.04
    sudo apt update
    sudo apt install python3.11 python3.11-venv python3-pip git
    
  3. Install Mistaber:

    git clone https://github.com/BrainyBlaze/mistraber.git
    cd mistaber
    python3.11 -m venv venv
    source venv/bin/activate
    pip install -e .
    
  4. Configure as a systemd service:

    # /etc/systemd/system/mistaber.service
    [Unit]
    Description=Mistaber Halachic Reasoning Engine
    After=network.target
    
    [Service]
    Type=simple
    User=ubuntu
    WorkingDirectory=/home/ubuntu/mistaber
    Environment=PATH=/home/ubuntu/mistaber/venv/bin:/usr/local/bin:/usr/bin
    Environment=MISTABER_ONTOLOGY=/home/ubuntu/mistaber/mistaber/ontology
    ExecStart=/home/ubuntu/mistaber/venv/bin/python -m mistaber.api
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    
    sudo systemctl daemon-reload
    sudo systemctl enable mistaber
    sudo systemctl start mistaber
    

AWS Lambda for Serverless Queries

For serverless query execution:

# lambda_handler.py
import json
import os
from pathlib import Path
from mistaber.engine import HsrsEngine

# Initialize engine at module level for warm starts
ONTOLOGY_PATH = os.environ.get('ONTOLOGY_PATH', '/opt/ontology')
engine = None

def get_engine():
    global engine
    if engine is None:
        engine = HsrsEngine(Path(ONTOLOGY_PATH))
    return engine

def lambda_handler(event, context):
    """AWS Lambda handler for Mistaber queries."""
    try:
        body = json.loads(event.get('body', '{}'))
        action = body.get('action', 'query')
        pattern = body.get('pattern')
        world = body.get('world')

        eng = get_engine()

        if action == 'query':
            results = eng.query(pattern)
            return {
                'statusCode': 200,
                'body': json.dumps({'results': results})
            }
        elif action == 'ask':
            result = eng.ask(pattern, world=world)
            return {
                'statusCode': 200,
                'body': json.dumps({'result': result})
            }
        elif action == 'compare':
            worlds = body.get('worlds', [])
            results = eng.compare(pattern, worlds=worlds if worlds else None)
            return {
                'statusCode': 200,
                'body': json.dumps({'results': results})
            }
        else:
            return {
                'statusCode': 400,
                'body': json.dumps({'error': f'Unknown action: {action}'})
            }

    except Exception as e:
        return {
            'statusCode': 500,
            'body': json.dumps({'error': str(e)})
        }

Lambda Layer for Dependencies:

# Create a Lambda layer with dependencies
mkdir -p layer/python
pip install clingo click lark pyyaml -t layer/python/
cd layer
zip -r ../mistaber-deps-layer.zip .

# Upload ontology as separate layer
mkdir -p ontology-layer/opt
cp -r mistaber/ontology ontology-layer/opt/
cd ontology-layer
zip -r ../mistaber-ontology-layer.zip .

SAM Template:

# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  MistaberFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: lambda_handler.lambda_handler
      Runtime: python3.11
      CodeUri: ./lambda/
      MemorySize: 1024
      Timeout: 30
      Environment:
        Variables:
          ONTOLOGY_PATH: /opt/ontology
      Layers:
        - !Ref MistaberDepsLayer
        - !Ref MistaberOntologyLayer
      Events:
        Api:
          Type: Api
          Properties:
            Path: /query
            Method: post

  MistaberDepsLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: mistaber-deps
      ContentUri: ./mistaber-deps-layer.zip
      CompatibleRuntimes:
        - python3.11

  MistaberOntologyLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: mistaber-ontology
      ContentUri: ./mistaber-ontology-layer.zip
      CompatibleRuntimes:
        - python3.11

S3 for Ontology Storage

Store and version ontology files in S3:

# ontology_loader.py
import boto3
import tempfile
from pathlib import Path

def load_ontology_from_s3(bucket: str, prefix: str = 'ontology/') -> Path:
    """Download ontology from S3 to local temp directory."""
    s3 = boto3.client('s3')
    temp_dir = Path(tempfile.mkdtemp())

    paginator = s3.get_paginator('list_objects_v2')
    for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
        for obj in page.get('Contents', []):
            key = obj['Key']
            local_path = temp_dir / key.replace(prefix, '')
            local_path.parent.mkdir(parents=True, exist_ok=True)
            s3.download_file(bucket, key, str(local_path))

    return temp_dir

GCP Deployment

Compute Engine Setup

# Create a VM
gcloud compute instances create mistaber-vm \
    --zone=us-central1-a \
    --machine-type=e2-medium \
    --image-family=ubuntu-2204-lts \
    --image-project=ubuntu-os-cloud \
    --boot-disk-size=20GB

# SSH into the instance
gcloud compute ssh mistaber-vm --zone=us-central1-a

# Install dependencies (on the VM)
sudo apt update
sudo apt install python3.11 python3.11-venv python3-pip git gringo

# Clone and install
git clone https://github.com/BrainyBlaze/mistraber.git
cd mistaber
python3.11 -m venv venv
source venv/bin/activate
pip install -e .

Cloud Functions

# main.py - GCP Cloud Function
import json
from pathlib import Path
from mistaber.engine import HsrsEngine

# Global engine instance for function warm starts
_engine = None

def get_engine():
    global _engine
    if _engine is None:
        _engine = HsrsEngine(Path('/workspace/ontology'))
    return _engine

def mistaber_query(request):
    """HTTP Cloud Function for Mistaber queries."""
    request_json = request.get_json(silent=True)

    if not request_json:
        return json.dumps({'error': 'No JSON body provided'}), 400

    pattern = request_json.get('pattern')
    if not pattern:
        return json.dumps({'error': 'Missing pattern parameter'}), 400

    try:
        engine = get_engine()
        results = engine.query(pattern)
        return json.dumps({'results': results}), 200
    except Exception as e:
        return json.dumps({'error': str(e)}), 500

Deployment:

gcloud functions deploy mistaber-query \
    --runtime python311 \
    --trigger-http \
    --allow-unauthenticated \
    --memory 1024MB \
    --timeout 30s \
    --source .

Cloud Storage for Ontology

# Create bucket
gsutil mb gs://mistaber-ontology

# Upload ontology
gsutil -m cp -r mistaber/ontology gs://mistaber-ontology/

# Set up versioning
gsutil versioning set on gs://mistaber-ontology

Azure Deployment

VM Setup

# Create resource group
az group create --name mistaber-rg --location eastus

# Create VM
az vm create \
    --resource-group mistaber-rg \
    --name mistaber-vm \
    --image Ubuntu2204 \
    --size Standard_B2s \
    --admin-username azureuser \
    --generate-ssh-keys

# Get public IP
az vm show -d -g mistaber-rg -n mistaber-vm --query publicIps -o tsv

# SSH and install
ssh azureuser@<public-ip>
# Follow Ubuntu installation steps from above

Azure Functions

# __init__.py - Azure Function
import json
import logging
import azure.functions as func
from pathlib import Path
from mistaber.engine import HsrsEngine

# Initialize engine
engine = HsrsEngine(Path('/home/site/wwwroot/ontology'))

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Mistaber query function triggered')

    try:
        req_body = req.get_json()
        pattern = req_body.get('pattern')

        if not pattern:
            return func.HttpResponse(
                json.dumps({'error': 'Missing pattern'}),
                status_code=400,
                mimetype='application/json'
            )

        results = engine.query(pattern)
        return func.HttpResponse(
            json.dumps({'results': results}),
            status_code=200,
            mimetype='application/json'
        )

    except Exception as e:
        return func.HttpResponse(
            json.dumps({'error': str(e)}),
            status_code=500,
            mimetype='application/json'
        )

Blob Storage for Ontology

# Create storage account
az storage account create \
    --name mistaberontology \
    --resource-group mistaber-rg \
    --location eastus \
    --sku Standard_LRS

# Create container
az storage container create \
    --name ontology \
    --account-name mistaberontology

# Upload ontology
az storage blob upload-batch \
    --destination ontology \
    --source ./mistaber/ontology \
    --account-name mistaberontology

CI/CD Integration

GitHub Actions Workflow

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install ruff
        run: pip install ruff

      - name: Run linting
        run: |
          ruff check .
          ruff format --check .

  type-check:
    name: Type Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install -e ".[dev]"

      - name: Run mypy
        run: mypy mistaber/ --ignore-missing-imports

  test:
    name: Test (Python ${{ matrix.python-version }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.10', '3.11', '3.12']

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install system dependencies
        run: sudo apt-get install -y gringo

      - name: Install dependencies
        run: pip install -e ".[dev]"

      - name: Run tests
        run: pytest --tb=short -v

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        if: matrix.python-version == '3.11'

  ontology-validation:
    name: Validate Ontology
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -e .

      - name: Validate ontology loads
        run: |
          python -c "
          from pathlib import Path
          from mistaber.engine import HsrsEngine
          engine = HsrsEngine(Path('mistaber/ontology'))
          assert engine.is_loaded, 'Ontology failed to load'
          worlds = engine.query('world(X)')
          assert len(worlds) >= 7, f'Expected 7+ worlds, got {len(worlds)}'
          print('Ontology validation passed')
          "

      - name: Check for UNSAT
        run: |
          clingo mistaber/ontology/worlds/*.lp mistaber/ontology/base/*.lp \
            mistaber/ontology/schema/*.lp -n 1 --warn=none

  build:
    name: Build Package
    runs-on: ubuntu-latest
    needs: [lint, type-check, test, ontology-validation]
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install build tools
        run: pip install build

      - name: Build package
        run: python -m build

      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: dist
          path: dist/

  docker:
    name: Build Docker Image
    runs-on: ubuntu-latest
    needs: [build]
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:latest
            ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Pre-commit Hooks

Configure pre-commit to run checks locally before commits:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.9
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.8.0
    hooks:
      - id: mypy
        additional_dependencies: [types-PyYAML]
        args: [--ignore-missing-imports]

  - repo: local
    hooks:
      - id: ontology-check
        name: Validate Ontology
        entry: python -c "from pathlib import Path; from mistaber.engine import HsrsEngine; HsrsEngine(Path('mistaber/ontology'))"
        language: system
        pass_filenames: false
        files: \.lp$

      - id: pytest-check
        name: Run Tests
        entry: pytest --tb=short -q
        language: system
        pass_filenames: false
        stages: [push]

Installation:

pip install pre-commit
pre-commit install
pre-commit install --hook-type pre-push

Documentation Deployment

# .github/workflows/docs.yml
name: Documentation

on:
  push:
    branches: [main]
    paths:
      - 'docs/**'
      - 'mkdocs.yml'
      - 'mistaber/**'
  workflow_dispatch:

permissions:
  contents: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install -e .
          pip install -r docs-requirements.txt

      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Build and deploy
        run: mkdocs gh-deploy --force

Performance Tuning

Grounding Optimization

The grounding phase can be the bottleneck for large ontologies:

% Use projection to reduce output size
#project asserts/2.
#project holds/2.
#project override/3.

% Add domain constraints early
:- asserts(W, P), not world(W).
:- asserts(W, issur(A, F, L)), not action(A).
:- asserts(W, issur(A, F, L)), not food(F).
:- asserts(W, issur(A, F, L)), not madrega(L).

Memory Management

# Reuse engine instances
from pathlib import Path
from mistaber.engine import HsrsEngine

class MistaberService:
    _engine = None

    @classmethod
    def get_engine(cls) -> HsrsEngine:
        if cls._engine is None:
            cls._engine = HsrsEngine(Path("mistaber/ontology"))
        return cls._engine

    @classmethod
    def reset_engine(cls):
        """Force reload of ontology (use sparingly)."""
        cls._engine = None

Query Caching

Implement caching for frequently repeated queries:

import hashlib
import json
from functools import lru_cache
from typing import Optional
import redis

class CachedEngine:
    def __init__(self, engine, redis_client: Optional[redis.Redis] = None):
        self.engine = engine
        self.redis = redis_client
        self.cache_ttl = 300  # 5 minutes

    def _cache_key(self, method: str, *args, **kwargs) -> str:
        data = json.dumps({'method': method, 'args': args, 'kwargs': kwargs}, sort_keys=True)
        return f"mistaber:{hashlib.md5(data.encode()).hexdigest()}"

    def query(self, pattern: str) -> list:
        if self.redis:
            key = self._cache_key('query', pattern)
            cached = self.redis.get(key)
            if cached:
                return json.loads(cached)

        result = self.engine.query(pattern)

        if self.redis:
            self.redis.setex(key, self.cache_ttl, json.dumps(result))

        return result

    def invalidate_cache(self):
        """Clear all cached queries."""
        if self.redis:
            for key in self.redis.scan_iter("mistaber:*"):
                self.redis.delete(key)

Parallel Processing

For batch queries, use parallel processing:

from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path

def query_worker(args):
    pattern, ontology_path = args
    from mistaber.engine import HsrsEngine
    engine = HsrsEngine(Path(ontology_path))
    return pattern, engine.query(pattern)

def batch_query(patterns: list, ontology_path: str, max_workers: int = 4):
    """Execute multiple queries in parallel."""
    results = {}
    args_list = [(p, ontology_path) for p in patterns]

    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(query_worker, args): args[0] for args in args_list}

        for future in as_completed(futures):
            pattern, result = future.result()
            results[pattern] = result

    return results

Monitoring & Logging

Structured Logging

import logging
import json
from datetime import datetime

class JSONFormatter(logging.Formatter):
    def format(self, record):
        log_record = {
            'timestamp': datetime.utcnow().isoformat(),
            'level': record.levelname,
            'message': record.getMessage(),
            'module': record.module,
            'function': record.funcName,
        }
        if record.exc_info:
            log_record['exception'] = self.formatException(record.exc_info)
        return json.dumps(log_record)

# Configure logging
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger = logging.getLogger('mistaber')
logger.addHandler(handler)
logger.setLevel(logging.INFO)

Query Metrics

import time
from dataclasses import dataclass, field
from typing import Dict, List

@dataclass
class QueryMetrics:
    total_queries: int = 0
    total_time_ms: float = 0.0
    queries_by_pattern: Dict[str, int] = field(default_factory=dict)
    slow_queries: List[dict] = field(default_factory=list)

    def record_query(self, pattern: str, duration_ms: float):
        self.total_queries += 1
        self.total_time_ms += duration_ms
        self.queries_by_pattern[pattern] = self.queries_by_pattern.get(pattern, 0) + 1

        if duration_ms > 1000:  # Slow query threshold: 1 second
            self.slow_queries.append({
                'pattern': pattern,
                'duration_ms': duration_ms,
                'timestamp': time.time()
            })

    def get_stats(self) -> dict:
        return {
            'total_queries': self.total_queries,
            'avg_query_time_ms': self.total_time_ms / max(self.total_queries, 1),
            'top_patterns': sorted(
                self.queries_by_pattern.items(),
                key=lambda x: x[1],
                reverse=True
            )[:10],
            'slow_query_count': len(self.slow_queries)
        }

# Usage
metrics = QueryMetrics()

def timed_query(engine, pattern):
    start = time.perf_counter()
    result = engine.query(pattern)
    duration_ms = (time.perf_counter() - start) * 1000
    metrics.record_query(pattern, duration_ms)
    return result

Health Endpoints

from flask import Flask, jsonify
from pathlib import Path
from mistaber.engine import HsrsEngine

app = Flask(__name__)
engine = HsrsEngine(Path("mistaber/ontology"))

@app.route('/health')
def health():
    """Basic health check."""
    return jsonify({'status': 'healthy'})

@app.route('/health/ready')
def readiness():
    """Readiness check - verify engine is loaded."""
    if engine.is_loaded:
        return jsonify({'status': 'ready', 'ontology_loaded': True})
    return jsonify({'status': 'not_ready', 'ontology_loaded': False}), 503

@app.route('/health/live')
def liveness():
    """Liveness check - verify basic query works."""
    try:
        worlds = engine.query("world(X)")
        return jsonify({
            'status': 'live',
            'world_count': len(worlds)
        })
    except Exception as e:
        return jsonify({'status': 'unhealthy', 'error': str(e)}), 503

Backup & Recovery

Ontology Versioning

Use Git for ontology version control:

# Initialize ontology as separate repo (if not already)
cd mistaber/ontology
git init
git add .
git commit -m "Initial ontology commit"

# Tag releases
git tag -a v1.0.0 -m "Basar Bechalav initial release"
git push origin v1.0.0

# Create branches for different traditions
git checkout -b sefardi-extensions
# Make changes
git commit -m "Add Yalkut Yosef specific rulings"

State Backup

For deployments with session state:

#!/bin/bash
# backup-state.sh

BACKUP_DIR="/backups/mistaber/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"

# Backup ontology
cp -r mistaber/ontology "$BACKUP_DIR/"

# Backup session state (if using encoding plugin)
cp .mistaber-session.yaml "$BACKUP_DIR/" 2>/dev/null || true
cp -r .mistaber-artifacts "$BACKUP_DIR/" 2>/dev/null || true

# Backup configuration
cp mistaber.yaml "$BACKUP_DIR/" 2>/dev/null || true

# Compress
tar -czf "$BACKUP_DIR.tar.gz" -C "$(dirname $BACKUP_DIR)" "$(basename $BACKUP_DIR)"
rm -rf "$BACKUP_DIR"

echo "Backup created: $BACKUP_DIR.tar.gz"

Disaster Recovery

#!/bin/bash
# restore-state.sh

BACKUP_FILE="$1"

if [ -z "$BACKUP_FILE" ]; then
    echo "Usage: restore-state.sh <backup-file.tar.gz>"
    exit 1
fi

# Create temp directory
TEMP_DIR=$(mktemp -d)
tar -xzf "$BACKUP_FILE" -C "$TEMP_DIR"

# Find the backup directory
BACKUP_DIR=$(find "$TEMP_DIR" -maxdepth 1 -type d | tail -1)

# Restore ontology
cp -r "$BACKUP_DIR/ontology"/* mistaber/ontology/

# Restore session state
cp "$BACKUP_DIR/.mistaber-session.yaml" . 2>/dev/null || true
cp -r "$BACKUP_DIR/.mistaber-artifacts" . 2>/dev/null || true

# Cleanup
rm -rf "$TEMP_DIR"

echo "Restore complete from $BACKUP_FILE"

Security Considerations

Access Control

For API deployments, implement authentication:

from functools import wraps
from flask import request, jsonify
import jwt

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization', '').replace('Bearer ', '')
        if not token:
            return jsonify({'error': 'Missing authorization token'}), 401

        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
            request.user = payload
        except jwt.InvalidTokenError:
            return jsonify({'error': 'Invalid token'}), 401

        return f(*args, **kwargs)
    return decorated

@app.route('/query', methods=['POST'])
@require_auth
def query_endpoint():
    # ... query logic
    pass

Input Validation

Always validate query patterns:

import re

SAFE_PATTERN = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*\([^()]*\)$')

def validate_pattern(pattern: str) -> bool:
    """Validate that pattern is a safe ASP atom pattern."""
    # Basic syntax check
    if not SAFE_PATTERN.match(pattern):
        return False

    # Check for injection attempts
    dangerous = ['#include', '#script', '#external', 'system(', 'exec(']
    for d in dangerous:
        if d.lower() in pattern.lower():
            return False

    return True

def safe_query(engine, pattern: str):
    if not validate_pattern(pattern):
        raise ValueError(f"Invalid or unsafe pattern: {pattern}")
    return engine.query(pattern)

Data Privacy

For deployments handling sensitive queries:

import hashlib
import logging

def anonymize_pattern(pattern: str) -> str:
    """Hash variable values in pattern for logging."""
    # Simple anonymization - hash any quoted strings
    import re
    def hash_match(match):
        return f'"{hashlib.sha256(match.group(1).encode()).hexdigest()[:8]}"'
    return re.sub(r'"([^"]*)"', hash_match, pattern)

# Log anonymized queries
logger.info(f"Query executed: {anonymize_pattern(pattern)}")

Quick Reference: Deployment Checklist

Before deploying to production:

  • [ ] Python 3.10+ installed and verified
  • [ ] Clingo 5.6.0+ installed and importable
  • [ ] Ontology files present and validated
  • [ ] Memory limits configured (minimum 2GB recommended)
  • [ ] Health check endpoints implemented
  • [ ] Logging configured with appropriate levels
  • [ ] Backup strategy in place
  • [ ] Input validation implemented
  • [ ] Authentication configured (if public API)
  • [ ] CI/CD pipeline tested and passing
  • [ ] Rollback procedure documented
  • [ ] Monitoring and alerting configured