The Control Plane is a new architectural layer in UnifyWeaver designed to provide declarative control over how the compiler evaluates and generates code for Prolog predicates. It introduces a strict separation between security policies (Firewall) and implementation choices (Preferences).
This system allows fine-grained control over which backends and services can be used, and in what order, ensuring both security and flexibility.
Settings are applied in a three-tiered hierarchy, with more specific layers overriding more general ones. The final choice at each step must always be validated against the firewall.
The system distinguishes between:
bash, python).sql, vector_db, llm).If no explicit firewall policy is defined for a predicate, the system defaults to a safe, symbolic computation mode, ensuring that no unintended external services are invoked.
The firewall enforces hard security and policy boundaries. It is defined using rule_firewall/2 for predicate-specific rules and firewall_default/1 for global defaults.
firewall:rule_firewall(+PredicateIndicator, +PolicyTerms:list)Declares predicate-specific firewall policies.
Policy Terms:
execution([backend1, backend2, ...]): Whitelist of allowed primary execution backends (e.g., bash, python).services([service1, service2, ...]): Whitelist of allowed services that can be called from an execution backend (e.g., sql, vector_db, llm).denied([backend1, service1, ...]): Blacklist of backends or services that are explicitly forbidden. This always takes precedence.max_cost(low | medium | high): An abstract cost limit to prevent expensive operations.firewall:firewall_default(+PolicyTerms:list)Declares global default firewall policies. If no rule_firewall is found for a predicate, firewall_default is used. If firewall_default is also not defined, the system defaults to implicit allow.
firewall:validate_against_firewall(+Target, +Options, +Firewall) is called by the main compiler dispatcher to enforce policies.
The preference system guides the compiler on which implementation to choose from the options permitted by the firewall. It is defined using rule_preferences/2 for predicate-specific settings and preferences_default/1 for global defaults.
preferences:rule_preferences(+PredicateIndicator, +PreferenceTerms:list)Declares predicate-specific preference settings.
Preference Terms:
prefer([backend1, service1, ...]): The desired order of implementation choice.fallback_order([backend2, service2, ...]): The order to try if preferred implementations fail.optimization(speed | memory | balance): A hint for the compiler to choose the most appropriate template or strategy.service_mode(embedded | remote): For services like databases, whether to prefer a local file/process or a remote server.preferences:preferences_default(+PreferenceTerms:list)Declares global default preference settings for the entire project.
preferences:get_final_options(+Rule, +RuntimeOptions, -FinalOptions) is called by the main compiler dispatcher to merge options from all layers.
The main compilation dispatcher (recursive_compiler.pl) orchestrates the control plane logic. The conceptual flow is:
preferences:get_final_options/3 retrieves and merges preferences.firewall:rule_firewall/2 (or firewall_default/1) retrieves the policy.firewall:validate_against_firewall/3 validates the request against the policy. If it fails, compilation halts.FinalOptions.% 1. Define firewall rules (in config/firewall.pl or asserted at runtime)
:- firewall:rule_firewall(sensitive_pred/2, [denied([llm])]).
:- firewall:rule_firewall(db_access_pred/2, [execution([bash]), services([sql])]).
% 2. Define preferences (in config/preferences.pl or asserted at runtime)
:- preferences:rule_preferences(db_access_pred/2, [prefer([bash]), service_mode(embedded)]).
:- preferences:preferences_default([prefer([bash]), optimization(balance)]).
% 3. Compile with runtime override (in your Prolog session)
% This should succeed, using bash and embedded SQL
?- compile_recursive(db_access_pred/2, [], BashCode).
% This should fail due to firewall policy
?- compile_recursive(sensitive_pred/2, [use_services([llm])], BashCode).
% Output: Firewall Error: Service 'llm' is explicitly denied.
% This should succeed, overriding preferences to use Python (if allowed by firewall)
?- compile_recursive(db_access_pred/2, [prefer([python])], BashCode).
Configuration files for project-wide firewall rules and preferences are located in src/unifyweaver/config/:
firewall.pl: For global firewall policies.preferences.pl: For global preference settings.The control plane’s functionality is verified by tests/core/test_policy.pl. This test suite covers firewall validation, preference merging, and ensures correct behavior for allowed, denied, and overridden scenarios.
max_cost, data_hygiene).