The three-tier admin model
Where your ops files live
Running a CodeCanyon app is not just code — it’s also ops: deploy logs, backups, monitoring scripts, secret webhook URLs. The three-tier admin model answers one question for all of it: which of these files belongs to the project, which belongs to one domain, and which is shared across the whole server?
Get this map right once and you never again wonder where a deploy log or a Discord webhook should live.
The three tiers
Section titled “The three tiers”graph TD subgraph Laptop["Your laptop (project repo)"] Local["Admin-Local/<br/>guides · vault · customizations registry"] end
subgraph Server["The hosting account (one server)"] Domain["Admin-Domain/<br/>per-domain runtime: deploy logs, backups, monitors"] ServerWide["Admin-Server/<br/>account-wide: shared scripts, webhooks, baselines"] end
Local -->|deploys to| Domain ServerWide -->|provides defaults to| DomainThe diagram shows the flow: you author in Admin-Local on your laptop, deploy to a per-domain space on the server, and the server-wide tier supplies shared defaults to every domain.
| Tier | Lives at | Scope | In git? | Examples |
|---|---|---|---|---|
| Admin-Local | Project repo: Admin-Local/ | This one project | Yes — except 2-Vault/ (gitignored secrets) | Phase guides, ProjectCard, vault, customizations registry |
| Admin-Domain | Server: ~/domains/<domain>/Admin-Domain/ | One domain (e.g. staging.x.com, x.com) | No — runtime state | Deploy history, per-domain backups, monitor scripts, tmp |
| Admin-Server | Server: ~/Admin-Server/ | The whole hosting account | Yes — its own repo | Monitoring scripts, shared webhooks, MD5 baselines, server environment |
Why three tiers and not one
Section titled “Why three tiers and not one”The natural instinct is to put everything in the project’s .env. That breaks the moment one hosting account runs several Laravel apps (a common shared-hosting setup).
graph TD Account["One hosting account"] Account --> P1["Project A"] Account --> P2["Project B"] Account --> P3["Project C"]
Shared["Admin-Server<br/>shared webhook + scripts"] Shared -.->|one URL, all inherit| P1 Shared -.->|one URL, all inherit| P2 Shared -.->|one URL, all inherit| P3- Why not just
.envper project? If a Discord webhook lives in every project’s.env, rotating it means editing N projects. Put it inAdmin-Serveronce and every project inherits the new URL. - Why not put everything in
Admin-Server? Per-domain runtime state — deploy logs, monitor crons specific to one domain — belongs to that domain, not to a shared account-wide repo. That’s whatAdmin-Domainis for.
The git question — why one tier is tracked and one isn’t
Section titled “The git question — why one tier is tracked and one isn’t”The cleanest way to remember which tier goes in git: authoring state is tracked, runtime state is not.
- Admin-Local is in git because it’s authoring state — the guides and vault you write before you deploy.
- Admin-Domain is never in git because it’s runtime state — logs and timestamps that change every minute while the app runs. Runtime state in git is just noise: it bloats the repo and breaks reproducible deploys.
- Admin-Server has its own repo because the shared scripts are authored too, but they belong to the account, not to any single project.
How shared settings resolve — the cascade
Section titled “How shared settings resolve — the cascade”When a task needs a shared setting (like a webhook URL for deploy notifications), it doesn’t guess — it checks each tier in order and uses the first one it finds:
graph TD Need["Task needs a webhook URL"] Need --> S1{"Set in Admin-Server's<br/>server.env?"} S1 -->|Yes| Use1["Use it (server-wide default)"] S1 -->|No| S2{"Set in legacy<br/>webhooks.conf?"} S2 -->|Yes| Use2["Use it (legacy compatibility)"] S2 -->|No| S3{"Set in project<br/>shared/.env?"} S3 -->|Yes| Use3["Use it (project fallback)"] S3 -->|No| Skip["Skip the task with a<br/>logged note — never block the deploy"]The order matters: the Admin-Server v2 server.env value wins first because deploy notifications are server-tier operations shared by every project on the hosting account. Legacy webhooks.conf keeps older boxes working, and project shared/.env is a last-resort fallback when the server tier is not configured. If nothing is set, the task no-ops with a “skipped” log line rather than failing the deploy.
Canonical deploy webhook names in server.env are SLACK_WEBHOOK_DEPLOYS and DISCORD_WEBHOOK_DEPLOYS. Older Admin-Server installs may still contain webhooks.conf or the legacy names SLACK_DEPLOYS_WEBHOOK / DISCORD_DEPLOYS_WEBHOOK; current templates read those only as compatibility fallbacks while new projects write server.env.
The same v2 file also owns the server-wide alert posture:
| Channel | Canonical slots | Blank means |
|---|---|---|
| Ops alerts | DISCORD_WEBHOOK_OPS, SLACK_WEBHOOK_OPS | Monitoring alerts log locally only; the server is not alerting-ready until a synthetic ops alert arrives |
| Deploy notifications | DISCORD_WEBHOOK_DEPLOYS, SLACK_WEBHOOK_DEPLOYS | Deployer logs “notification skipped” and keeps deploying |
| Backup notifications | DISCORD_WEBHOOK_BACKUPS, SLACK_WEBHOOK_BACKUPS | Backup alert path no-ops or falls back to ops depending on the script |
| External heartbeat pings | HEARTBEAT_SNAPSHOT, HEARTBEAT_INTEGRITY, HEARTBEAT_RESOURCE, HEARTBEAT_ERRORLOG, HEARTBEAT_INVENTORY | Local cron still runs, but MonSpark/Healthchecks/Better Stack/Cronitor/UptimeRobot will not page on missed runs |
That distinction matters: an installed ~/Admin-Server/ folder is not proof of alerting. The shared-hosting gate must show status.sh exits 0, fresh cron-backed logs, monitored paths registered, and at least one successful synthetic ops alert before app deploy pages treat Admin-Server as live. Webhook URLs and heartbeat URLs are secrets: source values live in the shared infrastructure 1Password vault, runtime copies live only in gitignored server.env, and any committed webhook URL is compromised and must be deleted/recreated.
One naming rule
Section titled “One naming rule”The convention is the Admin- prefix everywhere — Admin-Local, Admin-Domain, Admin-Server. The inverted form Domain-Admin/ is legacy from an older convention; if you see it in an old guide or on a server, it should be migrated to Admin-Domain/.
When to use which tier
Section titled “When to use which tier”| Putting a file somewhere? Ask… | Then it goes in |
|---|---|
| Is it a guide, the vault, or a per-project record? | Admin-Local |
| Is it a log, backup, or monitor for one domain? | Admin-Domain |
| Is it a script, webhook, or baseline shared by all domains? | Admin-Server |