Build the CHP Approval & Authorization Engine
The repair engine is now capable of changing a website, but it must not decide by itself whether a repair is authorized.
We therefore add:
REPAIR PLAN
↓
VALIDATION
↓
AUTHORIZATION
↓
APPROVAL
↓
REPAIR EXECUTION
This creates a clear security boundary between proposing a change and allowing the change.
1. Why Approval Is Separate
Consider:
PHP:
8.2
Expected:
8.3
CHP can safely determine:
DRIFT
and generate:
RP-000021
But it should not assume:
DRIFT = permission to modify
The operator may have intentionally configured PHP 8.2.
Therefore:
Detection
≠
Authorization
2. Approval Architecture
DRIFT
│
▼
REPAIR PLAN
│
▼
VALIDATION
│
▼
RISK ANALYSIS
│
▼
APPROVAL REQUIRED
│
┌───────┴───────┐
▼ ▼
APPROVE REJECT
│ │
▼ ▼
APPROVED CANCELLED
│
▼
hosting-repair
3. New Command
Create:
sudo nano /usr/local/bin/hosting-repair-approve
Usage:
sudo hosting-repair-approve RP-000021
Reject:
sudo hosting-repair-reject RP-000021
For now, approval should be an explicit action.
4. Approval Requirements
Before approval, verify:
Plan exists
Plan is VALID
Site is MANAGED
Plan is not expired
Plan fingerprint is current
Risk policy permits approval
Operator is authorized
Only then:
VALID
↓
APPROVED
5. Approval Database
Create:
CREATE TABLE repair_approvals (
id INTEGER PRIMARY KEY AUTOINCREMENT,
plan_id INTEGER NOT NULL,
operator_id INTEGER,
decision TEXT NOT NULL,
comment TEXT,
approved_at TEXT NOT NULL,
FOREIGN KEY (plan_id)
REFERENCES repair_plans(id)
ON DELETE CASCADE
);
6. Decision Values
Use:
APPROVED
REJECTED
Later you can add:
DEFERRED
EXPIRED
but don’t need them initially.
7. Why Keep Rejected Approvals?
Suppose:
RP-000021
was rejected.
Don’t simply delete it.
Record:
Plan:
RP-000021
Decision:
REJECTED
Operator:
admin
Comment:
Keep PHP 8.2 for compatibility.
Time:
2026-08-13 17:20
This creates an audit trail.
8. Approval History
A plan can potentially have multiple approval events:
17:00
REJECTED
17:30
new plan created
18:00
APPROVED
Don’t overwrite history.
Each decision should be recorded.
9. But Only One Active Decision
For execution, CHP should determine the latest valid decision.
For example:
APPROVED
followed by:
REJECTED
means the plan must no longer be executable.
The latest decision wins, subject to plan state.
10. Approval State
The repair plan itself can contain:
approval_status
For example:
PENDING
APPROVED
REJECTED
However, keep the detailed history in:
repair_approvals
11. Update repair_plans
Add:
ALTER TABLE repair_plans
ADD COLUMN approval_status TEXT NOT NULL DEFAULT 'PENDING';
Then:
DRAFT
VALID
can have:
approval_status:
PENDING
After approval:
approval_status:
APPROVED
12. Don’t Confuse Plan Status and Approval Status
These are different.
Example:
Plan status:
VALID
Approval:
PENDING
means:
The plan is technically valid but nobody has authorized it yet.
Another:
Plan status:
APPROVED
could mean the approval has been accepted and execution may proceed.
A cleaner model is:
plan status:
VALID
approval status:
APPROVED
Then the execution engine checks both.
13. Authorization vs Approval
These are also different.
Authorization
Is this operator allowed to approve this type of repair?
Approval
Did this operator actually approve this specific repair?
Therefore:
Authorization
↓
Can this user approve?
↓
Approval
↓
Did this user approve?
14. Operator Roles
Create a simple role model.
Start with:
VIEWER
OPERATOR
ADMIN
15. Viewer
Can:
view sites
view health
view reconciliation
view repair plans
view operation history
Cannot:
approve
execute
rollback
16. Operator
Can:
view
create repair plans
approve LOW/MEDIUM repairs
execute approved repairs
view operations
depending on the site’s permissions.
17. Admin
Can:
everything
including:
HIGH-risk approval
management-state changes
user authorization
platform settings
Critical operations should still require stronger controls.
18. Risk-Based Approval
Use:
LOW
MEDIUM
HIGH
CRITICAL
and map them to approval requirements.
Example:
| Risk | Minimum approval |
|---|---|
| LOW | Operator |
| MEDIUM | Operator |
| HIGH | Admin |
| CRITICAL | Manual / restricted |
19. PHP Repair
Our current PHP configuration repair:
Risk:
MEDIUM
Therefore:
OPERATOR
may approve it.
20. DNS Repair
A future DNS change:
Risk:
HIGH
requires:
ADMIN
This prevents an ordinary operator from accidentally redirecting a production domain.
21. Database Repair
A database modification:
Risk:
CRITICAL
should not be automatically approved through the basic operator workflow.
Require:
manual administrator authorization
and eventually possibly:
two-person approval
22. Approval Command
For the first CLI implementation:
sudo hosting-repair-approve RP-000021
The command should:
load plan
↓
verify plan
↓
verify operator
↓
verify risk
↓
revalidate fingerprint
↓
record approval
↓
update plan
23. Revalidate Before Approval
Suppose the plan was created at:
16:00
and approval happens at:
17:00
The server may have changed.
Therefore:
hosting-repair-approve
must perform another reconciliation.
If state changed:
PLAN STALE
and approval must stop.
24. Example
Plan:
RP-000021
PHP:
8.2 → 8.3
Current server:
PHP:
8.2
Approval:
VALID
But if the server now has:
PHP:
8.4
then:
Approval:
BLOCKED
Reason:
Plan is stale.
25. Why Approval Must Recheck
Otherwise:
Plan created
↓
Server changes
↓
Old plan approved
↓
Old plan executed
could overwrite a newer administrator decision.
26. Approval Expiration
Approved plans should also have an expiration.
Example:
Approved:
17:00
Expires:
19:00
If execution happens after:
19:00
then:
APPROVAL EXPIRED
Generate a fresh plan.
27. Approval TTL
Add:
approval_expires_at
to the plan.
ALTER TABLE repair_plans
ADD COLUMN approval_expires_at TEXT;
28. Why Approval Expiry?
Consider:
Monday:
approve PHP change
but the operator doesn’t execute it until:
Friday
The server may have changed dramatically.
An old approval should not remain valid indefinitely.
29. Approval Comment
Require a comment for:
HIGH
CRITICAL
Example:
Reason:
Approved after confirming application compatibility.
For LOW-risk operations, a comment can be optional.
30. Approval Example
CresignSys Repair Approval
==========================
Plan:
RP-000021
Site:
example.com
Repair:
PHP 8.2 → 8.3
Risk:
MEDIUM
Plan:
VALID
Fingerprint:
CURRENT
Operator:
admin
Decision:
APPROVED
Expires:
2026-08-13 19:00
31. Rejection Example
CresignSys Repair Approval
==========================
Plan:
RP-000021
Decision:
REJECTED
Reason:
Application currently depends on PHP 8.2.
No changes have been made.
Plan becomes:
CANCELLED
or:
REJECTED
depending on your state model.
32. Better State Model
Use:
DRAFT
VALID
APPROVED
APPLYING
VERIFIED
ROLLED_BACK
FAILED
REJECTED
EXPIRED
Then:
VALID
↓
APPROVED
or:
VALID
↓
REJECTED
33. Plan Lifecycle
DRAFT
│
▼
VALID
│
├─────────────┐
▼ ▼
APPROVED REJECTED
│
▼
APPLYING
│
┌┴─────────────┐
▼ ▼
VERIFIED ROLLED_BACK
│
▼
COMPLETED
34. Plan Expiration
A plan can move from:
VALID
to:
EXPIRED
if it is not approved within the configured time.
An approved plan can also expire if:
approval_expires_at
has passed.
35. Don’t Reuse Expired Plans
If:
RP-000021
expires:
hosting-repair RP-000021
must reject it.
Correct process:
new reconcile
↓
new plan
↓
new approval
36. CLI Identity
For the first local implementation, you may use:
id -un
to identify the operating-system user.
Example:
operator:
root
But this is only temporary.
A production web control panel needs its own authenticated identity.
37. Future User Model
Eventually:
users
might contain:
id
username
email
role
status
created_at
and:
site_permissions
could determine which sites an operator can manage.
38. Site-Level Authorization
An operator might be allowed to manage:
example.com
shop.example.com
but only view:
customer-site.com
Therefore authorization needs two dimensions:
WHO
+
WHICH SITE
39. Example
Operator:
John
Role:
OPERATOR
Permissions:
example.com MANAGE
shop.example.com MANAGE
client.com VIEW
John can approve:
example.com
but not:
client.com
40. Future Permission Table
CREATE TABLE site_permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
site_id INTEGER NOT NULL,
permission TEXT NOT NULL,
FOREIGN KEY (user_id)
REFERENCES users(id),
FOREIGN KEY (site_id)
REFERENCES sites(id)
);
Possible permissions:
VIEW
MANAGE
APPROVE
ADMIN
41. Don’t Use Only Global Roles
Avoid:
ADMIN = can modify every site
OPERATOR = can modify every site
as the only authorization mechanism.
Hosting platforms often need:
global role
+
site permission
42. Approval Check
Conceptually:
operator
↓
global role
↓
site permission
↓
risk permission
↓
approval allowed?
All four must pass.
43. Approval Decision
For:
RP-000021
the engine should determine:
Operator:
admin
Role:
OPERATOR
Site:
example.com
Permission:
MANAGE
Risk:
MEDIUM
Can approve:
YES
Then:
APPROVED
44. Unauthorized Example
Operator:
viewer
Role:
VIEWER
Site:
example.com
Risk:
MEDIUM
Can approve:
NO
Output:
Approval denied.
Operator does not have permission to approve this repair.
No state change.
45. Audit Everything
Every approval attempt should be logged.
Successful:
APPROVAL:
APPROVED
Denied:
APPROVAL:
DENIED
Even rejected authorization attempts can be valuable security information.
46. Audit Event
Eventually create:
CREATE TABLE audit_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_type TEXT NOT NULL,
actor_id INTEGER,
site_id INTEGER,
plan_id INTEGER,
result TEXT NOT NULL,
message TEXT,
created_at TEXT NOT NULL
);
Examples:
PLAN_CREATED
PLAN_VALIDATED
APPROVAL_REQUESTED
APPROVAL_GRANTED
APPROVAL_DENIED
REPAIR_STARTED
REPAIR_COMPLETED
REPAIR_ROLLED_BACK
47. Approval and Repair Are Different Actors
This is useful even if you initially use one operator.
Future model:
Operator A:
creates plan
Admin B:
approves
Repair engine:
executes
This provides separation of duties.
48. Two-Person Approval
For high-risk operations:
Operator A:
proposes
Admin B:
approves
Admin C:
second approval
Then:
HIGH-RISK REPAIR
│
▼
APPROVAL #1
│
▼
APPROVAL #2
│
▼
APPLY
Do not implement this yet, but design the database so it remains possible.
49. Multiple Approvals
The existing:
repair_approvals
table supports multiple records:
plan_id | operator | decision
--------|----------|---------
21 | 5 | APPROVED
21 | 7 | APPROVED
Then policy determines whether enough approvals exist.
50. Approval Policy
Eventually:
risk = LOW
required approvals = 1
risk = MEDIUM
required approvals = 1
risk = HIGH
required approvals = 2
risk = CRITICAL
manual intervention
This should be configuration, not hard-coded everywhere.
51. Approval Policy Table
Eventually:
CREATE TABLE approval_policies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
risk_level TEXT NOT NULL,
required_approvals INTEGER NOT NULL,
minimum_role TEXT NOT NULL
);
Example:
LOW 1 OPERATOR
MEDIUM 1 OPERATOR
HIGH 2 ADMIN
CRITICAL manual
52. Avoid Hard-Coded Permission Logic
Don’t scatter:
if [[ "$RISK" == "HIGH" ]]; then ...
through dozens of scripts.
Use a central function:
check_repair_authorization
and eventually:
get_approval_policy
53. Authorization Library
Create:
sudo nano /etc/cresignsys/lib/authorization.sh
Functions:
get_current_operator
get_operator_role
check_site_permission
get_risk_policy
can_create_plan
can_approve_plan
can_execute_plan
54. Approval Function
Conceptually:
can_approve_plan() {
local operator="$1"
local plan_id="$2"
# verify identity
# verify site permission
# verify role
# verify risk
# verify plan state
# verify approval policy
}
Return:
ALLOW
DENY
55. Approval Is Not Execution Permission
This distinction is important.
An operator may be allowed to:
APPROVE
but the system may require:
REPAIR EXECUTOR
to be a restricted service account.
The repair engine itself should run with controlled privileges.
56. Don’t Give the Web UI Root Shell Access
Eventually the web dashboard should not execute:
sudo bash
or arbitrary commands.
Instead:
Web UI
↓
CHP API
↓
Authorization
↓
Structured repair request
↓
Repair engine
57. Never Accept Shell Commands From Browser
Bad:
{
"command": "systemctl restart nginx"
}
Good:
{
"plan_id": "RP-000021"
}
The backend loads the approved plan.
58. Approval API
Eventually:
POST /api/repair-plans/RP-000021/approve
The server determines:
current user
plan
risk
site
authorization
The browser never tells the server:
who is allowed
The server determines that itself.
59. CSRF and Session Security
When the web interface is introduced, approval operations require:
authenticated session
CSRF protection
authorization check
audit logging
These are not optional for write operations.
60. CLI Safety
For local CLI use, require explicit command:
sudo hosting-repair-approve RP-000021
Do not make:
hosting-repair-plan
automatically approve anything.
61. No Approval Through Environment Variable
Avoid:
CHP_APPROVED=true hosting-repair ...
as a production authorization mechanism.
Approval must exist in the authoritative CHP state.
62. No --force
Avoid:
hosting-repair RP-000021 --force
in the initial system.
A force option tends to become a bypass around safety checks.
If an exceptional override is eventually needed, make it:
explicit
audited
role-restricted
time-limited
63. Approval Expiration Check
Before execution:
current_time < approval_expires_at
If false:
APPROVAL EXPIRED
Then:
REPAIR BLOCKED
64. Approval Fingerprint
Store the fingerprint when approval occurs:
approved_fingerprint
Then the repair engine checks:
current_fingerprint
==
approved_fingerprint
If not:
APPROVAL INVALIDATED
This provides another safety layer.
65. Add to Database
ALTER TABLE repair_plans
ADD COLUMN approved_fingerprint TEXT;
When approval occurs:
approved_fingerprint = current_fingerprint
66. Full Approval Validation
At approval:
Plan:
VALID
Site:
MANAGED
Operator:
AUTHORIZED
Risk:
ALLOWED
Fingerprint:
CURRENT
Approval:
RECORDED
At execution:
Plan:
APPROVED
Approval:
NOT EXPIRED
Fingerprint:
MATCHES
Site:
MANAGED
Reconciliation:
EXPECTED
Proceed.
67. Example End-to-End
16:00
DRIFT detected
16:02
RP-000021 created
16:03
Plan validated
16:05
Operator reviews plan
16:06
Operator approves
16:06
Approval fingerprint recorded
16:07
Repair starts
16:07
Current state rechecked
16:08
Backup created
16:08
Configuration changed
16:08
Nginx validated
16:08
Nginx reloaded
16:09
Health check passed
16:09
Reconciliation passed
16:09
Plan VERIFIED
68. If Something Changes
Suppose:
16:05
Approved
16:06
Administrator manually changes PHP
16:07
Repair starts
The repair engine detects:
fingerprint mismatch
and stops:
REPAIR BLOCKED
PLAN STALE
This prevents CHP from overwriting the administrator’s new configuration.
69. Approval Is a Snapshot
Think of approval as:
"I authorize this exact proposed change to this exact site state."
Not:
"I authorize whatever this plan becomes later."
This is why fingerprints are essential.
70. Lesson 086 — Core Principle
The CHP authorization model is:
WHO
+
WHAT
+
WHICH SITE
+
WHICH RISK
+
WHICH PLAN
+
WHICH STATE
+
WHEN
must all be validated before a repair is executed.
The final flow becomes:
DRIFT
↓
REPAIR PLAN
↓
VALIDATE
↓
AUTHORIZE
↓
APPROVE
↓
RECHECK STATE
↓
BACKUP
↓
REPAIR
↓
VERIFY
This gives CHP a proper separation between observation, planning, authorization, execution, and verification.
Next Lesson — 087
Build the CHP Backup & Restore Engine
The repair engine now depends on a reliable rollback mechanism.
We will build:
sudo hosting-backup example.com
and:
sudo hosting-restore example.com BACKUP-ID
covering:
Website files
MySQL database
Nginx configuration
SSL metadata
Backup manifest
SHA-256 verification
Retention
Backup integrity
Restore validation
The key architecture will be:
BACKUP
│
┌─────────┴─────────┐
▼ ▼
FILES DATABASE
│ │
└─────────┬─────────┘
▼
MANIFEST
│
▼
CHECKSUMS
│
▼
VERIFIED BACKUP
│
▼
RESTORE
│
▼
VALIDATE + HEALTH
This backup layer will become the foundation not only for customer backups, but also for safe CHP repairs and automatic rollback.
Leave a Reply