petter2025's picture
Upload folder using huggingface_hub
e9de9ae verified
raw
history blame
10.2 kB
"""
Database models for the ARF API Control Plane.
This module defines the SQLAlchemy ORM models for:
- Intents (InfrastructureIntent evaluations)
- Outcomes (recorded results of executed intents)
- Beta state (conjugate Bayesian posteriors per tenant and category)
- Audit logs (immutable decision records for compliance)
- Tenants (multi‑tenant isolation)
All tables include a `tenant_id` column to enforce data partitioning.
The BetaStateDB now stores parameters per (tenant, category) pair.
"""
from sqlalchemy import (
Column, Integer, String, DateTime, Boolean, Text, JSON,
Float, ForeignKey, UniqueConstraint, Index
)
from sqlalchemy.orm import relationship
import datetime
from .base import Base
# ============================================================================
# Tenant table – root of multi‑tenancy
# ============================================================================
class TenantDB(Base):
"""
Represents a customer tenant (organisation). All other tables
reference this table via a foreign key `tenant_id`.
Attributes:
id (str): UUID of the tenant (primary key).
name (str): Human‑readable organisation name.
created_at (datetime): UTC timestamp of creation.
created_by (str, optional): Email or user ID of the creator.
"""
__tablename__ = "tenants"
id = Column(String(64), primary_key=True, index=True)
name = Column(String(256), nullable=False)
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
created_by = Column(String(128), nullable=True)
# Relationships
api_keys = relationship("APIKeyDB", back_populates="tenant", cascade="all, delete-orphan")
intents = relationship("IntentDB", back_populates="tenant")
beta_states = relationship("BetaStateDB", back_populates="tenant")
audit_logs = relationship("DecisionAuditLogDB", back_populates="tenant")
# ============================================================================
# API keys (extended with tenant_id)
# ============================================================================
class APIKeyDB(Base):
"""
Stores API keys for authentication and tiered quota. Each key belongs
to exactly one tenant. The `tier` determines monthly evaluation limits.
Attributes:
key (str): The hashed API key (primary key).
tenant_id (str): Foreign key to `tenants.id`.
tier (str): Tier enumeration value (free, pro, premium, enterprise).
created_at (datetime): UTC creation time.
last_used_at (datetime, optional): Timestamp of last successful request.
is_active (bool): Soft‑delete flag.
"""
__tablename__ = "api_keys"
key = Column(String(256), primary_key=True, index=True)
tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
tier = Column(String(32), nullable=False)
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
last_used_at = Column(DateTime, nullable=True)
is_active = Column(Boolean, default=True, nullable=False)
# Relationships
tenant = relationship("TenantDB", back_populates="api_keys")
usage_logs = relationship("UsageLogDB", back_populates="api_key")
# ============================================================================
# Intents (evaluations) – now tenant‑scoped
# ============================================================================
class IntentDB(Base):
"""
Stores each InfrastructureIntent evaluation request and its resulting
risk score. One‑to‑many with OutcomeDB.
Attributes:
id (int): Auto‑increment primary key.
deterministic_id (str): Client‑provided idempotency identifier (unique).
tenant_id (str): Tenant that owns this intent.
intent_type (str): Type of intent (e.g., "provision_resource").
payload (JSON): Original API request payload.
oss_payload (JSON): Canonical OSS intent representation.
environment (str, optional): Environment label (prod, staging, etc.).
created_at (datetime): UTC timestamp of evaluation.
evaluated_at (datetime, optional): When the risk engine processed it.
risk_score (str, optional): String representation of the risk score.
"""
__tablename__ = "intents"
id = Column(Integer, primary_key=True, index=True)
deterministic_id = Column(String(64), unique=True, index=True, nullable=False)
tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
intent_type = Column(String(64), nullable=False)
payload = Column(JSON, nullable=False)
oss_payload = Column(JSON, nullable=True)
environment = Column(String(32), nullable=True)
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
evaluated_at = Column(DateTime, nullable=True)
risk_score = Column(String(32), nullable=True)
# Relationships
tenant = relationship("TenantDB", back_populates="intents")
outcomes = relationship("OutcomeDB", back_populates="intent", cascade="all, delete-orphan")
class OutcomeDB(Base):
"""
Records the outcome (success/failure) of a previously evaluated intent.
Only one outcome per intent is allowed (unique constraint on intent_id).
Attributes:
id (int): Primary key.
intent_id (int): Foreign key to `intents.id`.
success (bool): Whether the executed action succeeded.
recorded_by (str, optional): Identity of the caller (e.g., API key owner).
notes (str, optional): Free‑text notes.
recorded_at (datetime): UTC timestamp.
idempotency_key (str, optional): Unique idempotency key for this outcome.
"""
__tablename__ = "intent_outcomes"
id = Column(Integer, primary_key=True, index=True)
intent_id = Column(Integer, ForeignKey("intents.id", ondelete="CASCADE"), nullable=False)
success = Column(Boolean, nullable=False)
recorded_by = Column(String(128), nullable=True)
notes = Column(Text, nullable=True)
recorded_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
idempotency_key = Column(String(128), unique=True, nullable=True)
intent = relationship("IntentDB", back_populates="outcomes")
__table_args__ = (
UniqueConstraint("intent_id", name="uq_outcome_intentid"),
)
# ============================================================================
# Bayesian conjugate state – now per tenant and per category
# ============================================================================
class BetaStateDB(Base):
"""
Stores the posterior parameters (α, β) of the conjugate Beta model
for each (tenant, category) pair. This allows online learning to be
isolated per customer.
Attributes:
id (int): Primary key.
tenant_id (str): Tenant that owns this state.
category (str): ActionCategory value (e.g., "database", "compute").
alpha (float): α parameter of the Beta distribution.
beta (float): β parameter of the Beta distribution.
updated_at (datetime): Last update timestamp (auto‑set).
"""
__tablename__ = "beta_state"
id = Column(Integer, primary_key=True, index=True)
tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
category = Column(String(32), nullable=False, index=True)
alpha = Column(Float, nullable=False)
beta = Column(Float, nullable=False)
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
# Composite unique constraint: (tenant_id, category)
__table_args__ = (
UniqueConstraint("tenant_id", "category", name="uq_beta_state_tenant_category"),
)
# Relationships
tenant = relationship("TenantDB", back_populates="beta_states")
# ============================================================================
# NEW: Audit log for compliance (immutable decision records)
# ============================================================================
class DecisionAuditLogDB(Base):
"""
Immutable, tamper‑evident record of every governance decision.
Designed for compliance (SOC2, ISO) and forensic analysis.
Attributes:
id (str): UUID primary key.
tenant_id (str): Tenant that owns the decision.
deterministic_id (str): Intent identifier (idempotency key).
timestamp (datetime): UTC decision time.
risk_score (float): Fused Bayesian risk score (0‑1).
action (str): Selected action (approve, deny, escalate).
justification (str): Human‑readable explanation.
memory_success_rate (float, optional): Memory‑based correction value.
memory_weight (float, optional): Weight assigned to memory.
counterfactual (JSON, optional): Structured counterfactual explanation.
trace_id (str, optional): OpenTelemetry trace ID for debugging.
signature (str, optional): Ed25519 signature for tamper‑proofing.
"""
__tablename__ = "decision_audit_log"
id = Column(String(64), primary_key=True, default=lambda: str(uuid.uuid4()))
tenant_id = Column(String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
deterministic_id = Column(String(64), nullable=False, index=True)
timestamp = Column(DateTime, default=datetime.datetime.utcnow, nullable=False, index=True)
risk_score = Column(Float, nullable=False)
action = Column(String(32), nullable=False)
justification = Column(Text, nullable=False)
memory_success_rate = Column(Float, nullable=True)
memory_weight = Column(Float, nullable=True)
counterfactual = Column(JSON, nullable=True)
trace_id = Column(String(128), nullable=True)
signature = Column(String(256), nullable=True)
# Composite index for fast filtered queries
__table_args__ = (
Index("idx_audit_tenant_time", "tenant_id", "timestamp"),
)
tenant = relationship("TenantDB", back_populates="audit_logs")