From testing I can see that it is possible for a desktop macOS users to bind() TCP port numbers below 1024 (the so-called well-known ports). It works on both IPv4 and IPv6, and on the localhost/unspecified addresses.
The Darwin kernel source code is freely available - from there I can see that it has the functionality built-in to check for processes trying to bind/listen on ports <1024 and deny that. This is similar to the way Linux works. However, it is possible to disable this check when compiling the kernel by defining "IPNOPRIVPORTS". I assume this is what was done with the Mojave supplied kernel.
On Linux it used to be the case that it wasn't possible for non-root to bind low port numbers, but today you have various options of allowing non-root users to bind to low numbered ports. Such as for example using the capability NET_BIND_SERVICE.
On macOS you also have various options for restricting binding low-numbered ports. For example through kexts that does socket filtering, or through other means. Perhaps you have software installed, such as for example Little Snitch, that denied you permission when you were testing.
Please note that the various IP-addresses you have tried with have entirely different meanings:
127.0.0.1 = IPv4 address that means the "localhost" (i.e. your own computer basically) - it is not available on the network
[::1] = the IPv6 equivalent of 127.0.0.1
0.0.0.0 = IPv4 "unspecified address" - i.e. this is not an address in itself, rather it is a special value you give to bind() to let it know that you haven't specified a specific address that you want the port bind() on. Essentially this means that the port will be bound on all the IP-addresses you have.
[::0] = IPv6 equivalent of 0.0.0.0 (though usually written [::/128])
This can explain why some things failed and some things succeeded. For example you might not be able to bind to 127.0.0.1, but 0.0.0.0 works as it tries to bind the port not only on 127.0.0.1 but also on other IP-addresses you have.