CresignSys Learn — Lesson 077

Written by

in

Build the Shared CHP Core Library

We have reached an important point.

The platform currently has many commands:

hosting-create
hosting-list
hosting-info
hosting-health
hosting-site-status
hosting-repair
hosting-ssl
hosting-backup
hosting-restore
hosting-suspend
hosting-unsuspend
hosting-domain-add
hosting-domain-list
hosting-domain-remove
hosting-dns-check

If each script contains its own:

domain validation
logging
locking
error handling
Nginx handling
PHP handling
MySQL handling

the system will eventually become difficult to maintain.

So now we create the CresignSys Hosting Platform Core.


1. New Architecture

Instead of:

hosting-create
   └── 500 lines

hosting-health
   └── 500 lines

hosting-repair
   └── 500 lines

we want:

                    CHP CORE
                       │
       ┌───────────────┼────────────────┐
       ▼               ▼                ▼
    Commands        Libraries        Templates
       │               │                │
       ▼               ▼                ▼
 hosting-*       common functions    Nginx/PHP

The commands become thin interfaces.


2. Final Directory Structure

Create:

/etc/cresignsys/
├── hosting.conf
├── lib/
│   ├── common.sh
│   ├── domain.sh
│   ├── dns.sh
│   ├── nginx.sh
│   ├── php.sh
│   ├── mysql.sh
│   ├── wordpress.sh
│   ├── ssl.sh
│   ├── backup.sh
│   ├── logging.sh
│   └── lock.sh
│
├── templates/
│   ├── nginx/
│   ├── php/
│   └── suspended/
│
└── sites/

Site state:

/var/lib/cresignsys/
└── sites/
    └── example.com/
        ├── site.conf
        ├── domains.conf
        └── state

Logs:

/var/log/cresignsys/
├── operations.log
├── errors.log
└── audit.log

3. Create the Structure

Run:

sudo mkdir -p /etc/cresignsys/lib
sudo mkdir -p /etc/cresignsys/templates/nginx
sudo mkdir -p /etc/cresignsys/templates/php
sudo mkdir -p /etc/cresignsys/templates/suspended

sudo mkdir -p /var/lib/cresignsys/sites
sudo mkdir -p /var/log/cresignsys
sudo mkdir -p /var/lock/cresignsys

4. Central Configuration

Create:

sudo nano /etc/cresignsys/hosting.conf

Example:

CHP_ROOT="/etc/cresignsys"

SITE_STATE_ROOT="/var/lib/cresignsys/sites"

LOG_ROOT="/var/log/cresignsys"

LOCK_ROOT="/var/lock/cresignsys"

WEB_ROOT="/storage/websites"

BACKUP_ROOT="/backup/cresignsys"

SERVER_IPV4="YOUR_SERVER_IPV4"

SERVER_IPV6=""

DEFAULT_PHP_VERSION="8.3"

BACKUP_DAILY_RETENTION=7
BACKUP_WEEKLY_RETENTION=4
BACKUP_MONTHLY_RETENTION=3

Replace:

YOUR_SERVER_IPV4

with the actual server IP.


5. Configuration Permissions

Because this file contains infrastructure settings:

sudo chown root:root /etc/cresignsys/hosting.conf
sudo chmod 640 /etc/cresignsys/hosting.conf

Don’t make the configuration world-writable.


6. common.sh

Create:

sudo nano /etc/cresignsys/lib/common.sh

This becomes the foundation for every command.

It should contain:

require_root
command_exists
die
log_info
log_warn
log_error
timestamp

7. Root Check

Function:

require_root() {
    if [[ "$EUID" -ne 0 ]]; then
        echo "ERROR: This command must be run as root."
        exit 1
    fi
}

Now every command can simply call:

require_root

8. Command Existence

Add:

command_exists() {
    command -v "$1" >/dev/null 2>&1
}

Usage:

if ! command_exists nginx; then
    die "nginx command not found"
fi

9. die()

die() {
    echo "ERROR: $*" >&2
    exit 1
}

Then:

[[ -d "$WEB_ROOT" ]] || die "Web root does not exist"

10. Timestamp

timestamp() {
    date '+%Y-%m-%d %H:%M:%S'
}

This gives consistent timestamps throughout the platform.


11. Logging Library

Create:

sudo nano /etc/cresignsys/lib/logging.sh

Use:

operations.log
errors.log
audit.log

12. Operation Log

Example:

2026-08-13 19:20:00 INFO hosting-create example.com

Function:

log_info() {
    printf '%s INFO %s\n' "$(timestamp)" "$*" \
        >> "${LOG_ROOT}/operations.log"
}

13. Warning Log

log_warn() {
    printf '%s WARN %s\n' "$(timestamp)" "$*" \
        >> "${LOG_ROOT}/operations.log"
}

14. Error Log

log_error() {
    printf '%s ERROR %s\n' "$(timestamp)" "$*" \
        >> "${LOG_ROOT}/errors.log"
}

15. Audit Log

Audit logs should record actions that change customer state.

Example:

2026-08-13 19:30:00
operator=root
action=SUSPEND
domain=example.com

Function:

audit_log() {
    printf '%s AUDIT %s\n' "$(timestamp)" "$*" \
        >> "${LOG_ROOT}/audit.log"
}

16. Protect Logs

Run:

sudo chown -R root:root /var/log/cresignsys
sudo chmod 750 /var/log/cresignsys

Individual logs can be:

sudo chmod 640 /var/log/cresignsys/*.log

17. Domain Library

Create:

sudo nano /etc/cresignsys/lib/domain.sh

This should contain all domain-related functions.


18. Normalize Domain

normalize_domain() {
    printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
}

Example:

Example.COM

becomes:

example.com

19. Domain Validation

The function:

validate_domain()

should reject:

https://example.com
example.com/path
example.com:443

and accept:

example.com
www.example.com
shop.example.com

Keep this validation centralized.


20. Why Central Validation Matters

Previously:

hosting-domain-add

might validate one way while:

hosting-dns-check

validates another way.

Now both use:

validate_domain()

Therefore behavior stays consistent.


21. Site Existence

Add:

site_exists() {
    [[ -f "${SITE_STATE_ROOT}/$1/site.conf" ]]
}

Then:

if ! site_exists "$DOMAIN"; then
    die "Site does not exist: $DOMAIN"
fi

22. DNS Library

Create:

sudo nano /etc/cresignsys/lib/dns.sh

Functions:

get_a_records
get_aaaa_records
get_cname
get_nameservers
get_ttl
check_dns

23. get_a_records

Conceptually:

get_a_records() {
    dig +short A "$1"
}

Now every command uses the same DNS mechanism.


24. get_cname

get_cname() {
    dig +short CNAME "$1"
}

25. get_nameservers

get_nameservers() {
    dig +short NS "$1"
}

26. Nginx Library

Create:

sudo nano /etc/cresignsys/lib/nginx.sh

Functions:

nginx_is_running
nginx_config_test
reload_nginx
site_nginx_config_exists

27. Nginx Running Check

nginx_is_running() {
    systemctl is-active --quiet nginx
}

Usage:

if nginx_is_running; then
    echo "Nginx OK"
fi

28. Nginx Configuration Test

nginx_config_test() {
    nginx -t >/dev/null 2>&1
}

This becomes reusable everywhere.


29. Reload Nginx

reload_nginx() {
    systemctl reload nginx
}

Then:

nginx_config_test || die "Nginx configuration invalid"

reload_nginx

30. PHP Library

Create:

sudo nano /etc/cresignsys/lib/php.sh

Functions:

php_fpm_service
php_fpm_running
php_socket_exists

31. PHP-FPM Service

php_fpm_service() {
    printf 'php%s-fpm' "$1"
}

For:

8.3

returns:

php8.3-fpm

32. PHP-FPM Check

php_fpm_running() {
    local version="$1"
    systemctl is-active --quiet "$(php_fpm_service "$version")"
}

33. MySQL Library

Create:

sudo nano /etc/cresignsys/lib/mysql.sh

Functions:

mysql_running
database_exists

34. MySQL Running

mysql_running() {
    systemctl is-active --quiet mysql
}

35. Database Existence

A first implementation can check:

mysql -NBe "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA ..."

But avoid constructing SQL with unvalidated user input.

Use the internally stored database name after validating it.


36. WordPress Library

Create:

sudo nano /etc/cresignsys/lib/wordpress.sh

Functions:

wordpress_installed
wordpress_db_check
wordpress_version

37. WordPress Installed

wordpress_installed() {
    local root="$1"
    wp --path="$root" core is-installed >/dev/null 2>&1
}

38. SSL Library

Create:

sudo nano /etc/cresignsys/lib/ssl.sh

Functions:

certificate_exists
certificate_expiry
certificate_matches_domain

This prevents SSL logic being duplicated between:

hosting-ssl
hosting-health
hosting-site-status

39. Backup Library

Create:

sudo nano /etc/cresignsys/lib/backup.sh

Functions:

backup_exists
latest_backup
backup_status
backup_age
backup_is_valid

40. Lock Library

Create:

sudo nano /etc/cresignsys/lib/lock.sh

This is particularly important.


41. Why Locking Is Needed

Imagine:

17:00 hosting-backup example.com
17:01 hosting-restore example.com

Both modify:

/storage/websites/example.com

Without locking:

BACKUP
      │
      ├── reading files
      │
      └───────┐
              │
RESTORE      │
      │      │
      └──────┘

This can create inconsistent results.


42. Site Lock

Lock path:

/var/lock/cresignsys/example.com.lock

The operation acquires:

LOCK

before modifying the site.


43. Use flock

Linux already provides a reliable locking mechanism.

Conceptually:

exec 200>"${LOCK_ROOT}/${DOMAIN}.lock"
flock -n 200

If the lock cannot be acquired:

ERROR:
Another operation is already running.

44. Don’t Invent Locking With Temporary Files

Avoid:

if [ -f lock ]; then ...
touch lock

because two processes can race between:

check

and:

create

flock is designed for this problem.


45. Common Library Loading

Every command can start with:

source /etc/cresignsys/hosting.conf
source /etc/cresignsys/lib/common.sh
source /etc/cresignsys/lib/logging.sh
source /etc/cresignsys/lib/lock.sh

Then add only the libraries it needs.

For example:

hosting-dns-check
 ↓
common.sh
dns.sh
logging.sh

46. hosting-site-status

Now becomes much simpler:

load config
load libraries
validate domain
load site state

check DNS
check Nginx
check HTTP
check HTTPS
check SSL
check PHP
check database
check WordPress
check files
check backup

calculate overall status
display result

The complexity moves into reusable functions.


47. Error Handling

Use:

set -Eeuo pipefail

but be careful.

A health check is intentionally expected to encounter failures.

For example:

if php_fpm_running "$PHP_VERSION"; then
    PHP_STATUS="OK"
else
    PHP_STATUS="FAIL"
fi

Do not let set -e terminate the entire diagnostic before collecting all results.


48. Diagnostic Commands vs Action Commands

This distinction is important.

Action

hosting-repair

A failure may justify stopping immediately.

Diagnostic

hosting-site-status

A failure should usually be recorded and the next check should continue.


49. Example Diagnostic Flow

DNS       FAIL
Nginx     OK
PHP       OK
Database  FAIL
WordPress FAIL
Backup    OK

The user receives the full picture.


50. State Library

Add another library:

/etc/cresignsys/lib/state.sh

Functions:

get_service_state
set_service_state
get_health_state
set_health_state

51. State Transitions

Don’t allow arbitrary changes.

For example:

ACTIVE → SUSPENDING

is valid.

But:

DELETED → ACTIVE

should not happen through normal operations.


52. Centralize State Transitions

Instead of:

echo "ACTIVE" > state

in every script, use:

set_service_state "$DOMAIN" "ACTIVE"

This gives you one place to enforce rules and logging.


53. State File

A simple initial state file:

SERVICE_STATE=ACTIVE
HEALTH_STATE=HEALTHY

But don’t let arbitrary users modify it.

Use:

root:root

ownership.


54. Better Future Design

Eventually move state to a database:

sites
domains
services
backups
operations

But a structured filesystem state model is acceptable for the first CHP implementation.


55. Operation Library

Create:

/etc/cresignsys/lib/operation.sh

It can provide:

operation_start
operation_success
operation_failure

Example:

START
 ↓
LOCK
 ↓
ACTION
 ↓
VERIFY
 ↓
SUCCESS

56. Standard Operation Pattern

Every modifying command should follow:

VALIDATE
   ↓
LOCK
   ↓
PRECHECK
   ↓
CHANGE
   ↓
VERIFY
   ↓
STATE UPDATE
   ↓
AUDIT

This becomes the standard CHP operational pattern.


57. Example: hosting-suspend

VALIDATE DOMAIN
       ↓
LOCK SITE
       ↓
CHECK ACTIVE
       ↓
SET SUSPENDING
       ↓
GENERATE NGINX
       ↓
nginx -t
       ↓
RELOAD
       ↓
HTTP CHECK
       ↓
SET SUSPENDED
       ↓
AUDIT

58. Example: hosting-restore

VALIDATE BACKUP
       ↓
LOCK SITE
       ↓
PRE-RESTORE BACKUP
       ↓
RESTORE
       ↓
VERIFY
       ↓
HEALTH CHECK
       ↓
STATE UPDATE
       ↓
AUDIT

59. Example: hosting-domain-add

VALIDATE DOMAIN
       ↓
CHECK OWNERSHIP/POLICY
       ↓
LOCK SITE
       ↓
REGISTER DOMAIN
       ↓
GENERATE NGINX
       ↓
nginx -t
       ↓
RELOAD
       ↓
UPDATE SSL STATE
       ↓
AUDIT

60. Templates

Now create:

/etc/cresignsys/templates/nginx/

Eventually:

active.conf.tpl
suspended.conf.tpl
redirect.conf.tpl

61. Active Template

Conceptually:

server {
    listen 80;
    server_name {{DOMAINS}};

    root {{WEB_ROOT}};
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }
}

The actual template should be generated by your CHP template engine or carefully rendered using safe substitutions.


62. Suspended Template

Conceptually:

server {
    listen 80;
    server_name {{DOMAINS}};

    return 503;
}

The HTTPS server block should similarly preserve the site’s TLS configuration while serving the suspension response.


63. Redirect Template

server {
    listen 80;
    server_name {{DOMAIN}};

    return 301 https://{{TARGET}}$request_uri;
}

Again, HTTPS handling must be generated consistently with the site’s certificate configuration.


64. Template Principle

Never make the generated configuration depend on:

what happened last time

Instead:

CURRENT METADATA
      ↓
CURRENT DESIRED STATE
      ↓
GENERATE COMPLETE CONFIG

This makes repair much easier.


65. Configuration Generation

The ideal architecture becomes:

site.conf
domains.conf
service state
PHP version
SSL state
       │
       ▼
   CONFIG ENGINE
       │
       ▼
Nginx configuration

66. Validate Before Activation

Never directly overwrite the active configuration.

Use:

generate
   ↓
temporary file
   ↓
nginx -t
   ↓
activate

For example:

example.com.conf.new

then after successful validation:

example.com.conf

67. Atomic Configuration Replacement

The activation step should ideally be atomic:

OLD CONFIG
     │
     ▼
NEW CONFIG VALIDATED
     │
     ▼
atomic replacement

This avoids leaving partially written configuration files.


68. Backup the Configuration

Before a major configuration change:

example.com.conf

can be copied to:

example.com.conf.previous

This gives another recovery mechanism.


69. Don’t Accumulate .previous Files Forever

Configuration backups should have a retention policy too.

For example:

current
previous

may be sufficient for fast rollback.

Historical configuration versions should instead be stored in the audit/versioning system if needed.


70. File Ownership

Infrastructure configuration should normally be:

root:root

and not writable by:

www-data

This prevents a compromised website from modifying its own Nginx configuration.


71. Security Boundary

The architecture should therefore look like:

                   ROOT
                    │
            ┌───────┴────────┐
            ▼                ▼
       CHP Engine        Infrastructure
            │                │
            ▼                ▼
        Nginx/PHP          SSL
            │
            ▼
         WEBSITE
            │
            ▼
        www-data

The website user should not control the hosting engine.


72. Command Permissions

The /usr/local/bin/hosting-* commands should normally require root.

For example:

sudo hosting-site-status example.com

The future dashboard/API should also run through a controlled privileged service rather than granting unrestricted shell access.


73. Don’t Give the Web Server sudo

Avoid configurations such as:

www-data ALL=(ALL) NOPASSWD: ALL

That would effectively turn a website compromise into server compromise.

Use a narrowly scoped privileged backend later.


74. CHP Architecture Now

We now have:

                 CRESIGNSYS HOSTING
                         │
             ┌───────────┴───────────┐
             ▼                       ▼
         COMMANDS                    CORE
             │                       │
             │             ┌─────────┼─────────┐
             │             ▼         ▼         ▼
             │          DOMAIN      DNS      NGINX
             │             │         │         │
             │             ▼         ▼         ▼
             │           SSL       PHP       MYSQL
             │                       │         │
             │                       └────┬────┘
             │                            ▼
             │                        WORDPRESS
             │
             ├── backup
             ├── restore
             ├── suspend
             └── repair

75. The Commands Are Now Orchestrators

For example:

hosting-repair

should not contain its own implementation of:

PHP detection
Nginx reload
logging
locking
state

Instead:

hosting-repair
      ↓
core libraries
      ↓
perform repair
      ↓
verify

76. This Makes Future Development Faster

Once nginx.sh contains:

reload_nginx()

you can use it from:

hosting-create
hosting-domain-add
hosting-domain-remove
hosting-suspend
hosting-unsuspend
hosting-repair
hosting-ssl

without rewriting the logic.


77. Standard Result Format

Eventually every core function should return:

SUCCESS
WARNING
FAILURE

and optionally:

code
message
details

This makes JSON output easier later.


78. Example Internal Result

Conceptually:

status=FAIL
code=DNS_WRONG
message="Domain points to unexpected IP"

The CLI can display:

DNS:
WRONG

while the API can return:

{
  "status": "fail",
  "code": "DNS_WRONG",
  "message": "Domain points to unexpected IP"
}

79. Avoid Parsing Human Output

Don’t make:

hosting-site-status

parse:

[OK] DNS

from another command.

Instead both use:

check_dns()

This is one of the most important improvements in this lesson.


80. CHP Core Principles

From now on, every feature should follow these rules:

1. Validate input
2. Load centralized configuration
3. Acquire appropriate lock
4. Use shared libraries
5. Generate desired configuration
6. Validate before activation
7. Verify the result
8. Update state
9. Write audit log

81. Current Project Structure

The target structure is now:

/etc/cresignsys/
│
├── hosting.conf
│
├── lib/
│   ├── common.sh
│   ├── logging.sh
│   ├── lock.sh
│   ├── state.sh
│   ├── operation.sh
│   ├── domain.sh
│   ├── dns.sh
│   ├── nginx.sh
│   ├── php.sh
│   ├── mysql.sh
│   ├── wordpress.sh
│   ├── ssl.sh
│   └── backup.sh
│
└── templates/
    ├── nginx/
    ├── php/
    └── suspended/

82. Operational Data

/var/lib/cresignsys/
└── sites/
    └── example.com/
        ├── site.conf
        ├── domains.conf
        └── state

83. Logs

/var/log/cresignsys/
├── operations.log
├── errors.log
└── audit.log

84. Website Data

Your existing website architecture remains:

/storage/websites/
└── example.com/
    └── public/

This separation is important:

CHP metadata
      ≠
customer website

85. Backup Data

/backup/cresignsys/
└── example.com/
    ├── 2026-08-13_160000/
    ├── 2026-08-12_160000/
    └── ...

Again:

backup
≠
live website

86. The Four Major Data Layers

The platform now has four clearly separated layers:

1. CONFIGURATION
   /etc/cresignsys

2. STATE
   /var/lib/cresignsys

3. LIVE WEBSITE
   /storage/websites

4. BACKUP
   /backup/cresignsys

This separation is foundational.


87. Why This Is Important

A restore should modify:

LIVE WEBSITE

and selected application data.

It should not blindly overwrite:

CHP CORE

A configuration repair should modify:

CHP-generated infrastructure configuration

not customer content.

A backup prune should modify:

BACKUP

not live website data.


88. Lesson 077 — Core Principle

We have now moved from:

A collection of hosting Bash scripts

toward:

A structured hosting platform engine.

The architecture is:

                CHP CORE
                   │
       ┌───────────┼───────────┐
       ▼           ▼           ▼
    Commands     Libraries   Templates
       │           │           │
       └───────────┼───────────┘
                   ▼
              SITE STATE
                   │
          ┌────────┼────────┐
          ▼        ▼        ▼
        Nginx     PHP      MySQL
          │        │        │
          └────────┼────────┘
                   ▼
                Website

Next Lesson — 078

Build the CHP Site Database

The filesystem-based model is useful for the initial platform, but we are reaching the point where a database will make management much cleaner.

We will design:

sites
domains
site_services
backups
operations
ssl_certificates

with relationships such as:

                    sites
                      │
          ┌───────────┼────────────┐
          ▼           ▼            ▼
       domains     backups      services
          │
          ▼
        SSL

Then commands such as:

hosting-list
hosting-info
hosting-domain-list
hosting-backup-list
hosting-site-status

can query structured site metadata rather than scanning directories.

The next major architectural transition will therefore be:

FILESYSTEM STATE ↓ CENTRAL SITE DATABASE ↓ CHP CORE ↓ CLI + FUTURE WEB CONTROL PANEL

Comments

Leave a Reply

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