[e-lang] Revoking Capabilities

Mark S. Miller e-lang@mail.eros-os.org
Thu, 30 Jan 2003 10:20:48 -0800

At 09:27 PM 1/27/2003 Monday, Chris Turner wrote:
>Hi all,
>I've recently started playing around with E, and am trying to wrap my head 
>around some things... Would the following code snippet work for capability 

Yes! Although Constantine's suggested alternative of declaring "obj" to be 
"var" and setting it to null is better style, your's does work. You got it 
on the first shot.

Constantine and Bill are also right when about the weakness of this 
approach, but that doesn't invalidate it. It only limits its usefulness. I 
call this kind of revocation pattern "cooperative revocation" -- when the 
wrapped object's protocol is designed to be revoked by this pattern. 

A complex and useful example of cooperative revocation shown on 
http://www.erights.org/elib/capability/ode/ode-bearer.html is the use of 
makeTitleCompany -- an abstraction built around a revocable wrapper maker -- 
to transfer exclusive rights to a coveredCallOption as made by 
makeCoveredCallOption. The API of coveredCallOption is designed to not 
subvert the need to revoke access, and the scenario of use these were 
designed for justifies the cooperative assumption between the two 
abstractions -- they're both instantiated and composed by one interest -- 
the broker -- who uses this code to stand between four other mutually 
suspicious interests (the money issuer, the stock issuer, the options 
writer, and the options holder).

For uncooperative revocation, we need an abstraction I call a Membrane. It 
is much like the current CapTP comm system, in that 1) it stand between two 
subgraphs of the object graph by wrapping references going in either 
direction, 2) as messages are passed, to wraps and unwraps args and results 
in order to stay between, and 3) it can be severed, partitioning the two sides.

Unlike the CapTP comm system, this will be built only for intra-process use, 
keeping the revocability-for-security concerns separate from the comm system 
concerns, even though the mechanisms are so similar.

Unlike the CapTP comm system, and like E-Speak2.2, the Membrane must proxy 
introductions rather than shorten them. In other words, if Alice is within a 
Membrane and Bob and Carol are both outside, after Alice introduces Bob to 
Carol, the reference Bob holds goes back into the Membrane to a proxy for 
Carol, and then back out to Carol herself. When Bob's access to Alice gets 
revoked, so does the access to Carol that he obtained through Alice.

By contrast, in the corresponding 3-vat CapTP introduction, we shorten, so 
that if a partition separates Bob from Alice, Bob can remain connected to 
Carol. A revocation is an expression of policy, whereas a partition is 
simply a problem you survive as best you can.

>As far as I can tell this should work in a distributed 
>environment for remote revocation on a larger scale than this simple test 
>case, but I thought I'd check.

Almost perfect, but for one additional issue when your pattern is used in a 
distributed context. 

The distributed capability paradigm is surprising faithful to the properties 
of the single-machine capability paradigm. One of the differences between 
the two is that single-machine capabilities are "reliable", meaning that 
they work perfectly for as long as their universe exists. Distributed 
capabilities can be at most fail-stop, since one side can fail without the 
other, or a partition can separate them. Usually, this fails safe, since an 
inability to exercise authority is a lack of service, but not a breach.

The revoker is a nice example of the other case. If the holder of the 
revoker is remote, then a partition can prevent them from sending a revoke 
message, leaving the holder of the wrapper with too much authority. To solve 
this, we turn the revoker into a "dead-man switch" as follows:

        def revoker {
            to revoke() { ... }
            to __reactToLostClient(problem) { revoker.revoke() }

__reactToLostClient is a MirandaMethod explained at

>When someone was holding a partitionable eventual reference to this object, 
>and it suffers a partition, then inform this object that one of its clients 
>may no longer be able to talk to it, and why.
>The Miranda behavior is to do nothing, but objects may override this to 
>provide DeadManSwitch behavior. For example, a revoking facet of a revokable 
>service may decide that if its client may no longer be able to talk to it, 
>that it should auto-revoke. However inconvenient this solution, it is failsafe.

So, once we introduce Membranes, we have an interesting design choice. 
Should a revoking Membrane send __reactToLostClient messages to the severed 
parties? How should we think about revocable rights to revoke? 

Text by me above is hereby placed in the public domain