Set up shared hosting
Objective — bootstrap the per-server Admin-Server toolkit on a fresh hosting account so config drift detection, MD5 integrity checks, disk/inode alerts, PHP error-log scanning, domain inventory, log rotation, and alert channels (ops · deploys · backups) are defined once for every app on that account.
This is the account-scoped server-readiness playbook before CodeCanyon setup-new reaches deployment. Conceptual companion: Three-tier admin (handbook).
Once per account, then every app
flowchart LR A["New hosting account"] --> B["Server readiness · this playbook"] B --> C["setup-new · app 1"] B --> D["setup-new · app N"]Operating rules
Section titled “Operating rules”Every task in this phase follows four operating rules:
| Rule | Meaning |
|---|---|
| Runs on the server | SSH into ~/Admin-Server/ on the hosting account — zero toolkit binaries on your laptop |
| Secrets stay server-side | No webhook URL or SA token in laptop argv or agent chat — see Secrets discipline |
| Idempotent | Re-run safe: git pull-friendly; baselines non-destructive; cron lines detected before duplicate add |
| Secret source of truth | Webhooks and heartbeat URLs are created/stored first in the shared infrastructure 1Password vault recorded in Zaj-PROJECT.md (default example: General-Dev); runtime copies live only in gitignored server.env at chmod 600 |
Tasks at a glance
Section titled “Tasks at a glance”| # | Task | Priority | Type |
|---|---|---|---|
| 0.1 | Clone Admin-Server toolkit + initial repo setup | MUST | CLI (SSH) |
| 0.2 | Download op 1Password CLI (no root) | MUST after P9C-1 | CLI |
| 0.3 | Bootstrap config/server.env from template | MUST | 👤 on server (nano) |
| 0.4 | Make scripts executable + chmod 600 on server.env | MUST | CLI |
| 0.5 | Generate initial MD5 baseline | MUST | CLI |
| 0.6 | Run status.sh smoke test | MUST | CLI |
| 0.7 | Fire one synthetic alert through ops channel | MUST | CLI |
| 0.8 | Register Hostinger cron entries (6 lines, 5-field syntax) | MUST | API or 👤 hPanel UI |
| 0.9 | Discover server runtime paths | MUST | CLI |
| 0.10 | First commit + push to per-server private repo | MUST | CLI |
| 0.11 | Document server in CLAUDE.local.md | SHOULD | Manual |
Before you start
Section titled “Before you start”| Prerequisite | Verify |
|---|---|
| SSH public-key auth to the account | ssh <SSH_ALIAS> 'echo ok' — no password prompt |
Private GitHub repo Admin-Hostinger-<account> | Empty repo OK for first push |
| Discord or Slack ops channel | Ops webhook ready for #server-alerts or the project’s equivalent; configure only through the secret-safe flow below |
| Shared infrastructure 1Password vault | Record the actual vault name in Zaj-PROJECT.md; General-Dev is the default example, not a fixed requirement |
| Heartbeat provider decision | MonSpark, Healthchecks.io, Better Stack, Cronitor, UptimeRobot heartbeats, or N/A with reason |
Detect and skip
Section titled “Detect and skip”Run before any install work:
ssh <SSH_ALIAS> ' if [ -x ~/Admin-Server/scripts/status.sh ]; then echo "Admin-Server present — run Verify existing next" ~/Admin-Server/scripts/status.sh else ls ~/Admin-Server/scripts/*.sh 2>/dev/null | head -10 fi '| Output | Action |
|---|---|
status.sh or scripts listed | Jump to Verify existing — skip setup steps 0.1–0.5 only after liveness passes |
| No such file | Continue with the setup tasks |
Scripts but no lib/init.sh or no config/server.env | Legacy layout — run Migrate legacy installs to v2 before cron or app reuse |
Migrate legacy installs to v2
Section titled “Migrate legacy installs to v2”Run this branch when ~/Admin-Server/ exists but uses the older layout (config/webhooks.conf, missing config/server.env, missing lib/init.sh, or scripts that do not write the expected logs). Migration is not a pass by itself; after it, run Verify existing.
-
Normalize the config layout without deleting legacy files.
Terminal window ssh <SSH_ALIAS> 'bash -s' <<'REMOTE'set -euo pipefailcd ~/Admin-Servermkdir -p config logs baselinesif [ ! -f config/server.env ]; thencp config/server.env.example config/server.envchmod 600 config/server.envecho "Created config/server.env from example; edit it before alerting checks."fi[ -f config/webhooks.conf ] && echo "Legacy config/webhooks.conf kept as read-only fallback."test -d lib || echo "Missing lib/ — pull the Admin-Server v2 repo before continuing."git status --shortREMOTEExpected:
config/server.envexists atchmod 600; legacywebhooks.confis not deleted; missinglib/is treated as a blocker until the v2 repo is pulled. -
Complete the v2 gates. Edit
server.envwith the slot matrix in step 0.3, register absolute-path cron rows in step 0.8, regenerate the baseline, then run Verify existing. A migrated server does not become deploy-ready until the freshness probe has noSTALErows.
Verification-first flow: Clone → op CLI (P9C-1) → server.env → baseline → status + alert → cron liveness → runtime discovery → commit/push.
0.1 · Clone Admin-Server toolkit
Section titled “0.1 · Clone Admin-Server toolkit”-
Clone the per-server private repo into
~/Admin-Server/.Terminal window ssh <SSH_ALIAS> 'git clone git@github.com-<GITHUB_USER>:<GITHUB_USER>/Admin-Hostinger-<account>.git ~/Admin-Servercd ~/Admin-Server && git remote -v'# Expected: origin → your Admin-Hostinger-<account> repo -
If the directory already exists, update instead of re-cloning.
Terminal window ssh <SSH_ALIAS> 'cd ~/Admin-Server && git fetch && git pull'
0.2 · op CLI (deferred — P9C-1)
Section titled “0.2 · op CLI (deferred — P9C-1)”Step 0.2 (download op 1Password CLI, no root) is MUST after P9C-1, not part of the interim bootstrap. For forward compatibility only, add an empty placeholder when editing server.env in step 0.3:
OP_SERVICE_ACCOUNT_TOKEN=""Do not run op signin from an agent session.
0.3 · Bootstrap config/server.env
Section titled “0.3 · Bootstrap config/server.env”-
Create
server.envfrom the template on the server only.Terminal window ssh <SSH_ALIAS> 'cd ~/Admin-Servercp config/server.env.example config/server.envchmod 600 config/server.envnano config/server.env' -
Set identity and at least one ops webhook from the shared infrastructure vault (paste URL in
nanoon server — never into chat).Minimum alerting-ready fields:
SERVER_NAME,SERVER_LABEL,SERVER_ENV(staging|production), plusDISCORD_WEBHOOK_OPSorSLACK_WEBHOOK_OPS. The source copy lives in 1Password, usuallyop://General-Dev/...unlessZaj-PROJECT.mdnames a different shared infrastructure vault.
| Slot | Variables | Required? | Behavior when blank |
|---|---|---|---|
| Server identity | SERVER_NAME, SERVER_LABEL, SERVER_ENV | Required | status.sh cannot prove the account identity |
| Ops alerts | DISCORD_WEBHOOK_OPS, SLACK_WEBHOOK_OPS | Required for alerting-ready | Monitoring alerts log locally only; this playbook’s alert gate cannot pass |
| Deploy notifications | DISCORD_WEBHOOK_DEPLOYS, SLACK_WEBHOOK_DEPLOYS | Required when deploy notifications are enabled | Deployer Section J logs “notification skipped” and continues |
| Backup notifications | DISCORD_WEBHOOK_BACKUPS, SLACK_WEBHOOK_BACKUPS | Required when backup alerting is enabled | Backup alerts no-op or fall back to ops depending on the script |
| Legacy/default webhook | WEBHOOK_URL | Legacy fallback only | Current v2 scripts should prefer the channel-specific vars above |
| External heartbeat pings | HEARTBEAT_SNAPSHOT, HEARTBEAT_INTEGRITY, HEARTBEAT_RESOURCE, HEARTBEAT_ERRORLOG, HEARTBEAT_INVENTORY | Required when external heartbeat monitoring is claimed | Cron still runs locally, but MonSpark/Healthchecks/Better Stack/Cronitor/UptimeRobot will not page on missed runs |
| Future 1Password fetch | OP_SERVICE_ACCOUNT_TOKEN | Empty until service-account hardening | server.env remains the runtime copy; source values stay in the shared infra vault |
Wire alerting destinations
Section titled “Wire alerting destinations”Alert channels are part of the server setup, not a later nice-to-have. Use the channel names below as the default map, then record the actual workspace/channel names in Zaj-PROJECT.md.
If an agent is driving Discord, Slack, or MonSpark through the operator’s logged-in browser, use the vendor console automation guide: click by accessibility-tree element refs, not screenshot coordinates; let the human own login and secret-copy moments; move copied URLs through clipboard → stdin so webhook and heartbeat URLs never print in chat, terminal output, or process lists.
| Route | Common destination | Variables |
|---|---|---|
| Ops | #server-alerts | DISCORD_WEBHOOK_OPS or SLACK_WEBHOOK_OPS |
| Deploys | #deployments | DISCORD_WEBHOOK_DEPLOYS or SLACK_WEBHOOK_DEPLOYS |
| Backups | ops or backup channel | DISCORD_WEBHOOK_BACKUPS or SLACK_WEBHOOK_BACKUPS |
| App alerts | #app-alerts | App/Sentry/MonSpark routing, not Admin-Server deploy vars |
-
Create or reuse provider webhooks in the right workspace.
- Discord: right-click channel → Edit Channel → Integrations → Create Webhook → rename → Save → Copy URL.
- Slack:
api.slack.com/apps→ Incoming Webhooks → Add to Workspace → channel → Copy. - MonSpark heartbeats: Cron Job → Create Monitor → name/interval/grace → Copy heartbeat URL.
- If the operator already has the right channel and webhook, reuse it; do not create duplicates.
-
Store every URL in 1Password before it touches the server.
Shared infra vault: <actual-vault-name> # default example: General-Devop://<vault>/Admin-Server/SLACK_WEBHOOK_OPSop://<vault>/Admin-Server/DISCORD_WEBHOOK_OPSop://<vault>/Admin-Server/SLACK_WEBHOOK_DEPLOYSop://<vault>/Admin-Server/DISCORD_WEBHOOK_DEPLOYS -
Smoke-test endpoints without printing secrets.
Terminal window DISCORD_URL="$(op read 'op://<vault>/Admin-Server/DISCORD_WEBHOOK_OPS')"curl -fsS -o /dev/null -w '%{http_code}\n' "$DISCORD_URL"unset DISCORD_URL# Expected: 200 for Discord webhook GET metadataSLACK_URL="$(op read 'op://<vault>/Admin-Server/SLACK_WEBHOOK_OPS')"printf '{"text":"Admin-Server webhook smoke test"}' \| curl -fsS -X POST -H 'Content-type: application/json' --data @- "$SLACK_URL"unset SLACK_URL# Expected: okIf a provider does not support a harmless preflight for that URL, skip the endpoint probe and rely on the real
send_alert/alert-helpertest after wiring. -
Paste into
config/server.envthrough an interactive editor or secure clipboard bridge.Terminal window ssh <SSH_ALIAS> 'cd ~/Admin-Serverchmod 600 config/server.envnano config/server.env'Never echo webhook URLs into terminal logs, agent chat, README files, screenshots, or commits.
-
Verify with the shipped alert helper.
Terminal window ssh <SSH_ALIAS> 'cd ~/Admin-Serverif [ -x scripts/send_alert.sh ]; thenbash scripts/send_alert.sh ops INFO "Admin-Server alerting smoke test for $(hostname)"elsebash scripts/alert-helper.sh INFO "Admin-Server alerting smoke test for $(hostname)" opsfi'# Expected: exit 0 and the ops channel receives the message with the server env prefix.
0.4 · Executable scripts + server.env permissions
Section titled “0.4 · Executable scripts + server.env permissions”-
chmod scripts and confirm
server.envstays private.Terminal window ssh <SSH_ALIAS> 'chmod +x ~/Admin-Server/scripts/*.shchmod 600 ~/Admin-Server/config/server.envls -l ~/Admin-Server/scripts/status.sh ~/Admin-Server/config/server.env'# Expected: scripts -rwx; server.env -rw-------
0.5 · Generate initial MD5 baseline
Section titled “0.5 · Generate initial MD5 baseline”-
Generate and commit the integrity baseline.
Terminal window ssh <SSH_ALIAS> 'cd ~/Admin-Serverconf="config/monitored-paths.conf"touch "$conf"grep -Fxq "$HOME/.ssh/authorized_keys" "$conf" 2>/dev/null || printf "%s\n" "$HOME/.ssh/authorized_keys" >> "$conf"~/Admin-Server/scripts/generate-baseline.shgit add "$conf" baselines/checksums.md5git diff --cached --quiet || git commit -m "Initial integrity baseline"'# Expected: checksums.md5 with server dotfiles and ~/.ssh/authorized_keys
0.6 · Run status.sh smoke test
Section titled “0.6 · Run status.sh smoke test”-
Run the health snapshot — this is the verify step in the eight-step flow.
Terminal window ssh <SSH_ALIAS> '~/Admin-Server/scripts/status.sh'Expected: clean
SERVER_LABEL/SERVER_ENV; baseline ✅; webhook slots show ✅/❌ (at least one ✅ before you leave this playbook); cron schedule printed — use that output for the cron step.
0.7 · Fire one synthetic alert (ops channel)
Section titled “0.7 · Fire one synthetic alert (ops channel)”-
Send a test alert through the ops channel.
Terminal window ssh <SSH_ALIAS> '~/Admin-Server/scripts/alert-helper.sh INFO "Server-readiness smoke test" "Hello from $(hostname)" ops'Expected: Discord/Slack message with
[STAGING] 🟡or[PRODUCTION] 🔴prefix.
0.8 · Register six Hostinger cron entries
Section titled “0.8 · Register six Hostinger cron entries”Hostinger shared hosting often has no SSH crontab CLI. Use the Hostinger REST API when you have the account token; otherwise use hPanel → Advanced → Cron Jobs. Use SSH crontab only on hosts where command -v crontab succeeds.
| Cron job | Typical script | Watches |
|---|---|---|
| Config snapshot | snapshot-config.sh | Tracked config drift |
| MD5 integrity | integrity-check.sh | Baseline mutations |
| Disk / inode | resource-check.sh | Space and inode exhaustion |
| PHP error log | error-log-check.sh | New PHP errors across domains |
| Domain inventory | domain-inventory.sh | Apps/domains on account |
| Log rotation | rotate-logs.sh | Log volume |
-
Resolve the absolute Admin-Server path before creating any cron row.
Terminal window ssh <SSH_ALIAS> 'printf "ADMIN_SERVER=%s\n" "$HOME/Admin-Server"'# Expected: ADMIN_SERVER=/home/<user>/Admin-Server -
Copy cron expressions from
status.shoutput (step 0.6) — do not guess schedules. -
Create or verify the six cron rows with API GET-before-POST when available. Match the absolute command exactly.
Terminal window # Example row payload shape; repeat for each script from the table above.# Do not include redirects, pipes, or $HOME in command.command="bash /home/<user>/Admin-Server/scripts/snapshot-config.sh"If the API is not available, add the same absolute commands in hPanel and verify five-field syntax (
min hour dom mon dow). A UI preset is not enough; inspect the saved row. -
Confirm log files update after the first interval with the freshness probe.
Terminal window ssh <SSH_ALIAS> 'bash -s' <<'REMOTE'set -euo pipefailcd ~/Admin-Servernow=$(date +%s)check_log() {label="$1"; path="$2"; max_age="$3"if [ ! -f "$path" ]; thenecho "❌ STALE $label — missing $path"; exit 1fimtime=$(stat -c %Y "$path")age=$((now - mtime))if [ "$age" -gt "$max_age" ]; thenecho "❌ STALE $label — last run $(date -d "@$mtime" -Is), age ${age}s, max ${max_age}s"; exit 1fiecho "✅ fresh $label — last run $(date -d "@$mtime" -Is)"}check_any_log() {label="$1"; max_age="$2"; shift 2for path in "$@"; do[ -f "$path" ] && check_log "$label" "$path" "$max_age" && return 0doneecho "❌ STALE $label — missing expected log: $*"; exit 1}check_any_log "config snapshot" 172800 logs/snapshot-config.logcheck_any_log "integrity" 172800 logs/integrity.log logs/integrity-check.logcheck_any_log "disk/inode" 43200 logs/resource.log logs/resources.log logs/resource-check.logcheck_any_log "PHP error scan" 43200 logs/error-log.log logs/error-log-check.logcheck_any_log "domain inventory" 2592000 logs/domain-inventory.logcheck_any_log "log rotation" 1209600 logs/rotate-logs.logREMOTEExpected: every row prints
✅ fresh; any❌ STALErow means the cron is not alive enough for app deploys.
External heartbeat wiring (MonSpark, Healthchecks, Better Stack, Cronitor, UptimeRobot)
Section titled “External heartbeat wiring (MonSpark, Healthchecks, Better Stack, Cronitor, UptimeRobot)”External heartbeat monitors are layered on top of the local cron/log freshness gate. They are useful, but they are not a substitute for fresh Admin-Server logs. If the operator already pays for MonSpark, Better Stack, Cronitor, or UptimeRobot heartbeat monitors, use that tool here instead of signing up for a new Healthchecks.io account.
| Script | Heartbeat slot | Provider examples |
|---|---|---|
snapshot-config.sh | HEARTBEAT_SNAPSHOT | MonSpark Cron Job · Healthchecks ping · Better Stack/Cronitor/UptimeRobot heartbeat |
integrity-check.sh | HEARTBEAT_INTEGRITY | MonSpark Cron Job · Healthchecks ping · Better Stack/Cronitor/UptimeRobot heartbeat |
resource-check.sh | HEARTBEAT_RESOURCE | MonSpark Cron Job · Healthchecks ping · Better Stack/Cronitor/UptimeRobot heartbeat |
error-log-check.sh | HEARTBEAT_ERRORLOG | MonSpark Cron Job · Healthchecks ping · Better Stack/Cronitor/UptimeRobot heartbeat |
domain-inventory.sh | HEARTBEAT_INVENTORY | MonSpark Cron Job · Healthchecks ping · Better Stack/Cronitor/UptimeRobot heartbeat |
If a slot is blank, that script should still run locally but will not ping the external heartbeat provider. If you claim external heartbeat monitoring is configured, paste the heartbeat URLs into config/server.env, run the matching script once, and confirm the provider dashboard receives the ping. Leave blank slots explicitly documented as N/A — no external heartbeat configured.
For MonSpark specifically, create one Cron Job monitor per Admin-Server script and paste its URL into the matching HEARTBEAT_* slot. The MonSpark monitoring guide covers the full provider setup and optional Laravel MONSPARK_CRON_URL scheduler heartbeat.
0.9 · Discover server runtime paths
Section titled “0.9 · Discover server runtime paths”-
Record the server runtime paths the app deploys will consume.
Terminal window ssh <SSH_ALIAS> 'bash -s' <<'REMOTE'set -euo pipefailcd ~/Admin-Server{echo "# Generated by setup-shared-hosting; update when hPanel PHP/Node changes."echo "PHP_LOGIN_SHELL=$(command -v php || true)"for phpbin in /opt/alt/php84/usr/bin/php /opt/alt/php83/usr/bin/php /opt/alt/php82/usr/bin/php /usr/local/bin/php /usr/bin/php; do[ -x "$phpbin" ] && printf 'PHP_CANDIDATE=%s # %s\n' "$phpbin" "$("$phpbin" -r 'echo PHP_VERSION;' 2>/dev/null || true)"doneecho "COMPOSER_BIN=$(command -v composer || true)"echo "NODE_BIN=$(command -v node || true)"echo "NPM_BIN=$(command -v npm || true)"} > config/server-environment.confcat config/server-environment.confgit add config/server-environment.confgit diff --cached --quiet || git commit -m "Record server environment paths"REMOTEExpected:
config/server-environment.confis committed and shows the login-shell PHP separately from versioned PHP candidates. Later deploy pages still confirm the domain’s selected web/CLI PHP before pinning Composer.
0.10 · First commit + push
Section titled “0.10 · First commit + push”-
Push the Admin-Server repo (baseline + monitored paths + committed config such as
server-environment.conf).Terminal window ssh <SSH_ALIAS> 'cd ~/Admin-Servergit statusgit push'
0.11 · Document the server (SHOULD)
Section titled “0.11 · Document the server (SHOULD)”Record in the project’s CLAUDE.local.md (or equivalent local-only notes): SSH alias, Admin-Hostinger-<account> repo name, last baseline date, last webhook rotation — not in git-tracked secrets.
Verify existing
Section titled “Verify existing”When detect-and-skip found scripts already installed:
ssh <SSH_ALIAS> 'bash -s' <<'REMOTE'set -euo pipefailcd ~/Admin-Server./scripts/status.shnow=$(date +%s)
check_log() { label="$1"; path="$2"; max_age="$3" if [ ! -f "$path" ]; then echo "❌ STALE $label — missing $path"; exit 1; fi mtime=$(stat -c %Y "$path") age=$((now - mtime)) if [ "$age" -gt "$max_age" ]; then echo "❌ STALE $label — last run $(date -d "@$mtime" -Is), age ${age}s, max ${max_age}s"; exit 1 fi echo "✅ fresh $label — last run $(date -d "@$mtime" -Is)"}
check_any_log() { label="$1"; max_age="$2"; shift 2 for path in "$@"; do [ -f "$path" ] && check_log "$label" "$path" "$max_age" && return 0 done echo "❌ STALE $label — missing expected log: $*"; exit 1}
check_any_log "config snapshot" 172800 logs/snapshot-config.logcheck_any_log "integrity" 172800 logs/integrity.log logs/integrity-check.logcheck_any_log "disk/inode" 43200 logs/resource.log logs/resources.log logs/resource-check.logcheck_any_log "PHP error scan" 43200 logs/error-log.log logs/error-log-check.logcheck_any_log "domain inventory" 2592000 logs/domain-inventory.logcheck_any_log "log rotation" 1209600 logs/rotate-logs.logREMOTEFix only failed checks (missing baseline → 0.5; stale logs → 0.8; unset webhooks → 0.3 + 0.7). Then return to your app playbook.
If this verification is happening for a specific app domain, add that domain path to the integrity baseline before you return:
APP_DOMAIN="<domain.example.com>"ssh <SSH_ALIAS> "APP_DOMAIN='$APP_DOMAIN' bash -s" <<'REMOTE'set -euo pipefailAPP_DOMAIN="${APP_DOMAIN:?set APP_DOMAIN before running}"cd ~/Admin-Serverpath="$HOME/domains/$APP_DOMAIN"conf="config/monitored-paths.conf"touch "$conf"for monitored in "$HOME/.ssh/authorized_keys" "$path"; do grep -Fxq "$monitored" "$conf" 2>/dev/null || printf '%s\n' "$monitored" >> "$conf" grep -Fx "$monitored" "$conf"done./scripts/generate-baseline.shgit add "$conf" baselines/checksums.md5git diff --cached --quiet || git commit -m "Monitor $APP_DOMAIN domain path"REMOTE# Expected: monitored-paths.conf contains authorized_keys + the domain path; baseline regeneratedIdempotent re-run
Section titled “Idempotent re-run”| Concern | On second run |
|---|---|
| Repo | git fetch && git pull — no re-clone |
| Cron | Detected before add — no duplicates |
| Baseline | Regeneration non-destructive |
| Health | status.sh + the log freshness probe |
Secrets discipline
Section titled “Secrets discipline”- Webhook URLs are credentials — live only in 1Password and gitignored
server.env; never in README files, examples, commits, screenshots, or chat. - A committed webhook URL is compromised. Delete/recreate the webhook, then update 1Password and
server.env; editing nearby config is not recovery. - Before committing Admin-Server docs/config examples, run
rg -n 'discord\.com/api/webhooks/[0-9]+/[A-Za-z0-9._-]+' .and confirm there are no tracked Discord webhook URLs. server.env:chmod 600, gitignored — interim SoT forops/deploys/backups.- Prefer server-to-server copy or clipboard bridge at keyboard — never through agent chat.
- P9C-1 (future): 1Password SA fetch replaces plaintext URLs — see Hardening.
Hardening server.env
Section titled “Hardening server.env”| Weakness | Interim | After P9C-1 |
|---|---|---|
| Plaintext webhooks on disk | chmod 600 + gitignore | SA fetch at runtime |
| No audit trail | Rotation log in monitoring state | 1Password activity log |
| Per-server manual rotation | Operator checklist | Rotate vault once; servers pull fresh |
Later hardening: move Discord/Slack webhook URLs out of plaintext server.env into a scoped read-only 1Password service-account fetch that fails closed. This is post-launch Admin-Server ops work, not a setup-new Phase 9 launch gate.
P9C-1 unblocks after the CodeCanyon payments setup proves op on Hostinger CloudLinux.
Checklist
Section titled “Checklist”Verification checklist:
- SSH alias works passwordlessly from laptop
- Server GitHub auth:
ssh -T git@github.com-<GITHUB_USER>succeeds -
~/Admin-Server/present;git remote -vcorrect - All monitoring scripts executable (
-rwx) -
config/server.envexists atchmod 600, gitignored - Six cron entries installed through Hostinger API/hPanel or SSH
crontabwhen available; each row usesbash /home/<user>/Admin-Server/scripts/<script>.shwith no$HOME, pipes, or redirects - Admin-Server logs are fresh within each cron interval ×2; no
STALErows -
baselines/checksums.md5exists with ≥4 entries - Test alert received with
[STAGING] 🟡or[PRODUCTION] 🔴prefix - Adopted external heartbeat slots show fresh provider pings, or each blank slot is recorded as
N/Awith reason -
config/monitored-paths.confincludes~/.ssh/authorized_keysand this app’s domain path before reuse -
config/server-environment.confcommitted with PHP/Node/Composer path candidates - Server documented in
CLAUDE.local.md(step 0.11)
What’s next
Section titled “What’s next”Maintainer note
Section titled “Maintainer note”Source archives and intake ledgers stay out of reader-facing playbooks. The workflow above is the published server-readiness path.
When the gate passes, continue with CodeCanyon setup-new — Phase 1 workspace setup on your laptop.