Capabilities Example
A nice introductory example of capabilities (as found in ‘object capability systems’) vs access control lists, is to compare the following commands:
cp some/source/file some/destination/file
cat < some/source/file > some/destination/file
The first one is putting a lot of trust in the cp
command: we need to give it read/write access to the file system, and we hope that it will only access those paths we asked it to, and that it will access them in the right ways (reading from one, writing to the other).
In the second command, the cat
program doesn’t need any access to the filesystem, and the only thing we need to trust is that it will send through the data unchanged. That trust seems pretty much required though, since it’s the reason to use cat
in the first place.
A similar example is how Haskell programs tend to implement most of their logic using pure functions, and limit potentially-dangerous IO actions to the periphery. For example, I’ve written many programs which look like this:
main :: IO ()
main = interact someFunction
someFunction :: String -> String
someFunction input = ...
Here the main
function has type IO ()
, meaning that it can perform arbitrary effects. It calls the interact
function, which reads from stdin and writes to stdout, transforming one into the other using the given function (here called someFunction
). The nice part is: someFunction
is just a pure function from String
s to String
s, it doesn’t care where these String
s come from or go to. The only “capability” available to someFunction
(and hence to any other functions that it calls) is to affect the contents of its output String
(assuming we’re using the “Safe Haskell” option in GHC).
Compared to the above cp
/cat
example, we can think of main
as acting like the shell, and interact
as acting like < /dev/stdin > /dev/stdout
.