<< All versions
Skill v1.0.0
currentAutomated scan100/100bagelhole/devops-security-agent-skills/model-registry-governance
──Details
PublishedSeptember 29, 2026 at 03:01 PM
Content Hashsha256:86891d0e67beec3b...
Git SHA0365f57a079b
──Files
Files (1 file, 14.2 KB)
SKILL.md14.2 KBactive
SKILL.md · 429 lines · 14.2 KB
version: "1.0.0" name: model-registry-governance description: Establish model registry standards, governance controls, metadata schemas, approvals, and lifecycle policies for enterprise AI deployments. license: MIT metadata: author: devops-skills version: "1.0"
Model Registry Governance
Create a trustworthy system of record for model artifacts, prompts, adapters, and evaluation evidence.
When to Use This Skill
- Setting up a centralized model registry for your organization
- Defining metadata standards for model artifacts
- Building approval workflows for model promotion to production
- Implementing lifecycle policies for model retirement
- Preparing for compliance audits of AI systems
Prerequisites
- MLflow Tracking Server or Weights & Biases instance deployed
- Object storage for model artifacts (S3, GCS, or MinIO)
- CI/CD pipeline with access to the registry API
- OPA or similar policy engine for governance checks
- Git repository for policy definitions and promotion scripts
Core Principles
- Traceability: every production model maps to source code, data snapshot, and evaluation results.
- Reproducibility: builds are deterministic with pinned dependencies.
- Policy-driven promotion: no manual bypass for critical safety checks.
- Lifecycle hygiene: stale, vulnerable, or unowned models are retired automatically.
MLflow Registry Setup
bash
# Install MLflow with required backendspip install mlflow[extras] psycopg2-binary boto3# Start MLflow tracking server with PostgreSQL backend and S3 artifact storemlflow server \--backend-store-uri postgresql://mlflow:password@db:5432/mlflow \--default-artifact-root s3://mlflow-artifacts/models \--host 0.0.0.0 \--port 5000 \--serve-artifacts
yaml
# docker-compose.yaml for MLflowservices:mlflow:image: ghcr.io/mlflow/mlflow:2.12.0command: >mlflow server--backend-store-uri postgresql://mlflow:${DB_PASSWORD}@db:5432/mlflow--default-artifact-root s3://mlflow-artifacts/models--host 0.0.0.0--port 5000--serve-artifactsports:- "5000:5000"environment:AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}depends_on:- dbdb:image: postgres:16-alpineenvironment:POSTGRES_DB: mlflowPOSTGRES_USER: mlflowPOSTGRES_PASSWORD: ${DB_PASSWORD}volumes:- pgdata:/var/lib/postgresql/datavolumes:pgdata:
Required Metadata Schema
python
# model_metadata_schema.pyfrom pydantic import BaseModel, Fieldfrom typing import List, Optionalfrom datetime import datetimefrom enum import Enumclass LifecycleState(str, Enum):DRAFT = "draft"CANDIDATE = "candidate"APPROVED = "approved"DEPRECATED = "deprecated"RETIRED = "retired"class RiskRating(str, Enum):LOW = "low"MEDIUM = "medium"HIGH = "high"CRITICAL = "critical"class ModelMetadata(BaseModel):"""Required metadata for every registered model."""# Identityname: str = Field(description="Model name matching registry key")version: str = Field(description="Semantic version")checksum: str = Field(description="SHA-256 of model artifact")storage_uri: str = Field(description="Artifact store path")# Lineagebase_model: str = Field(description="Parent model identifier")fine_tune_method: Optional[str] = Field(default=None)training_dataset: Optional[str] = Field(default=None)training_date: Optional[datetime] = Field(default=None)source_commit: str = Field(description="Git SHA of training code")# Evaluationeval_datasets: List[str] = Field(description="Evaluation dataset IDs")eval_report_uri: str = Field(description="Path to evaluation results")quality_score: float = Field(ge=0, le=1)safety_score: float = Field(ge=0, le=1)# Governancelicense: str = Field(description="SPDX license identifier")allowed_use_cases: List[str]prohibited_use_cases: List[str]risk_rating: RiskRatingsecurity_controls: List[str]# Ownershipowner: str = Field(description="Primary owner email")backup_owner: str = Field(description="Backup owner email")escalation_contact: strteam: str# Lifecyclestate: LifecycleState = LifecycleState.DRAFTcreated_at: datetime = Field(default_factory=datetime.utcnow)approved_at: Optional[datetime] = Noneapproved_by: Optional[str] = Noneexpires_at: Optional[datetime] = None
Model Registration Script
python
# register_model.pyimport mlflowfrom mlflow.tracking import MlflowClientimport jsonimport hashlibdef register_model(model_path: str,model_name: str,metadata: dict,mlflow_uri: str = "http://mlflow:5000"):"""Register a model with full metadata and governance tags."""mlflow.set_tracking_uri(mlflow_uri)client = MlflowClient()# Compute artifact checksumwith open(model_path, "rb") as f:checksum = hashlib.sha256(f.read()).hexdigest()metadata["checksum"] = checksum# Log model with metadatawith mlflow.start_run(run_name=f"register-{model_name}-{metadata['version']}") as run:# Log all metadata as paramsmlflow.log_params({"model_name": model_name,"version": metadata["version"],"base_model": metadata["base_model"],"risk_rating": metadata["risk_rating"],"owner": metadata["owner"],"license": metadata["license"],})# Log quality metricsmlflow.log_metrics({"quality_score": metadata["quality_score"],"safety_score": metadata["safety_score"],})# Log full metadata as artifactwith open("metadata.json", "w") as f:json.dump(metadata, f, indent=2, default=str)mlflow.log_artifact("metadata.json")# Log model artifactmlflow.log_artifact(model_path)# Register in model registrymodel_uri = f"runs:/{run.info.run_id}/model"result = mlflow.register_model(model_uri, model_name)# Set lifecycle tagsclient.set_model_version_tag(model_name, result.version, "state", "draft")client.set_model_version_tag(model_name, result.version, "risk_rating", metadata["risk_rating"])client.set_model_version_tag(model_name, result.version, "checksum", checksum)return result
Approval Workflow
- Registration request created from CI.
- Security checks (artifact scan, dependency scan, provenance).
- Evaluation package uploaded (quality, toxicity, jailbreak, bias, latency, cost).
- Required approvals: platform + product + security (as policy dictates).
- Promotion to stage/prod based on signed decision record.
Promotion Script
python
# promote_model.pyimport mlflowfrom mlflow.tracking import MlflowClientfrom datetime import datetimeimport sysdef promote_model(model_name: str,version: str,target_stage: str,approver: str,mlflow_uri: str = "http://mlflow:5000"):"""Promote a model version after governance checks pass."""mlflow.set_tracking_uri(mlflow_uri)client = MlflowClient()# Verify current state allows promotionmv = client.get_model_version(model_name, version)current_state = mv.tags.get("state", "draft")valid_transitions = {"draft": ["candidate"],"candidate": ["approved", "draft"],"approved": ["deprecated"],"deprecated": ["retired"],}if target_stage not in valid_transitions.get(current_state, []):raise ValueError(f"Invalid transition: {current_state} -> {target_stage}. "f"Allowed: {valid_transitions.get(current_state, [])}")# Verify required eval scores for production promotionif target_stage == "approved":run = client.get_run(mv.run_id)quality = float(run.data.metrics.get("quality_score", 0))safety = float(run.data.metrics.get("safety_score", 0))if quality < 0.85:raise ValueError(f"Quality score {quality} below threshold 0.85")if safety < 0.95:raise ValueError(f"Safety score {safety} below threshold 0.95")# Record promotionnow = datetime.utcnow().isoformat()client.set_model_version_tag(model_name, version, "state", target_stage)client.set_model_version_tag(model_name, version, f"promoted_to_{target_stage}_at", now)client.set_model_version_tag(model_name, version, f"promoted_to_{target_stage}_by", approver)# Transition MLflow stage aliasstage_map = {"candidate": "Staging","approved": "Production","deprecated": "Archived",}if target_stage in stage_map:client.transition_model_version_stage(model_name, version, stage_map[target_stage])print(f"Model {model_name} v{version}: {current_state} -> {target_stage}")print(f"Approved by: {approver} at {now}")if __name__ == "__main__":promote_model(model_name=sys.argv[1],version=sys.argv[2],target_stage=sys.argv[3],approver=sys.argv[4],)
Lifecycle States
| State | Description | Serving Allowed | New Usage | |
|---|---|---|---|---|
draft | Internal experimentation | Dev only | Dev only | |
candidate | Passed baseline tests | Staging | Staging | |
approved | Authorized for production | All environments | Yes | |
deprecated | Replacement announced | Existing only | Blocked | |
retired | Archived for audit | None | None |
Lifecycle Automation
python
# lifecycle_policy.pyfrom mlflow.tracking import MlflowClientfrom datetime import datetime, timedeltadef enforce_lifecycle_policies(mlflow_uri: str = "http://mlflow:5000"):"""Run periodic lifecycle enforcement."""client = MlflowClient()for rm in client.search_registered_models():for mv in client.search_model_versions(f"name='{rm.name}'"):tags = mv.tagsstate = tags.get("state", "draft")# Auto-deprecate models with expired approvals (90 days)if state == "approved":approved_at = tags.get("promoted_to_approved_at")if approved_at:approved_date = datetime.fromisoformat(approved_at)if datetime.utcnow() - approved_date > timedelta(days=90):print(f"Auto-deprecating {rm.name} v{mv.version}: approval expired")client.set_model_version_tag(rm.name, mv.version, "state", "deprecated")client.set_model_version_tag(rm.name, mv.version, "auto_deprecated_reason", "approval_expired")# Auto-retire deprecated models after 30 daysif state == "deprecated":deprecated_at = tags.get("promoted_to_deprecated_at")if deprecated_at:deprecated_date = datetime.fromisoformat(deprecated_at)if datetime.utcnow() - deprecated_date > timedelta(days=30):print(f"Auto-retiring {rm.name} v{mv.version}")client.set_model_version_tag(rm.name, mv.version, "state", "retired")client.transition_model_version_stage(rm.name, mv.version, "Archived")# Flag drafts with no activity for 14 daysif state == "draft":created = datetime.fromisoformat(mv.creation_timestamp / 1000)if datetime.utcnow() - created > timedelta(days=14):print(f"Stale draft: {rm.name} v{mv.version}")
Governance Policies (OPA/Rego)
rego
# policy/model_governance.regopackage model.governance# Reject artifacts without SBOMdeny[msg] {not input.metadata.sbom_urimsg := "Model must include SBOM artifact URI"}# Block promotion if critical CVEs remaindeny[msg] {input.target_state == "approved"input.security_scan.critical_cves > 0msg := sprintf("Cannot promote: %d critical CVEs unresolved", [input.security_scan.critical_cves])}# Require refreshed evals after prompt changesdeny[msg] {input.target_state == "approved"input.prompt_changednot input.eval_refreshed_after_prompt_changemsg := "Evaluation must be re-run after prompt template changes"}# Require minimum eval scores for productiondeny[msg] {input.target_state == "approved"input.metadata.quality_score < 0.85msg := sprintf("Quality score %.2f below threshold 0.85", [input.metadata.quality_score])}# Require dual approval for high-risk modelsdeny[msg] {input.target_state == "approved"input.metadata.risk_rating == "high"count(input.approvals) < 2msg := "High-risk models require at least 2 approvals"}
Audit Readiness
Maintain immutable records of:
- Who approved and when
- Which policy checks executed
- Which exceptions were granted
- What model/version served each customer request window
Troubleshooting
| Issue | Diagnosis | Resolution | |
|---|---|---|---|
| Model registration fails | Check MLflow server connectivity and artifact store permissions | Verify S3/GCS credentials and bucket policy | |
| Promotion blocked by policy | Review OPA deny messages in CI output | Fix metadata gaps or request policy exception | |
| Stale models not auto-retiring | Lifecycle cron job not running | Check CronJob status in Kubernetes | |
| Duplicate model versions | Race condition in CI pipeline | Add locking via registry API or database | |
| Missing eval evidence | Eval pipeline skipped or failed | Re-run eval suite and re-register |
Related Skills
- sbom-supply-chain - Provenance and signing
- policy-as-code - Enforce governance with policy engines
- llm-fine-tuning - Version adapters and training outputs
- llmops-platform-engineering - Platform CI/CD and promotion workflows
- ai-sre-incident-response - Incident response for model issues