Why not object capability languages?

62 mike_hearn 29 5/11/2025, 6:57:07 PM blog.plan99.net ↗

Comments (29)

jauntywundrkind · 9h ago
The WASI systems libraries that define the standard platform for WebAssembly/wasm are Capability-based.

From their first high level goal:

> Define a set of portable, modular, runtime-independent, and WebAssembly-native APIs which can be used by WebAssembly code to interact with the outside world. These APIs preserve the essential sandboxed nature of WebAssembly through a Capability-based API design.

https://github.com/WebAssembly/WASI/blob/main/README.md#wasi...

hedora · 3h ago
Why not?

Answering the question: “Can this process access this resource?” is equivalent to solving the halting problem.

There’s a reason simpler access control models are popular. Even ACLs are completely untenable in practice. Look at all the trouble accidentally-public s3 buckets create.

ablob · 25m ago
Now I am aware that answering the question is np-hard, but why (and how) is it equivalent to solving the halting problem?
kumavis · 8h ago
Caja's spiritual successor is HardenedJS (https://hardenedjs.org/), authored by some of the same folks (Mark Miller + friends). As I understand it, Caja attempted to secure not just javascript but the DOM as well, which ultimately proved to be a too large, interconnected, and rapidly changing surface to keep up with.

LavaMoat (https://lavamoat.github.io/), while not quite object capabilities, builds on HardenedJS to provide runtime supplychain security protections to js apps (nodejs or browser) by eliminating ambient authority and only exposing global capabilities per npm package according user-specified policy. LavaMoat is used in production at MetaMask, protecting ~300M users.

OCapN (https://github.com/ocapn/ocapn/) is a nascent effort to standardize a distributed object capability protocol (transferring capabilities across mutually distrusting peers).

vvanders · 11h ago
One capability mechanism that's in wide use but not really well known or touched on in the article is Androids RPC mechanism, Binder(and a lot of the history predates Android from what I recall).

Binder handles work just like object capabilities, you can only use what's sent to you and process can delegate out other binder handles.

Android hides most of this behind their permission model but the capability still exist and can be implemented by anyone in the system.

duncanbeevers · 11h ago
There's a great paper implementing this idea in the node.js ecosystem; [BreakApp: Automated, Flexible Application Compartmentalization](https://ic.ese.upenn.edu/pdf/breakapp_ndss2018.pdf) which modifies the `require` signature to allow specifying a security scope in which the module can be run.

It doesn't quite work at the capabilities level, but it does provide some novel protections against unusual supply-chain attacks such as denial-of-service attacks which may otherwise require no special capabilities.

kumavis · 8h ago
hadn't heard of breakapp! paper author Nikos Vasilakis also contributed to Mir (https://github.com/andromeda/mir).

This is similar to my work on LavaMoat (https://lavamoat.github.io/) which provides runtime supplychain security protections to js apps (nodejs or browser) by eliminating ambient authority and only exposing global capabilities per npm package according user-specified policy. LavaMoat is used in production at MetaMask, protecting ~300M users.

cryptonector · 3h ago
Ahh, JAAS... Java applets were removed, but JAAS, which only existed to support applets, could not be removed because there was lots of code that depended on the Login and Subject classes and the runAs() method. Why would such code exist though, if JAAS existed only to support applets? Well, because the Login class could be used to acquire credentials. For example the Krb5Login class could be used as a Java kinit in Kerberos environments.

Anyways, JAAS' Permission class and model are weak, but yeah, they could be used to limit libraries' capabilities. A capability model would be much better than a permission model.

salmonellaeater · 4h ago
It's unclear to me why the "god object" pattern described in the article isn't a good solution. The pattern in the article is different from the "god object" pattern as commonly known[1], in that the god object in the article doesn't need to be referenced after initialization. It's basically used once during initialization then forgotten.

It's normal for an application to be built from many independent modules that accept their dependencies as inputs via dependency inversion[2]. The modules are initialized at program start by code that composes everything together. Using the "god object" pattern from the article is basically the same thing.

[1] https://en.wikipedia.org/wiki/God_object [2] https://en.wikipedia.org/wiki/Dependency_inversion_principle

RainyDayTmrw · 4h ago
An interesting analogy could be Rust's unsafe. One important property of unsafe is that it's not inherently contagious. A safe function can contain an unsafe block, and that unsafe block can contain unsafe code. What this entails in practice is the function's author pinky-swearing that, despite its internals, the author promises that the function won't violate memory safety. The analogy isn't exact. In particular, functions are allowed to place human language constraints on their callers, which aren't verified, in order to uphold that guarantee. I wonder why there hasn't been any work done in this direction. It seems promising, and if there's any obvious walls that this hits, at least I hadn't figured out any.
Veedrac · 3h ago
Without speaking to all of the issues, this is all made much harder by the underlying hardware having extremely bad defaults. The idea that running code on the hardware is itself an unsafe operation means that any time you want to touch it you need proxies and intermediate languages and all this by default.

It's pretty easy for me to imagine a world where running code was safe by default, and this followed all the way to the top. It's obviously not that onerous, else JavaScript wouldn't be as successful as it is. Most of the details the post touches on are then just package management and grouping concerns.

hedora · 3h ago
> The idea that running code on the hardware is itself an unsafe operation

Ignoring microcontrollers, and tiny embedded stuff, no hardware or modern operating systems I know of works that way.

Modern hardware almost all has an MMU (which blocks I/O once the process table is set up), and most have an IOMMU (which partitions the hardware mutually distrusting operating systems can run directly on the same machine).

The remaining architectural holes are side channel / timing attacks that hit JS just as hard as bare metal.

Veedrac · 3h ago
Process isolation only works across processes. You can't just execute an untrusted block of code without setting up a whole sandbox for it.
jerf · 9h ago
Yup, this is all really hard, which is why it hasn't been much more than a research project up to this point.

If I had to guess, the supply chain problems that may eventually cause this to be created will need to get, oh, I don't know, call it two orders of magnitude worse before the system as a whole really takes note. Then, since you can't really write a new language just for this, even though I'd like that to happen, it'll get bodged on to the side of existing languages and it won't be all that slick.

That said, I do think there's probably some 80/20 value in creating an annotation to the effect of "this library doesn't need filesystem access or sockets" and having perhaps a linter or some other external tool validate it externally to the main language compiler/runtime. The point of this would not be to solve the capabilities problem for libraries that are doing intrinsically tricky things, because that's really hard to do correctly, but just to get a lot of libraries out of the line of fire. There's a lot of libraries that already don't need to do those things, and more that could easily be tweaked to just take passed-in file handles or whatever if there was a concrete reason to design them that way.

The library that I personally could do the most damage with on my GitHub is a supervision tree library for Go. It doesn't need any capabilities to speak of. The closest thing is that you can pass in a logger object and that is constrained to specific calls too. Even a hack that just lets me say that this library doesn't need anything interesting would at least get that out of the set of libraries that could be exploited.

Or to put it another way, rather than trying to perfectly label all the code doing tricksy stuff, maybe we can start by labelling the code that doesn't.

I'd also point out that I think the question of libraries is different than things like Chrome isolation. Those things are good, but they're for treating data carefully and limiting blast radiuses; I'm looking at the problem of "if I download this library and miss one single file is it going to upload every AWS token it can find to someone who shouldn't have them".

mlinksva · 5h ago
I did not see a link to your screenshotted comment in the article so looked it up, https://news.ycombinator.com/item?id=43936830 to save others the work (not the whole thing is screenshotted, and the comment and its thread are good, thanks!)
pyinstallwoes · 9h ago
Shouldn’t it be automatic based on behavior? An annotation is ripe for exploitation if the system itself can’t make sense of its own parts.
jerf · 9h ago
That would be preferable, certainly. I'm staying vague because there's a world of differences in how all the languages would most easily be able to implement something like this and I'm trying to stay language non-specific. Imagine how you'd solve this in Rust versus Ruby.
rawkode · 7h ago
Surprised Pony isn't mentioned:

https://www.ponylang.io/

rawkode · 7h ago
jFriedensreich · 11h ago
The capability system i hear talked about too little for some reason and is even more "chromey baby" is workerd using isolates. You can clearly see the lineage from sandstorm/ capnp and its kind of crazy something like this is finally in a mainstream platform. Sure, the concept is not taken to the extreme without much possibility to delegate / demote capabilities at runtime, but the direction is clearly what we need. Whenever i have to come back to other environments I immediately feel the lack of trust, clarity and control these have.
refulgentis · 9h ago
For some reason I'm having a horrible time googling workerd, any tips? -- also, I might be looking up the wrong thing? (I'm hoping to learn more about the environment you are in before you "have to come back to other environments")
ameliaquining · 8h ago
coolcase · 9h ago
Ecosystems get it right because they have to. E.g. iOS and Android etc. This ain't so good on desktop systems.

Probably the compiled program should just get tbe permissions it needs.

A simple capability system for libraries might be the good that is the enemy of perfect:

Pure - can only access compute and its own memory plus passed in parameters (needs immutable languages or serialization at interop)

Storage IO - Pure but can do Storage IO. IO on what? Anything the program has access to.

Network IO - similar concept

Desktop in Window - can do UI stuff in the window

Desktop General - models, notifications, new windows etc.

Etc...

Not very fine grained but many libraries cab be Pure.

It ain't perfect.

A Pure library that formats a string can still inject some nasty JS hoping that you'll use that string on a web page! Ultimately... useful computation is messy and you can't secure everything in advance through capabilities alone.

ameliaquining · 8h ago
IIUC iOS and Android don't have library sandboxing; any code that you allow to run in your app's process can access the whole address space. Apps themselves are sandboxed, but that doesn't help with the class of problem that this post is about.
jdougan · 10h ago
I think Newspeak may have potential in this area.
kumavis · 8h ago
hyperpape · 8h ago
I’m really stuck on a relatively minor point: why does Joe-E have to ban the finally keyword?
macintux · 8h ago
Per Wikipedia: non-deterministic execution.

https://en.wikipedia.org/wiki/Joe-E

burnt-resistor · 8h ago
See also: formal verification methodologies like those applied to seL4. Holistic practices of upping rigorous proofs of safety and correctness is the elephant in the room besides merely testing, language features, or any one solution.