Build the CHP Configuration Reconciliation Engine
The Health Engine answers:
Is the website working?
The Reconciliation Engine answers:
Is the website configured the way CHP expects?
These are different questions.
A website can be healthy but incorrectly configured.
Health:
HEALTHY
Configuration:
DRIFTED
For example:
Desired PHP:
8.3
Actual PHP:
8.2
The website may still work, but the configuration is no longer what CHP expects.
1. Desired State vs Actual State
The central model is:
DESIRED STATE
│
▼
RECONCILIATION
▲
│
ACTUAL STATE
│
▼
DIFF
Result:
MATCH
DRIFT
UNKNOWN
CONFLICT
2. What Is Desired State?
Desired state is what CHP believes the site should look like.
Example:
{
"domain": "example.com",
"web_root": "/storage/websites/example.com/public",
"php_version": "8.3",
"php_fpm_socket": "/run/php/php8.3-fpm.sock",
"ssl_enabled": true,
"backup_enabled": true,
"health_monitoring": true
}
3. What Is Actual State?
Actual state is what CHP discovers from the server.
Example:
{
"domain": "example.com",
"web_root": "/storage/websites/example.com/public",
"php_version": "8.2",
"php_fpm_socket": "/run/php/php8.2-fpm.sock",
"ssl_enabled": true,
"backup_enabled": true,
"health_monitoring": true
}
4. Reconciliation
Compare:
DESIRED ACTUAL
-------- -------
PHP 8.3 PHP 8.2
SSL enabled SSL enabled
Backup enabled Backup enabled
Health enabled Health enabled
Result:
PHP:
DRIFT
SSL:
MATCH
Backup:
MATCH
Health:
MATCH
5. Reconciliation Must Not Automatically Repair
This is critical.
The reconciliation engine should initially do:
DISCOVER
↓
COMPARE
↓
REPORT
not:
DISCOVER
↓
COMPARE
↓
CHANGE PRODUCTION
The repair system remains a separate controlled process.
6. Architecture
SITE
│
┌────────┴────────┐
▼ ▼
DESIRED STATE ACTUAL STATE
│ │
└────────┬────────┘
▼
RECONCILIATION
│
▼
DIFF
│
┌──────────┼──────────┐
▼ ▼ ▼
MATCH DRIFT UNKNOWN
│
▼
REPAIR PLAN
7. Configuration Domains
Reconciliation should be divided into domains:
DOMAIN
WEB
PHP
DATABASE
SSL
DNS
FILESYSTEM
WORDPRESS
BACKUP
HEALTH
8. Why Divide Configuration?
Suppose:
PHP:
DRIFT
while:
SSL:
MATCH
The system can generate a precise repair plan instead of treating the whole site as broken.
9. Desired State Table
Create:
CREATE TABLE desired_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER NOT NULL,
config_domain TEXT NOT NULL,
config_version INTEGER NOT NULL DEFAULT 1,
desired_state TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (site_id)
REFERENCES sites(id)
ON DELETE CASCADE
);
10. Example
For:
example.com
you could have:
WEB
PHP
SSL
DATABASE
BACKUP
HEALTH
as separate configuration records.
11. Why config_version?
Suppose:
version 1:
PHP 8.2
then:
version 2:
PHP 8.3
CHP can determine:
desired configuration changed
rather than confusing it with external drift.
12. Configuration Fingerprint
Create a normalized representation:
{
"php_version": "8.3",
"ssl_enabled": true,
"backup_enabled": true
}
Then calculate a hash.
Example:
desired fingerprint:
abc123
Actual:
actual fingerprint:
def456
If:
abc123 == def456
the states match.
13. Don’t Compare Raw JSON Text
These two are logically identical:
{
"php": "8.3",
"ssl": true
}
and:
{
"ssl": true,
"php": "8.3"
}
String comparison would incorrectly report drift.
Normalize the data first.
14. Normalization
Before comparison:
RAW
↓
NORMALIZE
↓
SORT
↓
STANDARDIZE
↓
COMPARE
For example:
PHP:
"8.3 "
should become:
"8.3"
before comparison.
15. Actual State Collector
Create:
sudo nano /etc/cresignsys/lib/state-collector.sh
Functions:
collect_domain_state
collect_web_state
collect_php_state
collect_database_state
collect_ssl_state
collect_dns_state
collect_filesystem_state
collect_wordpress_state
16. Domain State
Collect:
primary domain
aliases
www domain
document root
Example:
{
"domain": "example.com",
"aliases": [
"www.example.com"
],
"web_root": "/storage/websites/example.com/public"
}
17. Web State
Collect:
Nginx site configuration
enabled/disabled
configuration test
server blocks
proxy configuration
PHP handler
Example:
{
"server": "nginx",
"enabled": true,
"config_test": true
}
18. PHP State
Collect:
PHP version
PHP-FPM service
PHP-FPM socket
configured pool
Example:
{
"version": "8.3",
"fpm_service": "php8.3-fpm",
"socket": "/run/php/php8.3-fpm.sock"
}
19. Database State
Collect:
engine
database name
host
port
connectivity
Do not expose:
password
in the actual-state object.
20. SSL State
Collect:
enabled
certificate expiry
issuer
subject
SAN
certificate fingerprint
Example:
{
"enabled": true,
"expires_at": "2026-09-20T00:00:00",
"issuer": "Let's Encrypt",
"fingerprint": "..."
}
21. DNS State
Collect:
A
AAAA
CNAME
where applicable.
Example:
{
"A": [
"203.0.113.10"
]
}
Don’t automatically classify a DNS difference as an error.
It could be intentional.
22. Filesystem State
Collect:
web root exists
owner
group
permissions
storage filesystem
disk usage
inode usage
Example:
{
"exists": true,
"owner": "www-data",
"group": "www-data",
"permissions": "0755"
}
23. WordPress State
For WordPress:
core version
plugin list
theme
site URL
database connection
Example:
{
"installed": true,
"core_version": "7.0.4"
}
24. WordPress Plugin State
A desired configuration could specify:
{
"required_plugins": [
"plugin-a",
"plugin-b"
]
}
Actual state:
{
"installed_plugins": [
"plugin-a",
"plugin-c"
]
}
Reconciliation detects:
plugin-b:
MISSING
plugin-c:
UNEXPECTED
But this should be treated carefully.
An unexpected plugin isn’t automatically something CHP should delete.
25. Configuration Ownership
This leads to an important concept:
CHP should only reconcile configuration that it owns.
For example:
CHP-managed:
Nginx virtual host
PHP-FPM mapping
backup schedule
health policy
But perhaps:
customer-managed:
WordPress plugins
WordPress themes
application content
unless the hosting plan explicitly enables CHP management.
26. Managed Fields
Add ownership:
{
"php_version": {
"value": "8.3",
"managed": true
},
"plugins": {
"value": [],
"managed": false
}
}
This prevents CHP from fighting customer changes.
27. Desired State Should Be Explicit
Don’t infer:
"Everything I discover is what the site should be."
Instead:
desired:
explicitly configured
and:
actual:
discovered
28. Unknown Desired State
If CHP doesn’t know:
desired PHP version
then:
actual PHP:
8.3
should produce:
UNKNOWN
not:
MATCH
and not:
DRIFT
29. Unknown Actual State
If CHP cannot determine:
PHP-FPM socket
then:
actual:
UNKNOWN
The result becomes:
UNKNOWN
rather than assuming:
DRIFT
30. Conflict
A conflict is different from ordinary drift.
Example:
Desired:
PHP 8.3
Actual:
PHP 8.2
Another active CHP job:
upgrade PHP to 8.4
Now CHP has conflicting desired information.
Result:
CONFLICT
rather than simply:
DRIFT
31. Reconciliation Result Table
Create:
CREATE TABLE reconciliation_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER NOT NULL,
config_domain TEXT NOT NULL,
status TEXT NOT NULL,
desired_state TEXT,
actual_state TEXT,
diff TEXT,
desired_fingerprint TEXT,
actual_fingerprint TEXT,
checked_at TEXT NOT NULL,
FOREIGN KEY (site_id)
REFERENCES sites(id)
ON DELETE CASCADE
);
32. Result Status
Use:
MATCH
DRIFT
UNKNOWN
CONFLICT
ERROR
33. Diff Object
Example:
{
"php_version": {
"desired": "8.3",
"actual": "8.2",
"change": "MODIFY"
}
}
This can directly feed the repair planner.
34. Multiple Drift Items
Example:
{
"php_version": {
"desired": "8.3",
"actual": "8.2"
},
"web_root": {
"desired": "/storage/websites/example.com/public",
"actual": "/storage/websites/example.com/public_html"
}
}
The repair planner can then determine whether these changes are related.
35. Reconciliation Command
Create:
sudo nano /usr/local/bin/hosting-reconcile
Usage:
sudo hosting-reconcile example.com
36. Example Output
CresignSys Reconciliation
=========================
Site:
example.com
DOMAIN:
MATCH
WEB:
MATCH
PHP:
DRIFT
Desired:
8.3
Actual:
8.2
DATABASE:
MATCH
SSL:
MATCH
BACKUP:
MATCH
HEALTH:
MATCH
Overall:
DRIFT
37. All Sites
sudo hosting-reconcile --all
Output:
SITE RESULT
--------------------------------
example.com MATCH
shop.example.com DRIFT
blog.example.com MATCH
client.example.com UNKNOWN
38. Reconciliation Scheduler
The reconciliation engine should use the central job queue.
Flow:
scheduler
↓
RECONCILE job
↓
worker
↓
collect actual state
↓
load desired state
↓
compare
↓
store result
↓
emit event
39. Reconciliation Frequency
Example:
Nginx:
15 minutes
PHP:
15 minutes
SSL:
1 hour
DNS:
30 minutes
Backup policy:
1 hour
Again, these are examples.
Configuration that changes rarely doesn’t need minute-by-minute reconciliation.
40. Reconciliation Event
If:
MATCH
and remains:
MATCH
don’t generate a new event every cycle.
Only emit meaningful transitions.
Example:
MATCH
↓
DRIFT
generates:
DRIFT_DETECTED
Then:
DRIFT
↓
MATCH
generates:
DRIFT_RESOLVED
41. Event Metadata
Example:
{
"domain": "PHP",
"field": "php_version",
"desired": "8.3",
"actual": "8.2"
}
This is much better than:
"PHP mismatch"
42. Drift Fingerprint
Create:
DRIFT:site7:PHP:php_version
or a hash.
This allows the alert system to deduplicate:
DRIFT_DETECTED
DRIFT_DETECTED
DRIFT_DETECTED
into one alert.
43. Drift Alert
The Alert Engine from Lesson 091 can now process:
DRIFT_DETECTED
Example:
WARNING
Configuration drift detected on example.com.
PHP version:
Expected 8.3
Actual 8.2
44. Drift Is Not Always an Error
Suppose customer intentionally changed:
PHP:
8.3 → 8.2
CHP should detect:
DRIFT
but shouldn’t necessarily repair it.
Instead:
DRIFT
+
customer-managed field
could produce:
INFO
or no alert.
45. Managed vs Unmanaged Drift
This is one of the most important CHP concepts.
Managed drift
CHP owns field
Example:
Nginx config
→ alert/repair candidate.
Unmanaged drift
Customer owns field
Example:
WordPress plugin
→ report only.
46. Configuration Ownership Table
Create:
CREATE TABLE config_ownership (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER NOT NULL,
config_domain TEXT NOT NULL,
config_key TEXT NOT NULL,
management_mode TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (site_id)
REFERENCES sites(id)
ON DELETE CASCADE
);
Modes:
MANAGED
UNMANAGED
READ_ONLY
47. Example
example.com
Nginx:
MANAGED
PHP:
MANAGED
SSL:
MANAGED
WordPress plugins:
UNMANAGED
WordPress content:
UNMANAGED
48. Why This Is Important for Reseller Hosting
Different customers want different levels of management.
Customer A:
hosting only
Customer B:
managed WordPress
Customer C:
fully managed website
CHP can use ownership policies to support all three.
49. Desired State Sources
Desired configuration can come from:
hosting plan
site settings
customer request
administrator configuration
approved repair plan
But there should be one canonical desired state.
50. Desired State Precedence
Example:
PLAN DEFAULT
↓
SITE OVERRIDE
↓
APPROVED CHANGE
↓
CURRENT DESIRED STATE
This prevents conflicting configuration sources.
51. Don’t Let the Repair Plan Become Desired State Automatically
Suppose a repair plan proposes:
PHP 8.3 → 8.4
The plan should not change the desired state merely because it was created.
Only after explicit approval/execution should the desired state be updated according to the defined policy.
52. Desired State Versioning
Every desired configuration change should create a new version:
version 1:
PHP 8.2
version 2:
PHP 8.3
version 3:
PHP 8.4
This gives CHP configuration history.
53. Configuration History Table
You can eventually add:
CREATE TABLE desired_config_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER NOT NULL,
config_version INTEGER NOT NULL,
config_state TEXT NOT NULL,
actor_id INTEGER,
reason TEXT,
created_at TEXT NOT NULL
);
54. Example
Version 1
---------
PHP 8.2
Created by:
SYSTEM
Version 2
---------
PHP 8.3
Created by:
ADMIN
Reason:
Upgrade PHP
This gives a clean audit trail.
55. Reconciliation Does Not Mean Enforcement
Think of it as:
RECONCILIATION
=
measurement + comparison
while:
REPAIR
=
controlled enforcement
Keeping them separate is safer.
56. Reconciliation → Repair
Once drift is detected:
DRIFT
↓
DIFF
↓
REPAIR PLAN
Example:
Plan:
Change PHP 8.2 → 8.3
Then:
PLAN
↓
VALIDATE
↓
APPROVE
↓
JOB
↓
BACKUP
↓
REPAIR
57. Never Create a Repair Plan From Raw Text
Bad:
"PHP is wrong"
Good:
{
"operation": "SET_PHP_VERSION",
"desired": "8.3",
"actual": "8.2"
}
The repair engine can validate structured operations.
58. Reconciliation Safety
Before creating a repair plan, confirm:
desired state is current
actual state is current
no conflicting job
no active restore
no maintenance lock
59. Reconciliation Race Condition
Example:
18:00
reconciliation sees PHP 8.2
18:01
admin manually changes PHP to 8.3
18:02
old repair plan says:
change 8.2 → 8.3
That plan is now stale.
Therefore the repair planner must re-check actual state before execution.
60. Desired/Actual Version Fingerprint
A repair plan should record:
desired fingerprint:
ABC123
actual fingerprint:
XYZ789
At execution:
current actual fingerprint:
NEW456
If:
XYZ789 != NEW456
the plan is stale.
Stop.
61. Stale Plan
Result:
PLAN_STALE
not:
repair anyway
This prevents CHP from applying outdated assumptions.
62. Reconciliation Status
The site can now have:
CONFIGURATION:
MATCH
DRIFT
UNKNOWN
CONFLICT
and the dashboard can show it separately from:
HEALTH:
HEALTHY
63. Example Dashboard
example.com
Health
------
HEALTHY
98/100
Configuration
-------------
DRIFT
PHP:
Expected 8.3
Actual 8.2
Backup
------
HEALTHY
Security
--------
HEALTHY
Alerts
------
1 WARNING
This is a very useful hosting control-plane view.
64. Reconciliation Summary
For all sites:
SITE HEALTH CONFIG
------------------------------------------------
example.com HEALTHY MATCH
shop.example.com HEALTHY DRIFT
blog.example.com WARNING MATCH
client.example.com DOWN UNKNOWN
65. Reconciliation Metrics
Eventually track:
sites matching desired state
sites with drift
sites unknown
sites with conflicts
Example:
Managed Sites:
120
MATCH:
108
DRIFT:
9
UNKNOWN:
2
CONFLICT:
1
66. Configuration Compliance
This becomes useful for hosting plans.
Example:
Configuration compliance:
90%
But again, don’t hide important individual drift behind a single percentage.
67. Event Flow
The complete configuration lifecycle is now:
ACTUAL STATE
│
▼
RECONCILIATION
│
▼
COMPARE
│
┌────┼──────────┐
▼ ▼ ▼
MATCH DRIFT UNKNOWN
│
▼
EVENT
│
▼
ALERT
│
▼
REPAIR PLAN
│
▼
APPROVAL
│
▼
JOB
│
▼
REPAIR
│
▼
RECONCILIATION
│
▼
MATCH
This is the core control-loop architecture.
68. The CHP Control Loop
CHP is now moving toward a true reconciliation-based control plane:
DESIRED
│
▼
OBSERVE
│
▼
COMPARE
│
▼
DRIFT
│
▼
PLAN
│
▼
APPROVAL
│
▼
BACKUP
│
▼
APPLY
│
▼
VERIFY
│
▼
RECONCILE
│
┌───┴───┐
▼ ▼
MATCH DRIFT
│ │
│ └──→ investigate
▼
COMPLETE
69. Lesson 093 — Core Principle
The most important architectural rule is:
Observe first. Change later.
Reconciliation should never assume that a difference automatically means something is broken.
A difference can be:
intentional
unintentional
customer-managed
CHP-managed
temporary
unknown
conflicting
Therefore CHP should first establish:
WHAT IS DESIRED?
WHAT IS ACTUAL?
WHO OWNS THE FIELD?
WHAT EXACTLY IS DIFFERENT?
Only then should the Repair Engine consider changing the site.
Next Lesson — 094
Build the CHP Desired-State & Configuration Management System
The reconciliation engine can now detect:
DESIRED ≠ ACTUAL
But we need a reliable source for:
What exactly is the desired configuration?
Lesson 094 will build the configuration management layer:
HOSTING PLAN
│
▼
SITE TEMPLATE
│
▼
SITE OVERRIDES
│
▼
DESIRED STATE
│
▼
RECONCILIATION
It will define:
site profiles
PHP policies
Nginx policies
SSL policies
backup policies
health policies
resource limits
WordPress management mode
customer overrides
configuration versioning
and establish which settings are:
DEFAULT
INHERITED
OVERRIDDEN
LOCKED
MANAGED
CUSTOMER-MANAGED
That will provide the missing foundation for generating safe, deterministic Repair Plans.
Leave a Reply