Bash terminal for LXD container in webpage

You were going wrong in a few places, it would be easier to explain on something like Github, most notably you weren’t using the right secret paths and text needs to be encoded before being sent to the websocket.

In the future when sharing code you should copy and paste it, not screenshot it as people are less likely to help if they to type it all out.

Here is a script that works, just change the variables at the top so it has your details in.

<?php
    /**
     * CHANGE ONLY THESE
     */
    $lxdDomainAndPort = "localhost:8443";
    $containerName = 'c1';
    $lxdCertLocation = 'PATH_TO_CERT.cert';
    $lxdKeyLocation = 'PATH_TO_KEY.key';
    $command = "bash"; // Will have to use "ash" or "zsh" or some instance types

    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "https://$lxdDomainAndPort/1.0/containers/{$containerName}/exec",
        CURLOPT_RETURNTRANSFER=> true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode([
            'command' => [$command],
            'environment' => [
                'HOME' => '/root',
                'TERM' => 'xterm',
                'USER' => 'root',
            ],
            'wait-for-websocket' => true,
            'interactive' => true,
            'width' => 80,
            'height' => 24,
            'timeout' => 60
        ]),
        CURLOPT_HTTPHEADER => [ 'Content-Type: application/json'],
        CURLOPT_SSLCERT => $lxdCertLocation,
        CURLOPT_SSLKEY => $lxdKeyLocation,
        CURLOPT_SSL_VERIFYHOST => 0,
        CURLOPT_SSL_VERIFYPEER => 0
    ]);
    $response = curl_exec($curl);
    $result = json_decode($response, true);
    $operationId = $result['operation'];
    $websocketUrl = "wss://{$lxdDomainAndPort}{$operationId}/websocket?secret=".$result['metadata']['metadata']["fds"][0];
    $controlUrl = "wss://{$lxdDomainAndPort}{$operationId}/websocket?secret=".$result['metadata']['metadata']["fds"]["control"];

?>
<style>
body {
    background-color: #002b36;
    color: #839496;
    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
}

#terminal-container {
    width: 80%;
    height: 500px;
    margin: auto;
    margin-top: 50px;
}
</style>
<div id="terminal-container"></div>
<script src=" https://cdn.jsdelivr.net/npm/xterm@5.1.0/lib/xterm.min.js "></script>
<link href=" https://cdn.jsdelivr.net/npm/xterm@5.1.0/css/xterm.min.css " rel="stylesheet">
<script>
    // Create an xterm.js terminal instance
    var term = new Terminal();
    // Attach the terminal instance to a container element
    term.open(document.getElementById('terminal-container'));
    // You must do this these days
    var controlSocket = new WebSocket('<?php echo $controlUrl; ?>');
    // Connect to the WebSocket endpoint
    var socket = new WebSocket('<?php echo $websocketUrl; ?>');
    // Set binary type
    socket.binaryType = "arraybuffer"
    // Handle incoming messages from the WebSocket
    socket.addEventListener('message', function (event) {
        term.write(new TextDecoder().decode(event.data));
    });
    // Handle user input and send it to the WebSocket
    term.onData(function (data) {
        socket.send(new TextEncoder(data).encode(data))
    });
</script>

Edit 1: Remove the leftover xterm-attach addon (it didn’t work as expected)