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
Leave a Reply