passwdFile = base_path('docker/mosquitto/config/passwd'); $this->reloadFile = base_path('docker/mosquitto/config/reload'); } /** Provision (or rotate) a credential for $username. Returns the plaintext password (show once). */ public function provision(string $username): string { $password = Str::random(20); $this->writeUser($username, $username.':'.$this->hash($password)); $this->triggerReload(); return $password; } private function hash(string $password): string { $salt = random_bytes(12); $derived = hash_pbkdf2('sha512', $password, $salt, self::ITERATIONS, 64, true); return '$7$'.self::ITERATIONS.'$'.base64_encode($salt).'$'.base64_encode($derived); } private function writeUser(string $username, string $line): void { $lines = is_file($this->passwdFile) ? file($this->passwdFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : []; $lines = array_values(array_filter($lines, fn ($l) => ! str_starts_with($l, $username.':'))); $lines[] = $line; file_put_contents($this->passwdFile, implode("\n", $lines)."\n", LOCK_EX); @chmod($this->passwdFile, 0644); // world-readable so the mosquitto user can read it } private function triggerReload(): void { file_put_contents($this->reloadFile, (string) now()->getTimestampMs()); } }