There might be some tricks we can use to get there. An algorithm for building ... precliques? comes to mind. We have shared weak address spaces, processes that add mutable storage on top of those, and endpoints that connect processes together.
Endpoints and cappages. I don't think we know clearly yet just how extensively people will use cappages but I guess that's the challenging part.
Circling back to this, though I will take these up in separate replies.
Cabbages are a terrible solution for capability storage. Words to live by. Thanks, gmail! :-)
CapPages are too. :-) The first problem they solve is that a few objects need to store capabilities in bulk. The second problem is that we sometimes want to pass capabilities as function arguments (e.g. in libraries). With capability pages you can hypothetically build a shadow stack for capabilities, but that requires more significant compiler modifications that we haven't ever gotten around to doing yet. Tagged memory would remove the need to have a different kind of page or a shadow stack, because it would allow capabilities and data to be commingled in the same page. One way or another we really need that part of the user-visible programming model not to suck.
In KeyKOS, the practice was to leave capabilities in the capability registers, effectively using some of them as global storage allocators. From the compiler perspective, they look like static register allocations.
Maybe it's just the applications I happened to try to write first, but I found "all caps have to go in registers" constraining even for surprisingly simple applications.
Jonathan