> But it would have bad ergonomics and still end up with a SecurityManager equivalent because a DSL for permissions is more convenient than doing it all in code.
This might be the core contention. I don't know if using actual capabilities in a language would have problematically bad ergonomics. You'd probably be passing more arguments to functions. But haskell seems to manage ok despite needing to pass IO to functions that need it. Capabilities seem similarly inconvenient. I think I'd need to see it tried. I agree - I might need to try it myself.
> Consider the most common task the SecurityManager was deployed for: stopping plugins calling System.exit() by accident. One might say, the right to exit the process should be an object capability.
I don't think this is a great example. Caps are generally for resources outside of your program or module scope. A program already has the capability to exit, so that wouldn't be something you would pass in from outside of the program.
> Java programs start at main() and it doesn't receive an object.
I agree that retrofitting caps into an existing language like java would be difficult and inconvenient. Passing a "god cap" to main() is the easy part! The hard part is just how much of the standard library implicitly depends on ambient authority. I've thought about doing this in rust, and concluded that I'd probably need to fork rust's std library.
> You'd get an explosion of types.
I've never heard of that stopping java programmers before.
The way SeL4 handles this is to have a generic call() interface for capabilities. It's very simple, and it would work fine in this example.
> Why would you want all HTTP requests to flow through a single capability object?
Capabilities are a combination of resource + access rights over that resource. If I wanted to give a module access to a REST endpoint, I'd make a cap representing that endpoint. The resource is the URL base (eg "example.com/foo/bar"). And I'd also specify access rights (eg only HEAD+GET, or HEAD+GET+POST or whatever makes sense). Then pass that object around to any modules which need access. I'd even keep the URL prefix private in the capability object. The capability object would only expose methods for http_get(), http_head(), http_post() and so on. This design would be more or less impossible to misuse. And it would be super handy for unit testing and dev environments.
It's not "one capability for everything" and it's not "a million fine-grained access rights". You want one cap per semantic resource, just like one fd per open file. If you want to refine the granted permissions, just reimplement the same interface with a different implementation of http_get() and friends. (Or, simpler: just wrap your existing RESTEndpoint class with another class which adds your extra checks).
> except then there's a weird ioctl escape hatch that isn't properly typed,
This is a flaw of the unix syscall API. In comparison, SeL4 only has 9 syscalls (plus 2 for debugging). The syscalls just let you call capabilities, and have your capabilities be called by other processes. And yield(). That's all the syscalls on sel4.
Because everything runs through that same API, it's trivial to stub out or replace capabilities provided by different components. Eg, any program can reimplement the filesystem API if it wants to. No need for FUSE, or special loopback mounting or anything like that. Because the filesystem is just a userland process which doesn't have access to the kernel's memory, there are no ioctls that you can accidentally forget to sandbox. The only special thing about the filesystem is that it holds a capability to do raw IO on the block device. (And that cap, in turn, is provided by another userland process.)
> My guess is you'd need a lot of language features to hide the explicit object capabilities away for ergonomic reasons
Yeah, I think that's our big disagreement. You seem to think that hiding object capabilities would be a necessary design choice. I think using caps directly would be much more ergonomic than a SecurityManager style design because custom caps can just be implemented in normal code. And caps are better because they encapsulate a resource, not just access control rights.