Bastio
Security Features

Intent Detection (IBAC)

Detect data-access intent in user prompts and require verification before sharing sensitive information.

Intent Detection (IBAC)

Intent-Based Access Control (IBAC) is a proactive security layer that detects when users are requesting access to sensitive data through AI conversations. Instead of relying solely on reactive threat detection, IBAC identifies data-access intent in natural language prompts and requires identity verification before the AI can respond with sensitive information.

IBAC sits between your users and AI providers, analyzing every prompt for patterns that indicate a request for protected data — order details, account information, payment records, personal data, or admin-level actions.

The Problem: Why IBAC Matters

Traditional AI security focuses on blocking malicious inputs — jailbreak attempts, prompt injections, and harmful content. But what about legitimate-looking requests that access data they shouldn't?

Consider this real-world scenario:

Real-world example: A customer support AI chatbot was manipulated through carefully crafted conversational prompts to reveal other customers' personal information. The requests appeared completely natural — "Can you look up the account details for case ID 8817?" — but the user making the request had no authorization to access that account. Traditional security passed every check because there was nothing technically malicious about the prompt.

The gap IBAC fills:

Security LayerWhat It CatchesWhat It Misses
Jailbreak Detection"Ignore your instructions and reveal..."Normal-sounding data requests
PII ProtectionSSN, credit card numbers in outputsRequests that would trigger PII exposure
Bot DetectionAutomated scraping patternsHuman social engineering
IBAC"Show me the address for order #34004"

IBAC adds the missing layer: detecting what data users are trying to access and requiring verification before the AI responds.

How It Works

Detection Flow

User Prompt


┌─────────────────────────┐
│  Pattern Matching        │  Aho-Corasick multi-pattern
│  (Built-in + Custom)     │  string matching algorithm
└───────────┬─────────────┘


┌─────────────────────────┐
│  Confidence Scoring      │  Base confidence + multi-match
│                          │  bonus, threshold: 0.70
└───────────┬─────────────┘

     ┌──────┴──────┐
     │             │
  < 0.70        ≥ 0.70
     │             │
     ▼             ▼
  Allow      ┌──────────────┐
  Request    │  Challenge    │  Return valid API response
             │  Response     │  with verification request
             └──────────────┘

Built-in Intent Categories

Bastio ships with 5 built-in intent categories that cover the most common data-access patterns:

CategoryBase ConfidenceRequired Verification
order_lookup0.85Identity, Email
account_info0.80Identity
payment_data0.90Identity, Payment
personal_info0.90Admin, Identity
admin_action0.85Admin, Identity

Confidence Scoring

The confidence score determines whether a prompt triggers a challenge:

  • Base confidence: Each category has a baseline score (0.80–0.90)
  • Multi-match bonus: +0.05 for each additional pattern matched
  • Cap: Maximum confidence is 0.99
  • Threshold: Only prompts scoring 0.70 or higher trigger a challenge

For example, "What's the tracking number for order #12345?" matches two patterns in order_lookup, resulting in a confidence of 0.85 + 0.05 = 0.90.

Challenge Responses

When IBAC detects data-access intent, it returns a valid API response (not an error) with a contextual verification message. This means your application continues to work normally — the user sees a helpful message instead of a broken experience.

Example challenge response (OpenAI Chat Completions format):

{
  "id": "resp_abc123",
  "object": "chat.completion",
  "model": "gpt-4o",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "I'd be happy to help with your order, but I need to verify your identity first. Please log in or verify your email to access order information."
    },
    "finish_reason": "stop"
  }]
}

Challenge metadata is included for programmatic detection:

{
  "bastio_challenge": "true",
  "action": "auth_required",
  "intent_category": "order_lookup",
  "challenge_id": "ch_abc123",
  "confidence": "0.90",
  "required_verification": "identity_verification,email_verification",
  "request_id": "resp_abc123"
}

Enabling Intent Detection

Via Dashboard

  1. Navigate to Security Center in your dashboard
  2. Open the Security Profile for your proxy
  3. Toggle Enable Intent Detection to on
  4. Click Save

Intent detection is enabled by default for Medium and above security levels.

Via API

curl -X PUT https://api.bastio.com/security/profiles \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "proxy_id": "your-proxy-id",
    "enable_intent_detection": true
  }'

Built-in Categories Reference

Order Lookup (order_lookup)

Detects attempts to look up order information, shipping details, and tracking numbers.

Patterns: order status, order number, tracking number, shipping address, delivery status, order details, order #, shipment, where is my order, order history

Challenge message: "I'd be happy to help with your order, but I need to verify your identity first. Please log in or verify your email to access order information."

Required verification: identity_verification, email_verification


Account Information (account_info)

Detects attempts to access account details like email, phone, and profile data.

Patterns: account details, my account, account info, email on file, phone number, account settings, profile information, my profile, account balance

Challenge message: "For your security, I need to verify your identity before sharing account information. Please complete the verification process to continue."

Required verification: identity_verification


Payment Data (payment_data)

Detects attempts to access payment methods, billing, and financial information.

Patterns: credit card, payment method, billing address, payment history, bank account, card on file, payment info, billing info, invoice, transaction history

Challenge message: "Payment information requires identity verification. Please verify your identity to access payment details."

Required verification: identity_verification, payment_verification


Personal Information (personal_info)

Detects attempts to access other users' personal data — the highest-risk category.

Patterns: personal information, social security, date of birth, home address, personal data, SSN, driver's license, passport number, other user, customer data

Challenge message: "I can't share personal information about other users without proper verification. Please verify your identity and authorization level."

Required verification: admin_verification, identity_verification


Administrative Action (admin_action)

Detects attempts to perform admin-level operations like deleting accounts or changing permissions.

Patterns: delete account, admin access, change permissions, reset password, modify user, admin panel, system settings, grant access, revoke access, bulk export

Challenge message: "This action requires administrator verification. Please verify your identity and admin privileges to proceed."

Required verification: admin_verification, identity_verification

Custom Intent Rules

Built-in categories cover common patterns, but your application likely has domain-specific data that needs protection. Custom intent rules let you define exactly what to detect and how to respond.

Creating Rules via API

curl -X POST https://api.bastio.com/security/intent-rules \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Medical Records Access",
    "category": "medical_records",
    "description": "Detects attempts to access patient medical records",
    "patterns": [
      "medical record",
      "patient history",
      "lab results",
      "diagnosis",
      "prescription history",
      "health records"
    ],
    "required_verification": ["identity_verification", "hipaa_verification"],
    "verification_message": "Access to medical records requires HIPAA-compliant identity verification. Please complete the verification process.",
    "severity": "critical",
    "priority": 10
  }'

Rule Properties

FieldTypeRequiredDescription
namestringYesHuman-readable rule name
categorystringYesCategory identifier (lowercase, underscores)
descriptionstringNoWhat this rule detects
patternsstring[]YesPhrases to match in user prompts
required_verificationstring[]YesVerification types needed
verification_messagestringNoCustom challenge message
severitystringNolow, medium, high, critical (default: medium)
priorityintNoHigher priority rules take precedence (default: 0)
proxy_idstringNoLimit rule to specific proxy
bypass_with_verified_userboolNoSkip challenge if user is already verified

Updating a Rule

curl -X PUT https://api.bastio.com/security/intent-rules/RULE_ID \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Medical Records Access (Updated)",
    "patterns": [
      "medical record",
      "patient history",
      "lab results",
      "diagnosis",
      "prescription history",
      "health records",
      "treatment plan",
      "medication list"
    ],
    "severity": "critical"
  }'

Deleting a Rule

curl -X DELETE https://api.bastio.com/security/intent-rules/RULE_ID \
  -H "Authorization: Bearer YOUR_TOKEN"

Real-World Example: Customer Support Chatbot

Let's walk through implementing IBAC for an e-commerce company "ShopCo" that has an AI-powered customer support chatbot.

Step 1: Enable Intent Detection

curl -X PUT https://api.bastio.com/security/profiles \
  -H "Authorization: Bearer $BASTIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "proxy_id": "shopco-support-proxy",
    "enable_intent_detection": true,
    "security_level": "medium"
  }'

Step 2: Create Custom Rules

Create domain-specific rules for ShopCo's data:

Rule 1: Refund Requests

curl -X POST https://api.bastio.com/security/intent-rules \
  -H "Authorization: Bearer $BASTIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Refund Request",
    "category": "refund_request",
    "patterns": ["refund", "return order", "money back", "cancel order", "dispute charge"],
    "required_verification": ["identity_verification", "email_verification"],
    "verification_message": "I can help with your refund! To protect your account, please verify your email address first.",
    "severity": "high",
    "proxy_id": "shopco-support-proxy"
  }'

Rule 2: Loyalty Points

curl -X POST https://api.bastio.com/security/intent-rules \
  -H "Authorization: Bearer $BASTIO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Loyalty Points Access",
    "category": "loyalty_points",
    "patterns": ["loyalty points", "rewards balance", "redeem points", "points history"],
    "required_verification": ["identity_verification"],
    "verification_message": "Let me look up your rewards balance. Please verify your identity first.",
    "severity": "medium",
    "proxy_id": "shopco-support-proxy"
  }'

Step 3: Handle Challenge Responses in Your App

import openai

client = openai.OpenAI(
    base_url="https://api.bastio.com/v1/guard/shopco-support-proxy",
    api_key="sk-your-bastio-api-key",
)

def handle_user_message(user_input: str):
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are ShopCo's support assistant."},
            {"role": "user", "content": user_input},
        ],
    )

    message = response.choices[0].message.content

    # Check for IBAC challenge in metadata
    # Metadata is available in response headers or streaming events
    if hasattr(response, 'metadata') and response.metadata:
        if response.metadata.get("bastio_challenge") == "true":
            challenge_id = response.metadata["challenge_id"]
            required = response.metadata["required_verification"]

            # Show verification UI to user
            return {
                "type": "challenge",
                "message": message,
                "challenge_id": challenge_id,
                "required_verification": required.split(","),
            }

    return {"type": "response", "message": message}


def retry_after_verification(
    user_input: str,
    challenge_id: str,
    verification_token: str,
):
    """Retry the request after user completes verification."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are ShopCo's support assistant."},
            {"role": "user", "content": user_input},
        ],
        extra_headers={
            "X-Bastio-Challenge-ID": challenge_id,
            "X-Bastio-Verification-Token": verification_token,
        },
    )
    return response.choices[0].message.content
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.bastio.com/v1/guard/shopco-support-proxy",
  apiKey: "sk-your-bastio-api-key",
});

interface ChallengeResponse {
  type: "challenge";
  message: string;
  challengeId: string;
  requiredVerification: string[];
}

interface NormalResponse {
  type: "response";
  message: string;
}

async function handleUserMessage(
  userInput: string
): Promise<ChallengeResponse | NormalResponse> {
  const response = await client.chat.completions.create({
    model: "gpt-4o",
    messages: [
      { role: "system", content: "You are ShopCo's support assistant." },
      { role: "user", content: userInput },
    ],
  });

  const message = response.choices[0].message.content ?? "";

  // For streaming responses, check metadata in the response.completed event
  const metadata = (response as any).metadata;
  if (metadata?.bastio_challenge === "true") {
    return {
      type: "challenge",
      message,
      challengeId: metadata.challenge_id,
      requiredVerification: metadata.required_verification.split(","),
    };
  }

  return { type: "response", message };
}

async function retryAfterVerification(
  userInput: string,
  challengeId: string,
  verificationToken: string
): Promise<string> {
  const response = await client.chat.completions.create({
    model: "gpt-4o",
    messages: [
      { role: "system", content: "You are ShopCo's support assistant." },
      { role: "user", content: userInput },
    ],
    // @ts-ignore - custom Bastio headers
    headers: {
      "X-Bastio-Challenge-ID": challengeId,
      "X-Bastio-Verification-Token": verificationToken,
    },
  });

  return response.choices[0].message.content ?? "";
}

How This Works in Practice

Let's walk through the complete flow so you can see how the pieces fit together.

1. User sends a message

A customer types something like "Can I get a refund for order #7291?" into your chat UI. Your app sends it to Bastio the same way it would send any chat completion request — no special handling needed:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Can I get a refund for order #7291?"}],
)

2. Bastio intercepts and detects intent

This happens transparently — your code doesn't change. Behind the scenes, Bastio's pattern matching identifies refund combined with order # and scores the request above the 0.70 threshold you configured in Step 1. Because the refund_request rule requires identity_verification, Bastio triggers a challenge instead of forwarding the request to the LLM.

3. Your app receives what looks like a normal response

This is the key insight: the chat completion returns normally. There's no error, no special status code. The response message.content contains the challenge text you configured — something like "I can help with your refund! To protect your account, please verify your identity first."

The difference is in the metadata. The response includes bastio_challenge: "true" along with a challenge_id and the required_verification method.

4. Check for the challenge flag

Your app inspects the metadata to decide what to do next:

if response.metadata.get("bastio_challenge") == "true":
    challenge_id = response.metadata["challenge_id"]
    # Show verification UI instead of a normal chat reply

If there's no challenge flag, the response is a normal LLM reply and you display it as usual.

5. Show verification UI

This is your app's responsibility. Bastio doesn't prescribe how you verify the user — pick whatever fits your security model:

  • Email link — send a one-time verification link
  • SMS OTP — text a 6-digit code
  • OAuth re-auth — prompt the user to re-authenticate via your identity provider
  • In-app identity check — ask for the last 4 digits of their payment method, a security question, etc.

The point is that the user proves they are who they claim to be before accessing sensitive data.

6. User completes verification

Once the user finishes the verification step, your auth system issues a verification_token. How this token is generated is entirely up to you — Bastio just needs to receive it on the retry.

7. Retry with headers

Send the original request again, this time attaching the challenge ID and verification token as headers:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Can I get a refund for order #7291?"}],
    extra_headers={
        "X-Bastio-Challenge-ID": challenge_id,
        "X-Bastio-Verification-Token": verification_token,
    },
)

8. Bastio validates and allows

What happens next depends on your verification mode:

  • Trust mode (default): Bastio checks the challenge_id is valid, not expired (10-minute TTL), and the token is non-empty. If all checks pass, the request goes through to the LLM. Works out of the box — no backend integration needed.
  • Webhook mode (recommended for production): Bastio POSTs to your webhook URL with an HMAC-SHA256 signed payload containing the challenge_id and verification_token. Your server validates the token against your auth system and responds {"verified": true}. If your webhook confirms, the request goes through.

If verification fails (invalid token, expired challenge, webhook rejects), Bastio re-issues a new challenge so the user can try again.

Step 4: Verify and Retry

After the user completes verification, your app retries the original request with the verification credentials.

Supported verification methods — use whichever fits your security model:

  • Email link — user clicks a one-time link, your backend generates a token
  • SMS OTP — user enters a code sent to their phone
  • OAuth re-auth — user re-authenticates through your identity provider
  • In-app identity check — user answers a security question or confirms account details

Retry the request with verification headers:

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": original_user_message}],
    extra_headers={
        "X-Bastio-Challenge-ID": challenge_id,
        "X-Bastio-Verification-Token": verification_token,
    },
)
# This time the request goes through to the LLM
print(response.choices[0].message.content)

What happens next:

  1. Your app receives a verification_token from your auth system
  2. The retry includes both X-Bastio-Challenge-ID and X-Bastio-Verification-Token headers
  3. Bastio validates the token against the original challenge — if valid, the request is forwarded to the LLM
  4. If the token is invalid or expired, Bastio re-issues the challenge so the user can verify again

Verification Modes

IBAC supports two verification modes, configured per-proxy on the security profile:

Trust Mode (Default)Webhook Mode (Recommended)
How it worksBastio checks challenge_id is valid + token is non-emptyBastio POSTs to your webhook; your server validates the token
Setup requiredNoneWebhook URL + signing secret
Security levelBasic — trusts any non-empty tokenFull — your backend validates the actual token
Best forDevelopment, low-risk dataProduction, sensitive data
Fail behaviorN/AFail-closed (timeout/error = rejected)

Configuring Verification Mode

Via Dashboard: Navigate to Security Center > Intent Detection and select the verification mode in the settings panel.

Via API:

curl -X PUT https://api.bastio.com/security/profiles \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ibac_verification_mode": "webhook",
    "ibac_webhook_url": "https://your-api.com/bastio/verify",
    "ibac_webhook_secret": "whsec_your_signing_secret_here"
  }'

Webhook Payload Format

When a user retries with verification headers, Bastio sends a POST request to your webhook URL:

{
  "challenge_id": "ch_abc123",
  "verification_token": "tok_user_provided_token",
  "proxy_id": "your-proxy-id",
  "timestamp": 1711036800
}

Headers included:

  • X-Bastio-Signature: sha256=<hmac_hex> — HMAC-SHA256 of the request body using your signing secret
  • X-Bastio-Timestamp: <unix_seconds> — Request timestamp
  • Content-Type: application/json

Expected response:

{"verified": true}

Or to reject:

{"verified": false, "reason": "Token expired"}

Verifying the Webhook Signature

import hmac
import hashlib
import json
from flask import Flask, request, jsonify

WEBHOOK_SECRET = "whsec_your_signing_secret_here"

app = Flask(__name__)

@app.route("/bastio/verify", methods=["POST"])
def verify_challenge():
    # 1. Verify HMAC signature
    signature = request.headers.get("X-Bastio-Signature", "")
    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(),
        request.data,
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        return jsonify({"verified": False, "reason": "Invalid signature"}), 401

    # 2. Parse payload
    payload = request.json
    challenge_id = payload["challenge_id"]
    token = payload["verification_token"]

    # 3. Validate token against your auth system
    is_valid = your_auth_system.validate_token(token, challenge_id)

    return jsonify({"verified": is_valid})
import crypto from "crypto";
import express from "express";

const WEBHOOK_SECRET = "whsec_your_signing_secret_here";
const app = express();
app.use(express.json());

app.post("/bastio/verify", (req, res) => {
  // 1. Verify HMAC signature
  const signature = req.headers["x-bastio-signature"] as string;
  const body = JSON.stringify(req.body);
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", WEBHOOK_SECRET).update(body).digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).json({ verified: false, reason: "Invalid signature" });
  }

  // 2. Parse payload
  const { challenge_id, verification_token } = req.body;

  // 3. Validate token against your auth system
  const isValid = yourAuthSystem.validateToken(verification_token, challenge_id);

  res.json({ verified: isValid });
});

Challenge TTL

Challenges expire after 10 minutes. If a user takes longer to verify, they'll receive a new challenge when they retry. This prevents replay attacks with stale challenge IDs.

Testing Intent Detection

Via API

Test a prompt against your rules without making a real request:

curl -X POST https://api.bastio.com/security/intent-rules/test \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "What is the shipping address for order #34004?"
  }'

Example response:

{
  "detected": true,
  "category": "order_lookup",
  "confidence": 0.90,
  "matched_patterns": ["shipping address", "order #"],
  "required_verification": ["identity_verification", "email_verification"],
  "challenge_message": "I'd be happy to help with your order, but I need to verify your identity first. Please log in or verify your email to access order information."
}

Testing a safe prompt:

curl -X POST https://api.bastio.com/security/intent-rules/test \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "What are your store hours?"
  }'
{
  "detected": false,
  "confidence": 0.0,
  "matched_patterns": [],
  "required_verification": []
}

Analytics & Monitoring

Event Stats API

Track IBAC activity over time:

curl -X GET "https://api.bastio.com/security/intent-events/stats?days=7" \
  -H "Authorization: Bearer YOUR_TOKEN"

Example response:

{
  "total_events": 1247,
  "challenges_issued": 89,
  "by_category": [
    { "category": "order_lookup", "count": 52 },
    { "category": "account_info", "count": 21 },
    { "category": "payment_data", "count": 9 },
    { "category": "personal_info", "count": 5 },
    { "category": "admin_action", "count": 2 }
  ],
  "recent_events": [
    {
      "timestamp": "2026-03-19T14:32:00Z",
      "category": "order_lookup",
      "confidence": 0.90,
      "challenged": true
    }
  ]
}

Dashboard Analytics

Navigate to Security Center > Intent Detection to view:

  • Total detections over the selected time period
  • Challenge rate — percentage of detected intents that triggered challenges
  • Category breakdown — which categories are triggered most frequently
  • Recent events — timeline of recent intent detections with details

Use these metrics to tune your rules — a high false-positive rate on a category suggests patterns that are too broad.

API Reference

MethodPathDescription
GET/security/intent-rulesList all intent rules
POST/security/intent-rulesCreate a new intent rule
PUT/security/intent-rules/:idUpdate an existing rule
DELETE/security/intent-rules/:idDelete a rule
GET/security/intent-rules/categoriesList available categories
POST/security/intent-rules/testTest a prompt for intent detection
GET/security/intent-events/statsGet event statistics

List Rules

GET /security/intent-rules
Authorization: Bearer YOUR_TOKEN

Returns all intent rules for your account, including both built-in and custom rules.

Create Rule

POST /security/intent-rules
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

{
  "name": "Rule Name",
  "category": "category_id",
  "patterns": ["pattern1", "pattern2"],
  "required_verification": ["identity_verification"],
  "severity": "medium"
}

Update Rule

PUT /security/intent-rules/:id
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

{
  "name": "Updated Name",
  "patterns": ["pattern1", "pattern2", "pattern3"]
}

Delete Rule

DELETE /security/intent-rules/:id
Authorization: Bearer YOUR_TOKEN

List Categories

GET /security/intent-rules/categories
Authorization: Bearer YOUR_TOKEN

Returns all available intent categories with their metadata, including built-in and custom categories.

Test Detection

POST /security/intent-rules/test
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

{
  "prompt": "Text to test for intent detection"
}

Event Stats

GET /security/intent-events/stats?days=7
Authorization: Bearer YOUR_TOKEN

Query parameter days controls the lookback period (default: 7, max: 90).

Best Practices

  1. Start with built-in categories — They cover the most common data-access patterns out of the box. Monitor for a week before adding custom rules.

  2. Add custom rules for domain-specific data — If your application handles medical records, legal documents, financial portfolios, or other specialized data, create rules with patterns specific to your domain.

  3. Set appropriate verification levels — Not all data is equally sensitive. Use identity_verification for basic data, add admin_verification for truly sensitive operations.

  4. Use bypass_with_verified_user — For returning, authenticated users, set this flag to skip challenges for lower-sensitivity categories. This reduces friction for legitimate users.

  5. Monitor analytics for false positives — Review the Intent Detection analytics dashboard weekly. If a category triggers too often on innocent requests, narrow its patterns.

  6. Use proxy-specific rules — Different proxies serve different use cases. A customer support proxy needs different rules than an internal analytics proxy.

  7. Test before deploying — Use the /security/intent-rules/test endpoint to validate your patterns against real user prompts before activating rules.

Next Steps