Build hosting-reconcile
We now have two sources of information:
CHP DATABASE
│
▼
Known / Desired State
and:
LIVE SERVER
│
▼
Observed / Actual State
The reconciliation engine compares them.
The first version must be read-only.
1. Reconciliation Command
Create:
sudo nano /usr/local/bin/hosting-reconcile
Usage:
sudo hosting-reconcile example.com
Also support:
sudo hosting-reconcile example.com --json
sudo hosting-reconcile example.com --verbose
Do not add automatic repair yet.
2. Core Principle
Reconciliation answers:
Is the actual server consistent with what CHP knows or expects?
It does not answer:
How should I change the server?
That belongs to:
hosting-repair
3. Architecture
CHP DATABASE
│
▼
EXPECTED STATE
│
│
▼
RECONCILIATION
▲
│
│
ACTUAL STATE
│
┌────────────────┼────────────────┐
▼ ▼ ▼
Nginx PHP SSL
│ │ │
▼ ▼ ▼
DNS WordPress MySQL
4. Reconciliation Result Types
Use a small, consistent vocabulary:
MATCH
DRIFT
MISSING
UNEXPECTED
UNKNOWN
ERROR
MATCH
Expected and actual are consistent.
DRIFT
Both exist but differ.
MISSING
Expected component doesn’t exist.
UNEXPECTED
Something exists that CHP does not expect.
UNKNOWN
Unable to determine state.
ERROR
The check itself failed.
5. Example
CHP says:
PHP = 8.3
Actual:
PHP = 8.2
Result:
DRIFT
6. Missing Example
CHP says:
SSL = ACTIVE
but the certificate is gone.
Result:
MISSING
7. Unexpected Example
CHP knows:
example.com
www.example.com
but Nginx contains:
test.example.com
Result:
UNEXPECTED
This can identify configuration that was manually added outside CHP.
8. Unknown Example
Suppose DNS cannot be queried:
DNS:
UNKNOWN
Do not report:
DRIFT
because you don’t know the actual state.
9. Load Libraries
The command should load:
source /etc/cresignsys/hosting.conf
source /etc/cresignsys/lib/common.sh
source /etc/cresignsys/lib/logging.sh
source /etc/cresignsys/lib/domain.sh
source /etc/cresignsys/lib/database.sh
source /etc/cresignsys/lib/discovery.sh
10. Root Requirement
Initially:
require_root
because reconciliation may need to inspect:
/etc/nginx
/etc/letsencrypt
/run/php
and protected site information.
11. Find the Site
The first database lookup is:
domain
↓
domains
↓
site_id
↓
sites
Conceptually:
SELECT site_id
FROM domains
WHERE domain = ?;
12. Unknown Site
If the domain isn’t registered in CHP:
CresignSys Reconciliation
=========================
Domain:
example.com
Result:
UNKNOWN
Reason:
Site is not registered in CHP database.
Do not automatically import it.
Use:
hosting-db-import example.com
instead.
13. Read Site Metadata
Retrieve:
primary_domain
web_root
php_version
site_user
application_type
management_state
service_state
health_state
These become the expected values.
14. Web Root Check
Expected:
/storage/websites/example.com/public
Actual:
[[ -d "$WEB_ROOT_PATH" ]]
Results:
MATCH
or:
MISSING
15. Web Root Drift
Suppose database says:
/storage/websites/example.com/public
but actual site is now:
/storage/websites/example.com/site
Result:
DRIFT
Don’t automatically change the database during reconciliation.
16. Owner Check
Expected:
www-data:www-data
Actual:
stat -c '%U:%G' "$WEB_ROOT_PATH"
If they differ:
DRIFT
Example:
Owner:
Expected: www-data:www-data
Actual: root:root
Result: DRIFT
17. Why Owner Drift Matters
A website accidentally owned by:
root:root
may cause:
WordPress updates fail
uploads fail
cache writes fail
while ownership that is too permissive can create security problems.
Reconciliation should identify this, not silently modify it.
18. Application Check
Database:
WORDPRESS
Actual:
wp-config.php
wp-content/
wp-admin/
Result:
MATCH
If WordPress was removed:
MISSING
If the site was changed to another application:
DRIFT
19. WordPress Version
If the database stores:
WordPress:
6.8.2
and actual:
6.8.3
this may be:
DRIFT
but be careful.
Version differences aren’t necessarily errors.
A future model should distinguish:
DESIRED VERSION
from:
OBSERVED VERSION
20. Configuration vs Runtime
This distinction is important.
Some properties are:
CONFIGURATION
while others are:
RUNTIME HEALTH
For example:
PHP version = configuration
PHP-FPM running = runtime
Therefore:
PHP version:
MATCH
PHP-FPM:
FAIL
is perfectly possible.
21. Nginx Check
First verify:
systemctl is-active --quiet nginx
Then inspect the configuration.
Expected:
example.com
Actual:
server_name example.com www.example.com;
22. Nginx Service Result
Possible:
OK
FAIL
UNKNOWN
For example:
Nginx Service:
OK
But the configuration itself may still have drift.
23. Nginx Configuration Test
Run:
nginx -t
If valid:
Nginx Configuration:
VALID
If invalid:
Nginx Configuration:
ERROR
This should be reported independently of whether the Nginx service is currently running.
24. Nginx Domain Check
Compare expected domains from:
domains
with actual server_name entries.
Example:
EXPECTED
--------
example.com
www.example.com
ACTUAL
------
example.com
www.example.com
old.example.com
Result:
UNEXPECTED:
old.example.com
25. Don’t Treat Every Nginx Server Name as a Site
Nginx may contain:
default_server
localhost
internal services
monitoring endpoints
The reconciliation engine must identify the specific server block associated with the CHP site’s configuration.
Don’t compare the entire Nginx installation against one website.
26. PHP-FPM Version
Expected:
8.3
Actual from Nginx:
php8.3-fpm.sock
Result:
MATCH
If:
php8.2-fpm.sock
then:
DRIFT
27. PHP-FPM Runtime
Check:
systemctl is-active php8.3-fpm
Result:
PHP-FPM:
OK
If stopped:
PHP-FPM:
FAIL
28. PHP Socket
Check:
test -S /run/php/php8.3-fpm.sock
Possible:
MATCH
MISSING
If the service is active but the expected socket doesn’t exist:
PHP Socket:
MISSING
29. MySQL
For WordPress sites, verify the configured database connection.
Use:
wp db check
If successful:
Database:
OK
If not:
Database:
FAIL
Don’t alter the database.
30. SSL Check
Expected from CHP:
SSL:
ACTIVE
Actual:
certificate exists
certificate valid
certificate covers domain
Then:
MATCH
31. SSL Expiry
A certificate can exist but be close to expiration.
For example:
SSL:
ACTIVE
Expires:
2026-08-20
Days remaining:
7
Report:
WARNING
rather than:
MATCH
if your policy considers that important.
32. SSL Domain Coverage
Expected:
example.com
www.example.com
Certificate SANs:
example.com
www.example.com
Result:
MATCH
If the certificate only contains:
example.com
then:
DRIFT
for www.example.com.
33. DNS Check
Expected server IP:
SERVER_IPV4
Actual:
dig +short A example.com
If equal:
MATCH
If different:
DRIFT
34. Multiple DNS Addresses
Suppose:
example.com
returns:
203.0.113.10
203.0.113.11
but CHP expects only:
203.0.113.10
Don’t immediately say:
WRONG
because multiple A records may be intentional.
Use:
MULTIPLE
or:
DRIFT
depending on the configured hosting policy.
35. DNS Failure
If DNS cannot be queried:
DNS:
UNKNOWN
not:
DRIFT
This distinction is important for reliable monitoring.
36. Backup Check
CHP database says:
12 backups
Actual directory contains:
12
Result:
MATCH
If database says:
12
but disk contains:
8
then:
DRIFT
37. Backup Verification
A backup should ideally have:
DISCOVERED
VERIFIED
state.
Reconciliation can report:
Backups:
12 expected
Verified:
10
Unverified:
2
Overall:
WARNING
rather than automatically calling the site broken.
38. Overall Site Result
Use priority:
ERROR
↓
DRIFT
↓
MISSING
↓
UNEXPECTED
↓
WARNING
↓
MATCH
A site with:
DNS MATCH
SSL MATCH
Nginx MATCH
PHP DRIFT
should have:
OVERALL:
DRIFT
39. Example Output
CresignSys Reconciliation
=========================
Site:
example.com
Management:
MANAGED
Filesystem
----------
Web Root MATCH
Owner MATCH
Application MATCH
Nginx
-----
Service OK
Configuration MATCH
Domains MATCH
PHP
---
Version DRIFT
Expected 8.3
Actual 8.2
Service OK
MySQL
-----
Connection OK
SSL
---
Certificate MATCH
Domains MATCH
Expiry WARNING
DNS
---
A Record MATCH
Backups
-------
Inventory MATCH
Verification WARNING
Overall:
DRIFT
40. Don’t Repair Automatically
Even though CHP knows:
PHP expected 8.3
PHP actual 8.2
do not execute:
apt install php8.3
or change Nginx.
The command is only reporting the difference.
41. JSON Output
Add:
sudo hosting-reconcile example.com --json
Example:
{
"domain": "example.com",
"overall": "DRIFT",
"checks": {
"web_root": "MATCH",
"owner": "MATCH",
"nginx": "MATCH",
"php_version": "DRIFT",
"php_service": "OK",
"mysql": "OK",
"ssl": "MATCH",
"dns": "MATCH",
"backups": "WARNING"
}
}
This will later be extremely useful for the web control panel.
42. Why JSON Matters
The future dashboard can request:
GET /sites/example.com/status
and receive:
{
"overall": "DRIFT"
}
The dashboard doesn’t need to parse:
PHP:
DRIFT
from human-readable terminal output.
43. Exit Codes
Use meaningful exit codes.
For example:
0 = MATCH
1 = DRIFT
2 = ERROR
3 = UNKNOWN
Then:
hosting-reconcile example.com
echo $?
can be used by automation.
44. Monitoring
A future monitoring process could execute:
hosting-reconcile example.com
and understand:
0 → healthy
1 → investigate
2 → checker failure
without parsing text.
45. Operation Logging
Every reconciliation should create an operation:
operation:
RECONCILE
For example:
site_id:
7
status:
SUCCESS
message:
DRIFT_DETECTED
Be careful with terminology:
A reconciliation operation can execute successfully even if it discovers drift.
For example:
operation status = SUCCESS
reconciliation result = DRIFT
These are different concepts.
46. Important Distinction
Don’t write:
RECONCILE
FAILED
just because PHP differs.
The reconciliation process succeeded.
It discovered:
DRIFT
So:
Operation:
SUCCESS
Result:
DRIFT
is better.
47. Database Fields
Eventually operations should include:
result
For example:
ALTER TABLE operations
ADD COLUMN result TEXT;
Then:
operation = RECONCILE
status = SUCCESS
result = DRIFT
48. Reconciliation History
This lets you answer:
When did the site first become unhealthy?
For example:
Aug 13 14:00 MATCH
Aug 13 15:00 MATCH
Aug 13 16:00 DRIFT
Aug 13 17:00 DRIFT
This becomes valuable for troubleshooting.
49. Don’t Run Too Frequently Yet
For manual testing:
hosting-reconcile example.com
is enough.
Later, scheduled monitoring can run every:
15 minutes
30 minutes
1 hour
depending on the monitoring design.
For a first version, don’t build a daemon yet.
50. Reconciliation Library
Create:
sudo nano /etc/cresignsys/lib/reconcile.sh
Functions:
reconcile_web_root
reconcile_owner
reconcile_application
reconcile_nginx
reconcile_php
reconcile_mysql
reconcile_ssl
reconcile_dns
reconcile_backups
reconcile_site
51. Standard Function Result
Each function should conceptually return:
MATCH
DRIFT
MISSING
UNEXPECTED
UNKNOWN
ERROR
For example:
reconcile_web_root "$DOMAIN"
returns:
MATCH
or:
MISSING
52. Don’t Mix Display With Logic
Avoid writing:
reconcile_php() {
echo "PHP: DRIFT"
}
Instead:
result="$(reconcile_php "$DOMAIN")"
Then the presentation layer decides:
PHP:
DRIFT
This allows:
CLI
JSON
API
Web dashboard
to use the same underlying logic.
53. Internal Result Structure
A future function can return:
{
"check": "php_version",
"result": "DRIFT",
"expected": "8.3",
"actual": "8.2"
}
The first Bash implementation can use simpler variables.
54. Expected/Actual Is Essential
For every meaningful drift, show:
EXPECTED
ACTUAL
Example:
PHP Version
Expected:
8.3
Actual:
8.2
Result:
DRIFT
This is much more useful than:
PHP:
FAIL
55. Example DNS Drift
DNS A Record
Expected:
YOUR_SERVER_IP
Actual:
ANOTHER_IP
Result:
DRIFT
Don’t automatically modify DNS.
56. Example Ownership Drift
Filesystem Owner
Expected:
www-data:www-data
Actual:
root:root
Result:
DRIFT
Again, report only.
57. Example SSL Drift
SSL Certificate
Expected:
example.com
www.example.com
Actual:
example.com
Result:
DRIFT
This immediately tells the operator what changed.
58. Example Missing Nginx Configuration
Nginx Configuration
Expected:
Detected
Actual:
Not found
Result:
MISSING
59. Reconciliation Categories
A useful final report structure is:
FILESYSTEM
NGINX
PHP
DATABASE
WORDPRESS
SSL
DNS
BACKUPS
This gives the operator a predictable interface.
60. Full Reconciliation Flow
hosting-reconcile example.com
│
▼
Load CHP record
│
▼
Acquire site lock
│
▼
Read actual state
│
┌───────┼────────┐
▼ ▼ ▼
Files Nginx Services
│ │ │
└───────┼────────┘
▼
DNS / SSL
│
▼
Database
│
▼
WordPress
│
▼
Backups
│
▼
Calculate result
│
▼
Display / JSON
│
▼
Audit log
61. Site Lock
Use the lock created in Lesson 077:
/var/lock/cresignsys/example.com.lock
Why lock a read-only reconciliation?
Because another operation could be changing the site’s configuration simultaneously.
For example:
REPAIR
│
├── changing Nginx
│
└── changing PHP
RECONCILE
│
└── reading Nginx/PHP
Without a lock, reconciliation might capture a temporary intermediate state.
62. Don’t Hold the Lock Forever
The reconciliation operation should:
acquire
↓
collect
↓
release
rather than holding the lock during unrelated long-running operations.
63. Concurrency Example
Without locking:
19:00:00 REPAIR starts
19:00:01 Nginx config temporary
19:00:02 RECONCILE reads
19:00:03 REPAIR completes
Reconciliation could incorrectly report:
Nginx:
DRIFT
even though repair was still in progress.
Locking prevents this ambiguity.
64. Global vs Site Lock
Use:
site lock
for:
reconcile example.com
repair example.com
backup example.com
restore example.com
Use:
global lock
for:
database migration
CHP database restore
global platform configuration
65. Reconciliation Should Be Safe to Repeat
Running:
hosting-reconcile example.com
10 times should not change the website.
It only observes.
This is called idempotent read-only behavior.
66. Example Repeated Results
Run 1:
MATCH
Run 2:
MATCH
Run 3:
MATCH
No server modification occurs.
67. First Test
After creating the command:
sudo chmod +x /usr/local/bin/hosting-reconcile
Run:
sudo hosting-reconcile learn.cresignsys.com
68. Expected Healthy Result
For a healthy site:
CresignSys Reconciliation
==========================
Domain:
learn.cresignsys.com
Filesystem:
Web Root MATCH
Owner MATCH
Application MATCH
Nginx:
Service MATCH
Configuration MATCH
Domains MATCH
PHP:
Version MATCH
Service MATCH
Socket MATCH
Database:
Connection MATCH
WordPress:
Installation MATCH
SSL:
Certificate MATCH
Domains MATCH
Expiry MATCH
DNS:
A Record MATCH
Backups:
Inventory MATCH
Overall:
MATCH
69. If There Is Drift
Example:
PHP:
Version DRIFT
Expected 8.3
Actual 8.2
Overall:
DRIFT
The correct next step is not:
hosting-repair
automatically.
First investigate why the difference exists.
70. Why Manual Investigation Comes First
Possible reasons:
1. Administrator changed PHP manually
2. CHP database is outdated
3. Import detected the wrong PHP socket
4. Site intentionally uses another PHP version
5. Nginx configuration changed
The reconciliation engine identifies the difference.
It does not assume the cause.
71. Reconciliation vs Repair
Keep these commands separate:
hosting-reconcile
│
└── READ
hosting-repair
│
└── WRITE
This separation is one of the most important CHP design decisions.
72. Future Repair Architecture
Eventually:
RECONCILE
↓
DRIFT
↓
OPERATOR CONFIRMATION
↓
REPAIR PLAN
↓
VALIDATE PLAN
↓
APPLY
↓
VERIFY
↓
RECONCILE AGAIN
Not:
DRIFT
↓
AUTOMATIC CHANGE
at least not initially.
73. Repair Plan
A future command might produce:
CresignSys Repair Plan
======================
Site:
example.com
Detected:
PHP version drift
Expected:
8.3
Actual:
8.2
Proposed action:
Update site Nginx PHP-FPM configuration to 8.3
Risk:
MEDIUM
Changes:
1. Nginx configuration
2. Nginx reload
Rollback:
Available
Apply:
hosting-repair example.com
This is much safer.
74. The Long-Term CHP Loop
We are building toward:
DESIRED STATE
│
▼
RECONCILIATION
│
▼
ACTUAL STATE
│
▼
DRIFT?
/ \
NO YES
│ │
▼ ▼
HEALTHY REPAIR PLAN
│
▼
APPROVE
│
▼
APPLY
│
▼
VERIFY
│
▼
RECONCILIATION
This is the core operating model of CHP.
75. Lesson 081 — Core Principle
hosting-reconcile is the bridge between:
CHP DATABASE
and:
REAL SERVER
It should answer precisely:
What does CHP expect?
What actually exists?
Do they match?
If not, exactly where do they differ?
It should not change anything.
Next Lesson — 082
Build the CHP Health Engine
Reconciliation tells us about configuration drift.
Health monitoring answers a different question:
Is the website actually working right now?
We will build:
sudo hosting-health example.com
to test:
DNS
HTTP
HTTPS
TLS
Nginx
PHP-FPM
MySQL
WordPress
response time
HTTP status
redirects
disk space
memory
CPU/load
The architecture will become:
CHP
│
┌─────────┴─────────┐
▼ ▼
RECONCILIATION HEALTH
│ │
"Is it configured "Is it
correctly?" working?"
│ │
└─────────┬─────────┘
▼
SITE STATUS
This distinction will allow CHP to tell the difference between configuration drift and an actual service outage.
Leave a Reply