Skip to content

Deploy the guardrails service

Composes the E1 baseline + the E2 call-an-llm module + one guardrail resource per cloud.

Verified 2026-08-08

Layout

examples/guardrails/
  service/                        # E2 chat + native-guardrail sandwich
    guardrail.py                  # Protocol + 3 per-cloud impls + Verdict + fail-closed helper
    main.py                       # FastAPI POST /chat -> {blocked, side, reason} | {reply}
    Dockerfile
    requirements.txt
  chart/                          # Helm chart
    Chart.yaml
    values.yaml
    templates/{deployment,service,serviceaccount}.yaml
  terraform/
    azure/  (module "baseline" + "call_an_llm" + azurerm_cognitive_account kind=ContentSafety)
    gcp/    (module "baseline" + "call_an_llm" + enable modelarmor.googleapis.com + writer IAM)
    aws/    (module "baseline" + "call_an_llm" + aws_bedrock_guardrail resource)

Terraform

module "baseline" {
  source  = "../../../foundations/azure"
  name    = var.name
  region  = var.region
  compute = var.compute
}

module "call_an_llm" {
  source                    = "../../../call-an-llm/terraform/azure"
  name                      = var.name
  region                    = var.region
  compute                   = var.compute
  azure_openai_base_url     = var.azure_openai_base_url
  azure_openai_deployment   = var.azure_openai_deployment
  foundry_claude_deployment = var.foundry_claude_deployment
}

resource "azurerm_cognitive_account" "content_safety" {
  name                = "${var.name}-cs"
  location            = var.region
  resource_group_name = module.baseline.resource_group_name
  kind                = "ContentSafety"
  sku_name            = "S0"
}

Feed azurerm_cognitive_account.content_safety.endpoint + primary key into the chart via env.CONTENT_SAFETY_ENDPOINT + a secret env for the key.

module "baseline" {
  source     = "../../../foundations/gcp"
  project_id = var.project_id
  name       = var.name
  region     = var.region
  compute    = var.compute
}

module "call_an_llm" {
  source     = "../../../call-an-llm/terraform/gcp"
  project_id = var.project_id
  name       = var.name
  region     = var.region
  compute    = var.compute
}

# Enable Model Armor on the project.
resource "google_project_service" "modelarmor" {
  project            = var.project_id
  service            = "modelarmor.googleapis.com"
  disable_on_destroy = false
}

# Workload SA gets modelarmor.user so the pod can call
# sanitize_user_prompt / sanitize_model_response.
resource "google_service_account" "guardrail" {
  account_id   = "${var.name}-guard"
  display_name = "Chiron guardrail caller"
}
resource "google_project_iam_member" "modelarmor_user" {
  project = var.project_id
  role    = "roles/modelarmor.user"
  member  = "serviceAccount:${google_service_account.guardrail.email}"
}

Templates (projects/{project}/locations/{location}/templates/{id}) are created out-of-band; the wrapper reads MODEL_ARMOR_TEMPLATE_INPUT and MODEL_ARMOR_TEMPLATE_OUTPUT env vars.

module "baseline" {
  source  = "../../../foundations/aws"
  name    = var.name
  region  = var.region
  compute = var.compute
}

module "call_an_llm" {
  source                = "../../../call-an-llm/terraform/aws"
  name                  = var.name
  region                = var.region
  compute               = var.compute
  eks_oidc_provider_arn = var.eks_oidc_provider_arn
}

resource "aws_bedrock_guardrail" "chiron" {
  name                       = var.name
  description                = "Chiron default guardrail"
  blocked_input_messaging    = "Sorry, that request violates policy."
  blocked_outputs_messaging  = "Sorry, that response violates policy."

  content_policy_config {
    filters_config {
      type            = "HATE"
      input_strength  = "HIGH"
      output_strength = "HIGH"
    }
    # ... same shape for INSULTS / SEXUAL / VIOLENCE / MISCONDUCT / PROMPT_ATTACK
  }

  sensitive_information_policy_config {
    pii_entities_config {
      type   = "EMAIL"
      action = "ANONYMIZE"
    }
    pii_entities_config {
      type   = "PHONE"
      action = "ANONYMIZE"
    }
    pii_entities_config {
      type   = "US_SOCIAL_SECURITY_NUMBER"
      action = "BLOCK"
    }
  }

  contextual_grounding_policy_config {
    filters_config {
      type      = "GROUNDING"
      threshold = 0.75
    }
    filters_config {
      type      = "RELEVANCE"
      threshold = 0.5
    }
  }
}

# Extend the workload role to allow apply_guardrail.
data "aws_iam_policy_document" "apply_guardrail" {
  statement {
    effect  = "Allow"
    actions = ["bedrock:ApplyGuardrail"]
    resources = [aws_bedrock_guardrail.chiron.guardrail_arn]
  }
}
resource "aws_iam_policy" "apply_guardrail" {
  name   = "${var.name}-apply-guardrail"
  policy = data.aws_iam_policy_document.apply_guardrail.json
}

Verify (validate-only)

for p in azure gcp aws; do
  ( cd examples/guardrails/terraform/$p && terraform init -backend=false && terraform validate )
done

helm lint examples/guardrails/chart
helm template guardrails examples/guardrails/chart > /dev/null

python -m compileall examples/guardrails/service

Live guardrail evaluations need real credentials + the resource actually deployed — Phase 3 handles billed apply.