<?php

namespace App\Services\Ssh;

use phpseclib3\Crypt\EC;

/**
 * The SSH identity this installation reaches its hosts with, minted in PHP.
 *
 * Same reasoning as App\Services\Wireguard\Keypair, one layer up: the console
 * runs as www-data inside a container, shelling out to `ssh-keygen` would need
 * a binary and a writable path that only exist by accident, and a key written
 * to a temp file is a private key on a disk somebody has to remember to shred.
 * Generated here, the private half goes straight into the vault and never
 * touches the filesystem at all.
 *
 * Built on phpseclib rather than a hand-rolled `openssh-key-v1` frame, and not
 * for convenience: PhpseclibRemoteShell::connectWithKey() hands the stored text
 * to `PublicKeyLoader::load()`. Minting with the same library is what makes
 * "this is a valid OpenSSH key" the same statement as "this logs in" — a
 * self-written frame that merely looked right would be found out at the first
 * host takeover, after the root password had already been scrubbed.
 *
 * Ed25519 because a modern OpenSSH accepts it everywhere, the key is short
 * enough to read out over a phone, and there is no key size to get wrong.
 */
final readonly class Keypair
{
    /**
     * What the public half is labelled with in `authorized_keys`.
     *
     * Fixed rather than derived from the installation: this string lands on
     * every host's root account, and an operator reading that file wants to see
     * WHO the key belongs to — not the hostname of whichever machine the panel
     * happened to run on when it was minted.
     */
    public const COMMENT = 'clupilot';

    public function __construct(public string $privateKey, public string $publicKey) {}

    public static function generate(): self
    {
        $key = EC::createKey('Ed25519');

        return new self(
            privateKey: $key->toString('OpenSSH'),
            // One line, `ssh-ed25519 AAAA… clupilot`, which is the whole of
            // what EstablishSshTrust appends to a host's authorized_keys.
            publicKey: $key->getPublicKey()->toString('OpenSSH', ['comment' => self::COMMENT]),
        );
    }
}
