CresignSys Learn — Lesson 081

Written by

in

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.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *