Welcome to the party, @Snowflake8. It sounds to me like your case is a good match for OpenZiti's capabilities.
Make sure I understood you correctly: you are committed to doing this with Docker Compose (as opposed to Linux services or Kubernetes), and you want to control which services can talk to each other, including which direction they can talk (who initiates the connection).
Can you say a little more about how you want to use mTLS? Ziti internally uses mTLS, but you might be thinking about using certificate authentication with the service you're publishing with Ziti too. For example, if you publish a web application with Ziti, your server could require clients' web browsers to present a valid user certificate. That would work with Ziti just fine because Ziti is application-neutral. It encrypts and delivers the whole application packet on a TCP or UDP socket.
One more clarification: when you say "fully local" I think you mean that the servers you are publishing with Ziti are not listening on any IP addresses except the loopback address, so they're not reachable without Ziti outside the device. Is that accurate?
This is a good example of what Ziti does best: total isolation at the connectivity layer, allowing only authorized connections via Ziti services.
Like Service B, right? Ziti's "tunnelers" don't prevent the application from connecting to non-Ziti destinations, but has an additive, split-tunnel mode of operation. A client application that attempts to resolve a DNS name like "myapp.ziti.internal" will first check Ziti DNS which is always the primary nameserver. If that fails, then it should ask the next nameserver. If you can only configure one nameserver, you can tell the Ziti tunneler to recurse instead of failing the query.
Let's distinguish between publishing the Ziti control and data planes vs. publishing your applications with Ziti or Nginx or both.
Control and data planes: at least one Ziti controller TCP port and one router TCP port must be reachable from anywhere.
Your applications: you wanted at least some of the applications to be "fully local," so I don't think you want to publish them with Nginx, but only publish them with Ziti. However, suppose you want to publish a web application with Ziti, and Nginx is providing TLS termination for the web server. Let me know if you have such a need. I have some ideas.
Yes, you'd normally create a Ziti identity for each thing so you can give it a friendly name and a role so it can be authorized for Ziti services in one or both directions, i.e., clients "dial" and servers "bind"/"host" Ziti services.
Ziti can connect different processes on the same host, other hosts in the same network, or span various networks. As far as Ziti is concerned, your Docker bridge networks are just another subnet. You can isolate your apps with a straightforward firewall rule like "allow outbound only," place your Ziti routers so they're reachable from anywhere, like a public IP, or at least reachable from the Ziti identities' subnets, then write Ziti service policies to give permission for each direction (dial or bind) and each application (Ziti service).