Chapters: 

intake evidence β†’ classify the box β†’ apply policy rules β†’ produce readiness / scan / agent posture β†’ flag exceptions β†’ generate a report.

WTF is in this box?

What is this?
Do we know enough?
Who owns it?
Where does it live?
Can we scan it?
Can we install an agent?
What is the risk?
What exception is needed?
What should happen next?

Β 

Automation shape

1. Intake
   		↓
2. Normalize evidence
   		↓
3. Classify machine type + zone
   		↓
4. Apply policy rules
   		↓
5. Produce output report / recommendation / exception

Core files

Example

policy-engine/
β”œβ”€β”€ policies/
β”‚   β”œβ”€β”€ readiness_policy.yml
β”‚   β”œβ”€β”€ scan_agent_policy.yml
β”‚   └── zone_mapping.yml
β”œβ”€β”€ schemas/
β”‚   β”œβ”€β”€ asset_input.schema.json
β”‚   └── assessment_output.schema.json
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ standard_it_server.yml
β”‚   β”œβ”€β”€ legacy_server.yml
β”‚   β”œβ”€β”€ vendor_appliance.yml
β”‚   β”œβ”€β”€ plc.yml
β”‚   └── unknown_device.yml
β”œβ”€β”€ tests/
β”‚   └── test_policy_cases.yml
β”œβ”€β”€ output/
β”‚   └── assessments/
└── assess_box.py

The central input: β€œWTF is this box?”

Every assessment starts with a machine record:

asset_id: BOX-001
hostname: srv-mail-01
owner: infrastructure_team
requested_role: mail_server
machine_type: standard_it_server
zone: enterprise

evidence:
  inventory_record: true
  owner_attestation: true
  authenticated_scan: true
  cloud_metadata: false
  vendor_documentation: false
  network_observation: true

attributes:
  os_known: true
  support_status: supported
  internet_facing: false
  safety_critical: false
  vendor_managed: false
  legacy: false
  fragile: false
  short_lived: false
  operational_impact_unknown: false

constraints:
  agent_install_forbidden: false
  active_scan_forbidden: false
  requires_manual_approval: false

That gives the policy engine enough material to make a decision without inventing things in the fog.

Policy A: readiness rules

policy_id: readiness_policy
policy_name: Machine Readiness Policy
version: 0.1

default_status: review_required

statuses:
  - approved
  - approved_with_limitations
  - review_required
  - rejected
  - exception_required
  - unknown

rules:
  - id: RDY-001
    description: System must have a known owner.
    when:
      owner: null
    result:
      readiness_status: unknown
      confidence: low
      recommendation: Identify accountable owner before onboarding.

  - id: RDY-002
    description: System must have a declared role.
    when:
      requested_role: null
    result:
      readiness_status: unknown
      confidence: low
      recommendation: Confirm business or technical function.

  - id: RDY-003
    description: System must map to a known machine type.
    when:
      machine_type: unknown
    result:
      readiness_status: review_required
      confidence: low
      recommendation: Classify machine type before assessment.

  - id: RDY-004
    description: Legacy systems require limitation notes.
    when:
      attributes.legacy: true
    result:
      readiness_status: approved_with_limitations
      confidence: medium
      limitation: Legacy system requires controlled handling.

  - id: RDY-005
    description: Vendor-managed systems require support restriction review.
    when:
      attributes.vendor_managed: true
    result:
      readiness_status: exception_required
      confidence: medium
      recommendation: Review vendor contract and support restrictions.

  - id: RDY-006
    description: OT or safety-critical systems require human review.
    when:
      attributes.safety_critical: true
    result:
      readiness_status: review_required
      confidence: medium
      recommendation: Require operational impact review before onboarding.

Policy B: scan / agent posture rules

policy_id: scan_agent_policy
policy_name: Scan / Agent Posture Policy
version: 0.1

default_scan_posture: limited
default_agent_posture: restricted

scan_postures:
  - normal_authenticated
  - controlled
  - limited
  - passive_only
  - manual_evidence_only
  - prohibited

agent_postures:
  - allowed
  - allowed_with_controls
  - restricted
  - prohibited
  - exception_required
  - not_applicable

rules:
  - id: TCH-001
    description: If operational impact is unknown, do not actively scan.
    when:
      attributes.operational_impact_unknown: true
    result:
      scan_posture: passive_only
      agent_posture: restricted
      recommendation: Confirm operational impact before active assessment.

  - id: TCH-002
    description: Safety-critical systems default to passive or manual evidence.
    when:
      attributes.safety_critical: true
    result:
      scan_posture: manual_evidence_only
      agent_posture: prohibited
      recommendation: Use owner/vendor evidence unless exception is approved.

  - id: TCH-003
    description: Vendor restrictions may prohibit agents.
    when:
      constraints.agent_install_forbidden: true
    result:
      agent_posture: prohibited
      recommendation: Do not install agent.

  - id: TCH-004
    description: Fragile systems require controlled scan and restricted agent posture.
    when:
      attributes.fragile: true
    result:
      scan_posture: controlled
      agent_posture: restricted
      recommendation: Use controlled assessment window.

  - id: TCH-005
    description: Internet-facing systems require controlled scanning.
    when:
      attributes.internet_facing: true
    result:
      scan_posture: controlled
      agent_posture: allowed_with_controls
      recommendation: Log and scope scan activity.

  - id: TCH-006
    description: Unknown systems must not be scanned or modified.
    when:
      machine_type: unknown
    result:
      scan_posture: prohibited
      agent_posture: prohibited
      recommendation: Identify system before assessment.

Zone mapping automation

Example Distribution Environment Reference Architecture

This is where the network diagram becomes useful without becoming the law.

zones:
  enterprise:
    typical_systems:
      - it_server
      - workstation
      - dns
      - mail
      - jump_host
    default_scan_posture: normal_authenticated
    default_agent_posture: allowed
    notes: Standard managed space.

  it_dmz:
    typical_systems:
      - vpn
      - web_server
      - internet_facing_service
    default_scan_posture: controlled
    default_agent_posture: allowed_with_controls
    notes: Higher exposure, controlled scanning required.

  ot_dmz:
    typical_systems:
      - patch_mirror
      - jump_host
      - wsus
      - vault
    default_scan_posture: limited
    default_agent_posture: restricted
    notes: Bridge zone between IT and OT.

  supervisory:
    typical_systems:
      - hmi
      - historian
      - scada
      - application_server
    default_scan_posture: limited
    default_agent_posture: restricted
    notes: Industrial context, role-based assessment.

  local_station:
    typical_systems:
      - plc
      - rtu
      - relay
      - sensor
      - tac
    default_scan_posture: manual_evidence_only
    default_agent_posture: prohibited
    notes: Fragile operational systems.

  remote_ami:
    typical_systems:
      - meter
      - access_point
      - bridge
      - field_device
    default_scan_posture: limited
    default_agent_posture: prohibited
    notes: Remote constraints and ownership issues.

Output format

The engine should produce this:

asset_id: BOX-001
hostname: srv-mail-01

decision:
  readiness_status: approved
  scan_posture: normal_authenticated
  agent_posture: allowed
  confidence: high

findings:
  - Owner is known.
  - Requested role is known.
  - Machine type is standard IT server.
  - Zone allows normal authenticated scan.
  - No agent restrictions identified.

limitations: []

recommendations:
  - Admit system into managed workflow.
  - Use standard authenticated scan.
  - Agent installation allowed under normal controls.

exceptions:
  required: false
  reasons: []

reassessment:
  required: true
  cadence: annual

"Minimum!" viable automation

Input one machine YAML
Apply readiness policy
Apply scan / agent policy
Apply zone defaults
Produce assessment YAML or JSON

That is enough to prove the concept.

First Python sketch

import yaml
from pathlib import Path


def get_nested_value(data, dotted_key):
    current = data
    for part in dotted_key.split("."):
        if not isinstance(current, dict):
            return None
        current = current.get(part)
    return current


def condition_matches(asset, condition):
    for key, expected in condition.items():
        actual = get_nested_value(asset, key)
        if actual != expected:
            return False
    return True


def apply_rules(asset, policy):
    results = {
        "matched_rules": [],
        "readiness_status": policy.get("default_status"),
        "scan_posture": policy.get("default_scan_posture"),
        "agent_posture": policy.get("default_agent_posture"),
        "confidence": "medium",
        "findings": [],
        "limitations": [],
        "recommendations": [],
        "exceptions": {
            "required": False,
            "reasons": []
        }
    }

    for rule in policy.get("rules", []):
        if condition_matches(asset, rule.get("when", {})):
            results["matched_rules"].append(rule["id"])
            rule_result = rule.get("result", {})

            for key, value in rule_result.items():
                if key == "recommendation":
                    results["recommendations"].append(value)
                elif key == "limitation":
                    results["limitations"].append(value)
                elif key == "readiness_status" and value == "exception_required":
                    results["readiness_status"] = value
                    results["exceptions"]["required"] = True
                    results["exceptions"]["reasons"].append(rule["description"])
                else:
                    results[key] = value

            results["findings"].append(rule["description"])

    return results


def merge_results(*result_sets):
    merged = {
        "matched_rules": [],
        "readiness_status": None,
        "scan_posture": None,
        "agent_posture": None,
        "confidence": "medium",
        "findings": [],
        "limitations": [],
        "recommendations": [],
        "exceptions": {
            "required": False,
            "reasons": []
        }
    }

    for result in result_sets:
        merged["matched_rules"].extend(result.get("matched_rules", []))
        merged["findings"].extend(result.get("findings", []))
        merged["limitations"].extend(result.get("limitations", []))
        merged["recommendations"].extend(result.get("recommendations", []))

        for key in ["readiness_status", "scan_posture", "agent_posture", "confidence"]:
            if result.get(key):
                merged[key] = result[key]

        if result.get("exceptions", {}).get("required"):
            merged["exceptions"]["required"] = True
            merged["exceptions"]["reasons"].extend(
                result["exceptions"].get("reasons", [])
            )

    return merged


def assess(asset_path):
    asset = yaml.safe_load(Path(asset_path).read_text())

    readiness_policy = yaml.safe_load(Path("policies/readiness_policy.yml").read_text())
    scan_agent_policy = yaml.safe_load(Path("policies/scan_agent_policy.yml").read_text())

    readiness_result = apply_rules(asset, readiness_policy)
    scan_agent_result = apply_rules(asset, scan_agent_policy)

    decision = merge_results(readiness_result, scan_agent_result)

    assessment = {
        "asset_id": asset.get("asset_id"),
        "hostname": asset.get("hostname"),
        "machine_type": asset.get("machine_type"),
        "zone": asset.get("zone"),
        "owner": asset.get("owner"),
        "requested_role": asset.get("requested_role"),
        "decision": {
            "readiness_status": decision.get("readiness_status"),
            "scan_posture": decision.get("scan_posture"),
            "agent_posture": decision.get("agent_posture"),
            "confidence": decision.get("confidence"),
        },
        "matched_rules": decision["matched_rules"],
        "findings": decision["findings"],
        "limitations": decision["limitations"],
        "recommendations": decision["recommendations"],
        "exceptions": decision["exceptions"],
        "reassessment": {
            "required": True,
            "cadence": "annual"
        }
    }

    return assessment


if __name__ == "__main__":
    result = assess("examples/standard_it_server.yml")
    print(yaml.dump(result, sort_keys=False))

The real product hiding in this

This becomes a policy-guided infrastructure intake engine.

Not a scanner.
Not an agent.
Not Kubernetes confetti.
Not β€œAI will fix it.”

It answers:

What is this?
Do we know enough?
Who owns it?
Where does it live?
Can we scan it?
Can we install an agent?
What is the risk?
What exception is needed?
What should happen next?

That is an actual business object.

The first automation target should be:

Given one machine record, produce a readiness + touchability decision.

That is the little iron beetle we build first. Then we can bolt on CSV intake, diagram-zone mapping, report generation, and test cases after it walks.

πŸͺ²