“lxc exec NAME – bash” will send a request to the LXD daemon, either locally or remotely using LXD’s REST API. The LXD daemon will then spawn the command directly inside the container by attaching to the contianer’s namespaces and spawning the requested binary. stdin, stdout and stderr for the process are then bridged to websockets that are returned to the client over the REST API.
So the client tool only does HTTP requests to get the command executed, the server talks to the kernel, spawns the process in the container, makes sure all resource limits and security restrictions are applied and then connects the console devices.
Since that all happens directly through kernel APIs, there’s no need for any daemon to run inside the container (like sshd) and you can use “lxc exec” even against containers which aren’t reachable over the network (so long as you can talk to the LXD daemon on the host).