Because that’s how networking works in literally every system ever. Containers don’t magically "export" services to the world. They have to bind to a port. That’s how TCP/IP, networking stacks, and every server-client model ever designed functions. Docker is no exception. It has an internal port (inside the container) and an external port (on the host), again, when we're dealing with the default bridge networking. Mapping these is a fundamental requirement for exposing services. Complaining about this is like whining that you have to plug in a power cable to use a computer. Clearly your "expertise" in networking is... Well. Another misunderstanding.
> The tooling should let me wire up a container’s 'http' export to some consumer or to my local port 8000.
Ummmm... It does. It's called: Docker Compose, --network, or service discovery. You can use docker run -p 8000:80 or define a Docker network where containers resolve each other by name. You already don’t have to care about internal ports inside a proper Docker setup.
But you still need to map ports when exposing to the host because… Guess what? Your host machine isn't psychic. It doesn’t magically figure out that some random container process running an HTTP server needs to be accessible on a specific port. That’s why port mapping exists. But you already know this because "you understand TCP and UDP just fine".
> The internal number should be an implementation detail.
This hands-down the dumbest part of the argument. Ports are not just "implementation details." They're literally how services communicate. Inside the container, your app binds to a port (usually one) that it was explicitly configured to use.
If an app inside a container is listening on port 5000, but you want to access it on port 8000, you must declare that mapping (-p 8000:5000). Otherwise, how the hell is Docker (or anyone) supposed to know what port to use? According to you - the software should magically resolve this. And guess what? You don’t have to expose ports if you don’t need to. Just connect containers via a shared network which happens automagically via container name resolution within Docker networking.
Saying ports should be an "implementation detail" is like saying street addresses should be an implementation detail when mailing a letter. You need an address so people know where to send things. I'm sure you get all sorts of riled up when you need to put an address on a blank envelope because the mail should just know... Right? o_O