chore(config): documented env placeholders + SSH keys from file paths
- .env.example gains a full, commented CluPilot operations block (Stripe, Hetzner
DNS, Traefik, WireGuard hub, SSH identity, monitoring, prod mail) with a note
that everything is optional for local dev
- config/provisioning.php: CLUPILOT_SSH_{PUBLIC,PRIVATE}_KEY_PATH read the key
from a file (a multi-line PEM cannot live in .env), falling back to inline vars
- documents that Uptime Kuma's REST API is read-only (monitor CRUD is Socket.IO),
so it needs a bridge rather than being drop-in for the generic REST client
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
parent
32fa1028cc
commit
f06f00c428
69
.env.example
69
.env.example
|
|
@ -71,10 +71,77 @@ VITE_APP_NAME=CluPilot
|
|||
# when developing against a remote host; localhost for local-only.
|
||||
VITE_HMR_HOST=localhost
|
||||
|
||||
# ── Mail (dev → log) ─────────────────────────────────────────────────────
|
||||
# ── Mail ─────────────────────────────────────────────────────────────────
|
||||
# DEV: `log` writes mails to storage/logs (nothing is sent).
|
||||
# PROD: switch to smtp and fill in the credentials below.
|
||||
MAIL_MAILER=log
|
||||
MAIL_FROM_ADDRESS=hello@clupilot.local
|
||||
MAIL_FROM_NAME=CluPilot
|
||||
# MAIL_MAILER=smtp
|
||||
# MAIL_HOST=smtp.your-provider.tld
|
||||
# MAIL_PORT=587
|
||||
# MAIL_USERNAME=
|
||||
# MAIL_PASSWORD=
|
||||
# MAIL_SCHEME=tls # tls | smtps (465)
|
||||
|
||||
# ═════════════════════════════════════════════════════════════════════════
|
||||
# CluPilot operations — fill these in to run real provisioning.
|
||||
# Everything below is OPTIONAL for local dev: left blank, the services are
|
||||
# no-ops/fakes and the app + test suite still run. Only real infrastructure
|
||||
# work (host onboarding, customer provisioning) needs them.
|
||||
# ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── Stripe (payments) ────────────────────────────────────────────────────
|
||||
# WEBHOOK_SECRET is READ BY CODE today (signature check on /webhooks/stripe).
|
||||
# Outside local/testing a missing secret makes the webhook fail closed.
|
||||
STRIPE_WEBHOOK_SECRET=
|
||||
# Not read yet — needed once the Checkout flow is built (currently the portal
|
||||
# only records a purchase intent):
|
||||
STRIPE_KEY=
|
||||
STRIPE_SECRET=
|
||||
|
||||
# ── DNS (Hetzner DNS API) ────────────────────────────────────────────────
|
||||
# Creates <subdomain>.<zone> A-records for each customer instance.
|
||||
HETZNER_DNS_TOKEN=
|
||||
CLUPILOT_DNS_ZONE=clupilot.com
|
||||
|
||||
# ── Traefik (reverse proxy + TLS) ────────────────────────────────────────
|
||||
# Directory ON THE PROXMOX HOST where CluPilot writes dynamic route files.
|
||||
TRAEFIK_DYNAMIC_PATH=/etc/traefik/dynamic
|
||||
|
||||
# ── WireGuard hub (this CluPilot VM) ─────────────────────────────────────
|
||||
# Hosts join this hub during onboarding; CluPilot reaches them over the tunnel.
|
||||
CLUPILOT_WG_SUBNET=10.66.0.0/24
|
||||
CLUPILOT_WG_HUB_IP=10.66.0.1
|
||||
CLUPILOT_WG_ENDPOINT= # public host:port peers dial, e.g. vpn.clupilot.com:51820
|
||||
CLUPILOT_WG_HUB_PUBKEY= # `wg pubkey < /etc/wireguard/privatekey`
|
||||
CLUPILOT_WG_CONFIG_PATH=/etc/wireguard/wg0.conf
|
||||
|
||||
# ── SSH identity for host onboarding ─────────────────────────────────────
|
||||
# Deployed to each fresh server after the one-time root password login.
|
||||
# PREFERRED: point at files (a multi-line PEM cannot live in .env).
|
||||
# ssh-keygen -t ed25519 -N '' -C clupilot -f storage/app/ssh/clupilot
|
||||
CLUPILOT_SSH_PUBLIC_KEY_PATH=
|
||||
CLUPILOT_SSH_PRIVATE_KEY_PATH=
|
||||
# Alternative (single-line values only):
|
||||
CLUPILOT_SSH_PUBLIC_KEY=
|
||||
CLUPILOT_SSH_PRIVATE_KEY=
|
||||
CLUPILOT_SSH_COMMAND_TIMEOUT=2000
|
||||
|
||||
# ── Monitoring ───────────────────────────────────────────────────────────
|
||||
# The built-in client speaks a GENERIC REST API:
|
||||
# GET/POST /monitors, GET/DELETE /monitors/{id}
|
||||
# Uptime Kuma's own REST API is READ-ONLY (badges/status page/push) — creating
|
||||
# monitors goes through Socket.IO, so Kuma needs a small REST bridge in front
|
||||
# of it (or a dedicated client). Leave blank to disable monitoring entirely:
|
||||
# provisioning then records a stable breadcrumb and its own health checks
|
||||
# (occ status, TLS, admin user) still gate acceptance.
|
||||
MONITORING_API_URL=
|
||||
MONITORING_API_TOKEN=
|
||||
|
||||
# ── Nextcloud blueprint template ─────────────────────────────────────────
|
||||
# NOT an env var: set the Proxmox template VMID per plan in
|
||||
# config/provisioning.php → plans.*.template_vmid (default 9000).
|
||||
|
||||
# ── docker-compose knobs (host-side) ─────────────────────────────────────
|
||||
HOST_UID=1000
|
||||
|
|
|
|||
|
|
@ -8,6 +8,21 @@ $wgSubnet = env('CLUPILOT_WG_SUBNET', '10.66.0.0/24');
|
|||
$wgBase = ip2long($wgNetwork) & (0xFFFFFFFF << (32 - (int) $wgBits)) & 0xFFFFFFFF;
|
||||
$wgHubDefault = long2ip($wgBase + 1); // first usable address in the CIDR
|
||||
|
||||
/**
|
||||
* Read a secret from a FILE path if given, else from the inline env var.
|
||||
* SSH keys are multi-line PEM, which a plain .env cannot hold — pointing at a
|
||||
* file keeps the key out of .env entirely (and off the environment).
|
||||
* Note: after `config:cache`, changing the file needs `php artisan config:clear`.
|
||||
*/
|
||||
$secretFrom = function (string $pathVar, string $inlineVar): string {
|
||||
$path = (string) env($pathVar, '');
|
||||
if ($path !== '' && is_readable($path)) {
|
||||
return (string) file_get_contents($path);
|
||||
}
|
||||
|
||||
return (string) env($inlineVar, '');
|
||||
};
|
||||
|
||||
return [
|
||||
/*
|
||||
| Ordered step classes per pipeline. ::class is textual — listing steps that
|
||||
|
|
@ -104,8 +119,9 @@ return [
|
|||
|
||||
// SSH identity CluPilot deploys to each host after first password login.
|
||||
'ssh' => [
|
||||
'public_key' => env('CLUPILOT_SSH_PUBLIC_KEY', ''),
|
||||
'private_key' => env('CLUPILOT_SSH_PRIVATE_KEY', ''),
|
||||
// Prefer *_PATH (a file on disk); fall back to the inline env value.
|
||||
'public_key' => $secretFrom('CLUPILOT_SSH_PUBLIC_KEY_PATH', 'CLUPILOT_SSH_PUBLIC_KEY'),
|
||||
'private_key' => $secretFrom('CLUPILOT_SSH_PRIVATE_KEY_PATH', 'CLUPILOT_SSH_PRIVATE_KEY'),
|
||||
// Per-command SSH timeout; below the job timeout (2100s) but far above
|
||||
// the phpseclib ~10s default so apt full-upgrade etc. can finish.
|
||||
'command_timeout' => (int) env('CLUPILOT_SSH_COMMAND_TIMEOUT', 2000),
|
||||
|
|
|
|||
Loading…
Reference in New Issue