Merge "veyron/services/security/revoker: revocation service"
diff --git a/services/security/discharger/discharger.go b/services/security/discharger/discharger.go
new file mode 100644
index 0000000..9bcb9ae
--- /dev/null
+++ b/services/security/discharger/discharger.go
@@ -0,0 +1,31 @@
+package discharger
+
+import (
+	"fmt"
+	"time"
+	ssecurity "veyron/services/security"
+	"veyron2/ipc"
+	"veyron2/security"
+	"veyron2/vdl/vdlutil"
+)
+
+// dischargerd issues discharges for all caveats present in the current
+// namespace with no additional caveats iff the caveat is valid.
+type dischargerd struct {
+	id security.PrivateID
+}
+
+// TODO(andreser,ataly): make it easier for third party public key caveats to specify the caveats on their discharges
+
+func (d dischargerd) Discharge(ctx ipc.ServerContext, caveatAny vdlutil.Any) (vdlutil.Any, error) {
+	caveat, ok := caveatAny.(security.ThirdPartyCaveat)
+	if !ok {
+		return nil, fmt.Errorf("type %T does not implement security.ThirdPartyCaveat", caveatAny)
+	}
+	return d.id.MintDischarge(caveat, ctx, time.Minute, nil)
+}
+
+// New returns a Discharger server that can be passed to a dispatcher
+func New(id security.PrivateID) interface{} {
+	return ssecurity.NewServerDischarger(&dischargerd{id})
+}
diff --git a/services/security/discharger/revoker.go b/services/security/discharger/revoker.go
new file mode 100644
index 0000000..4f32574
--- /dev/null
+++ b/services/security/discharger/revoker.go
@@ -0,0 +1,123 @@
+package discharger
+
+import (
+	"crypto/rand"
+	"crypto/sha256"
+	"encoding/hex"
+	"fmt"
+	"path/filepath"
+	"strings"
+	"sync"
+	"veyron/security/caveat"
+	ssecurity "veyron/services/security"
+	"veyron2/ipc"
+	"veyron2/naming"
+	"veyron2/rt"
+	"veyron2/security"
+	"veyron2/storage"
+	"veyron2/storage/vstore"
+	"veyron2/storage/vstore/primitives"
+	"veyron2/vlog"
+	"veyron2/vom"
+)
+
+// TODO(ataly, andreser) This package uses a global variable to store the
+// revoker state to make it accessible to caveat.Validate. Ideally, we would
+// pass this in through the context (or something equivalent).
+
+type revocationServiceT struct {
+	store       storage.Store
+	pathInStore string
+}
+
+var revocationService struct {
+	*revocationServiceT
+	sync.Mutex
+}
+
+type revocationCaveat [32]byte
+
+func (cav revocationCaveat) Validate(security.Context) error {
+	// TODO(ashankar,mattr): Figure out how to get the context of an existing RPC here
+	rctx := rt.R().NewContext()
+	revocation := revocationService.store.Bind(naming.Join(revocationService.pathInStore,
+		hex.EncodeToString(cav[:])))
+	tx := primitives.NewTransaction(rctx)
+	defer tx.Abort(rctx)
+	exists, err := revocation.Exists(rctx, tx)
+	if err != nil {
+		return err
+	}
+	if exists {
+		return fmt.Errorf("revoked")
+	}
+	return nil
+}
+
+// NewRevocationCaveat returns a security.ThirdPartyCaveat that discharger will
+// mint discharges until explicitly told not to by calling Revoke on it
+// (using the returned revocation token)
+func NewRevocationCaveat(dischargerID security.PublicID, dischargerLocation string) (ssecurity.RevocationToken, security.ThirdPartyCaveat, error) {
+	var revocation ssecurity.RevocationToken
+	if _, err := rand.Read(revocation[:]); err != nil {
+		return revocation, nil, err
+	}
+	restriction := revocationCaveat(sha256.Sum256(revocation[:]))
+	cav, err := caveat.NewPublicKeyCaveat(restriction, dischargerID, dischargerLocation)
+	return revocation, cav, err
+}
+
+func (revoceationService *revocationServiceT) Revoke(ctx ipc.ServerContext, caveatPreimage ssecurity.RevocationToken) error {
+	caveatNonce := sha256.Sum256(caveatPreimage[:])
+	tx := primitives.NewTransaction(ctx)
+	revocation := revocationService.store.Bind(naming.Join(revocationService.pathInStore, hex.EncodeToString(caveatNonce[:])))
+	if _, err := revocation.Put(ctx, tx, caveatPreimage[:]); err != nil {
+		tx.Abort(ctx)
+		return err
+	}
+	return tx.Commit(ctx)
+}
+
+// NewRevoker returns a new revoker service that can be passed to a dispatcher.
+// Currently, due to the use of global variables, this function can be called only once.
+func NewRevoker(storeName, pathInStore string) (interface{}, error) {
+	revocationService.Lock()
+	defer revocationService.Unlock()
+	if revocationService.revocationServiceT != nil {
+		return nil, fmt.Errorf("revoker.Revoker called more than once")
+	}
+	var err error
+	revocationService.revocationServiceT = new(revocationServiceT)
+	revocationService.store, err = vstore.New(storeName)
+	if err != nil {
+		return nil, err
+	}
+
+	rctx := rt.R().NewContext()
+	tx := primitives.NewTransaction(rctx)
+
+	// Create parent directories for the revoker root, if necessary
+	// TODO(tilaks,andreser): provide a `mkdir -p` equivalent in store
+	l := strings.Split(pathInStore, "/")
+	fmt.Println(l)
+	for i := 0; i <= len(l); i++ {
+		fmt.Println(i, filepath.Join(l[:i]...))
+		prefix := filepath.Join(l[:i]...)
+		o := revocationService.store.Bind(prefix)
+		if exist, err := o.Exists(rctx, tx); err != nil {
+			vlog.Infof("Error checking existence at %q: %s", prefix, err)
+		} else if !exist {
+			if _, err := o.Put(rctx, tx, &Dir{}); err != nil {
+				vlog.Infof("Error creating directory %q: %s", prefix, err)
+			}
+		}
+	}
+	if err := tx.Commit(rctx); err != nil {
+		vlog.Fatalf("Commit creation of revocer root et %s: %s", pathInStore, err)
+	}
+	return ssecurity.NewServerRevoker(revocationService.revocationServiceT), nil
+}
+
+func init() {
+	vom.Register(revocationCaveat{})
+}
diff --git a/services/security/discharger/revoker_test.go b/services/security/discharger/revoker_test.go
new file mode 100644
index 0000000..acb89e9
--- /dev/null
+++ b/services/security/discharger/revoker_test.go
@@ -0,0 +1,97 @@
+package discharger
+
+import (
+	"testing"
+	ssecurity "veyron/services/security"
+	teststore "veyron/services/store/testutil"
+	"veyron2/ipc"
+	"veyron2/naming"
+	"veyron2/rt"
+	"veyron2/security"
+)
+
+func init() {
+	rt.Init()
+}
+
+func setup(t *testing.T) (dischargerID security.PublicID, dischargerEndpoint, revokerEndpoint string, closeFunc func()) {
+	// Create and start the store instance that the revoker will use
+	storeServer, err := rt.R().NewServer()
+	if err != nil {
+		t.Fatalf("rt.R().NewServer: %s", err)
+	}
+	storeVeyronName, closeStore := teststore.NewStore(t, storeServer, rt.R().Identity().PublicID())
+
+	// Create and start revoker and revocation discharge service
+	revokerServer, err := rt.R().NewServer()
+	if err != nil {
+		t.Fatalf("rt.R().NewServer: %s", err)
+	}
+	revokerEP, err := revokerServer.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		t.Fatalf("revokerServer.Listen failed: %v", err)
+	}
+	revokerService, err := NewRevoker(storeVeyronName, "/testrevoker")
+	if err != nil {
+		t.Fatalf("setup revoker service: %s", err)
+	}
+	err = revokerServer.Serve("", ipc.SoloDispatcher(revokerService, nil))
+	if err != nil {
+		t.Fatalf("revokerServer.Serve discharger: %s", err)
+	}
+
+	dischargerServer, err := rt.R().NewServer()
+	if err != nil {
+		t.Fatalf("rt.R().NewServer: %s", err)
+	}
+	dischargerEP, err := dischargerServer.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		t.Fatalf("revokerServer.Listen failed: %v", err)
+	}
+	err = dischargerServer.Serve("", ipc.SoloDispatcher(New(rt.R().Identity()), nil))
+	if err != nil {
+		t.Fatalf("revokerServer.Serve revoker: %s", err)
+	}
+	return rt.R().Identity().PublicID(),
+		naming.JoinAddressName(dischargerEP.String(), ""),
+		naming.JoinAddressName(revokerEP.String(), ""),
+		func() {
+			revokerServer.Stop()
+			dischargerServer.Stop()
+			closeStore()
+		}
+}
+
+func TestDischargeRevokeDischargeRevokeDischarge(t *testing.T) {
+	dcID, dc, rv, closeFunc := setup(t)
+	defer closeFunc()
+	revoker, err := ssecurity.BindRevoker(rv)
+	if err != nil {
+		t.Fatalf("error binding to server: ", err)
+	}
+	discharger, err := ssecurity.BindDischarger(dc)
+	if err != nil {
+		t.Fatalf("error binding to server: ", err)
+	}
+
+	preimage, cav, err := NewRevocationCaveat(dcID, dc)
+	if err != nil {
+		t.Fatalf("failed to create public key caveat: %s", err)
+	}
+
+	if _, err = discharger.Discharge(rt.R().NewContext(), cav); err != nil {
+		t.Fatalf("failed to get discharge: %s", err)
+	}
+	if err = revoker.Revoke(rt.R().NewContext(), preimage); err != nil {
+		t.Fatalf("failed to revoke: %s", err)
+	}
+	if discharge, err := discharger.Discharge(rt.R().NewContext(), cav); err == nil || discharge != nil {
+		t.Fatalf("got a discharge for a revoked caveat: %s", err)
+	}
+	if err = revoker.Revoke(rt.R().NewContext(), preimage); err != nil {
+		t.Fatalf("failed to revoke again: %s", err)
+	}
+	if discharge, err := discharger.Discharge(rt.R().NewContext(), cav); err == nil || discharge != nil {
+		t.Fatalf("got a discharge for a doubly revoked caveat: %s", err)
+	}
+}
diff --git a/services/security/discharger/schema.vdl b/services/security/discharger/schema.vdl
new file mode 100644
index 0000000..d92d035
--- /dev/null
+++ b/services/security/discharger/schema.vdl
@@ -0,0 +1,3 @@
+package discharger
+
+type Dir struct {} // TODO(tilaks, andreser): move this to store?
diff --git a/services/security/discharger/schema.vdl.go b/services/security/discharger/schema.vdl.go
new file mode 100644
index 0000000..462d255
--- /dev/null
+++ b/services/security/discharger/schema.vdl.go
@@ -0,0 +1,7 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: schema.vdl
+
+package discharger
+
+type Dir struct {
+} // TODO(tilaks, andreser): move this to store?
diff --git a/services/security/dischargerd/main.go b/services/security/dischargerd/main.go
new file mode 100644
index 0000000..cde9103
--- /dev/null
+++ b/services/security/dischargerd/main.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+	"flag"
+
+	"veyron/lib/signals"
+	"veyron/services/security/discharger"
+	"veyron2/ipc"
+	"veyron2/rt"
+	"veyron2/security"
+	"veyron2/vlog"
+)
+
+var (
+	protocol = flag.String("protocol", "tcp", "protocol to listen on")
+	address  = flag.String("address", ":0", "address to listen on")
+	aclFile  = flag.String("discharger-acl", "", "ACL to use for the discharge service")
+	publish  = flag.String("publish", "discharger", "the Object Name under which to publish this service")
+
+	storeName      = flag.String("revocation-store", "", "Object Name of the Veyron store to be used for revocation. Omit to disable revocation functionality.")
+	publishRevoker = flag.String("publish-revoker", "revoker", "the Object Name under which to publish this service")
+	pathInStore    = flag.String("path-in-store", "/revoker", "the location in store where the revoker keeps its state")
+	revokerAclFile = flag.String("revoker-acl", "", "ACL to use for the revocation service")
+)
+
+func authorizer(file string) security.Authorizer {
+	if file == "" {
+		return security.NewACLAuthorizer(security.ACL{security.AllPrincipals: security.AllLabels})
+	}
+	return security.NewFileACLAuthorizer(file)
+}
+
+func main() {
+	r := rt.Init()
+	defer r.Cleanup()
+
+	dischargerServer, err := r.NewServer()
+	if err != nil {
+		vlog.Fatal(err)
+	}
+	defer dischargerServer.Stop()
+	dischargerEndpoint, err := dischargerServer.Listen(*protocol, *address)
+	if err != nil {
+		vlog.Fatal(err)
+	}
+	if err = dischargerServer.Serve(*publish, ipc.SoloDispatcher(discharger.New(r.Identity()), authorizer(*aclFile))); err != nil {
+		vlog.Fatal(err)
+	}
+	vlog.Infof("discharger: %s", dischargerEndpoint.String())
+
+	if *storeName != "" {
+		revokerServer, err := r.NewServer()
+		if err != nil {
+			vlog.Fatal(err)
+		}
+		defer revokerServer.Stop()
+		revokerEndpoint, err := revokerServer.Listen(*protocol, *address)
+		if err != nil {
+			vlog.Fatal(err)
+		}
+		revokerService, err := discharger.NewRevoker(*storeName, *pathInStore)
+		if err != nil {
+			vlog.Fatal(err)
+		}
+		err = revokerServer.Serve(*publish, ipc.SoloDispatcher(revokerService, authorizer(*revokerAclFile)))
+		if err != nil {
+			vlog.Fatal(err)
+		}
+		vlog.Infof("revoker: %s", revokerEndpoint.String())
+	}
+
+	<-signals.ShutdownOnSignals()
+}
diff --git a/services/security/revoker.vdl b/services/security/revoker.vdl
new file mode 100644
index 0000000..da13597
--- /dev/null
+++ b/services/security/revoker.vdl
@@ -0,0 +1,27 @@
+package security
+
+import "veyron2/security"
+
+// RevocationToken can be presented to a revocation service to revoke a caveat
+type RevocationToken [16]byte
+
+// Revoker is the interface for preventing discharges from being issued. The
+// dicharger ensures that no discharges will be issued for caveats that
+// have been explicitly revoked using this interface. To prevent discharge
+// stealing caveats just have to be unique; the exact structure is not relevant
+// to the client or the verifier. To make Revoker's job easy, each caveat
+// contains a SHA256 hash of its revocation token. To revoke a caveat C and
+// have it added to the discharger's blacklist, one simply needs to call
+// Revoke(x) with an x s.t.  SHA256(x) = C. All caveats for which this has not
+// been revoked will get discharges, irrespective of who created them. This
+// means that the existence of a valid discharge does not imply that a
+// corresponding caveat exists, and even if it does, it may not be meant for
+// use with this revocation service. Just looking at discharges is meaningless,
+// a valid (Caveat, Discharge) pair is what can be relied on for
+// authentication. Not keeping track of non-revoked caveats enables
+// performance improvements on the Discharger side.
+type Revoker interface {
+	// Revoke ensures that iff a nil is returned, all discharge requests to the
+	// caveat with nonce sha256(caveatPreimage) are going to be denied.
+	Revoke(caveatPreimage RevocationToken) error {security.WriteLabel}
+}
diff --git a/services/security/revoker.vdl.go b/services/security/revoker.vdl.go
new file mode 100644
index 0000000..811fbe2
--- /dev/null
+++ b/services/security/revoker.vdl.go
@@ -0,0 +1,202 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: revoker.vdl
+
+package security
+
+import (
+	"veyron2/security"
+
+	// The non-user imports are prefixed with "_gen_" to prevent collisions.
+	_gen_veyron2 "veyron2"
+	_gen_context "veyron2/context"
+	_gen_ipc "veyron2/ipc"
+	_gen_naming "veyron2/naming"
+	_gen_rt "veyron2/rt"
+	_gen_vdlutil "veyron2/vdl/vdlutil"
+	_gen_wiretype "veyron2/wiretype"
+)
+
+// RevocationToken can be presented to a revocation service to revoke a caveat
+type RevocationToken [16]byte
+
+// Revoker is the interface for preventing discharges from being issued. The
+// dicharger ensures ensures that no discharges will be issued for caveats that
+// have been explicitly revoked using this interface. To prevent discharge
+// stealing caveats just have to be unique; the exact structure is not relevant
+// to the client or the verifier. To make Revoker's job easy, each caveat
+// contains a SHA256 hash of its revocation token. To revoke a caveat C and
+// have it added to the discharger's blacklist, one simply needs to call
+// Revoke(x) with an x s.t.  SHA256(x) = C. All caveats for which this has not
+// been revoked will get discharges, irrespective of who created them. This
+// means that the existence of a valid discharge does not imply that a
+// corresponding caveat exists, and even if it does, it may not be meant for
+// use with this revocation service. Just looking at discharges is meaningless,
+// a valid (Caveat, Discharge) pair is what can be relied on for
+// authentication. Not keeping track of non-revoked caveats enables
+// performance improvements on the Discharger side.
+// Revoker is the interface the client binds and uses.
+// Revoker_ExcludingUniversal is the interface without internal framework-added methods
+// to enable embedding without method collisions.  Not to be used directly by clients.
+type Revoker_ExcludingUniversal interface {
+	// Revoke ensures that iff a nil is returned, all discharge requests to the
+	// caveat with nonce sha256(caveatPreimage) are going to be denied.
+	Revoke(ctx _gen_context.T, caveatPreimage RevocationToken, opts ..._gen_ipc.CallOpt) (err error)
+}
+type Revoker interface {
+	_gen_ipc.UniversalServiceMethods
+	Revoker_ExcludingUniversal
+}
+
+// RevokerService is the interface the server implements.
+type RevokerService interface {
+
+	// Revoke ensures that iff a nil is returned, all discharge requests to the
+	// caveat with nonce sha256(caveatPreimage) are going to be denied.
+	Revoke(context _gen_ipc.ServerContext, caveatPreimage RevocationToken) (err error)
+}
+
+// BindRevoker returns the client stub implementing the Revoker
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindRevoker(name string, opts ..._gen_ipc.BindOpt) (Revoker, error) {
+	var client _gen_ipc.Client
+	switch len(opts) {
+	case 0:
+		client = _gen_rt.R().Client()
+	case 1:
+		switch o := opts[0].(type) {
+		case _gen_veyron2.Runtime:
+			client = o.Client()
+		case _gen_ipc.Client:
+			client = o
+		default:
+			return nil, _gen_vdlutil.ErrUnrecognizedOption
+		}
+	default:
+		return nil, _gen_vdlutil.ErrTooManyOptionsToBind
+	}
+	stub := &clientStubRevoker{client: client, name: name}
+
+	return stub, nil
+}
+
+// NewServerRevoker creates a new server stub.
+//
+// It takes a regular server implementing the RevokerService
+// interface, and returns a new server stub.
+func NewServerRevoker(server RevokerService) interface{} {
+	return &ServerStubRevoker{
+		service: server,
+	}
+}
+
+// clientStubRevoker implements Revoker.
+type clientStubRevoker struct {
+	client _gen_ipc.Client
+	name   string
+}
+
+func (__gen_c *clientStubRevoker) Revoke(ctx _gen_context.T, caveatPreimage RevocationToken, opts ..._gen_ipc.CallOpt) (err error) {
+	var call _gen_ipc.Call
+	if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Revoke", []interface{}{caveatPreimage}, opts...); err != nil {
+		return
+	}
+	if ierr := call.Finish(&err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+func (__gen_c *clientStubRevoker) UnresolveStep(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply []string, err error) {
+	var call _gen_ipc.Call
+	if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "UnresolveStep", nil, opts...); err != nil {
+		return
+	}
+	if ierr := call.Finish(&reply, &err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+func (__gen_c *clientStubRevoker) Signature(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply _gen_ipc.ServiceSignature, err error) {
+	var call _gen_ipc.Call
+	if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Signature", nil, opts...); err != nil {
+		return
+	}
+	if ierr := call.Finish(&reply, &err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+func (__gen_c *clientStubRevoker) GetMethodTags(ctx _gen_context.T, method string, opts ..._gen_ipc.CallOpt) (reply []interface{}, err error) {
+	var call _gen_ipc.Call
+	if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "GetMethodTags", []interface{}{method}, opts...); err != nil {
+		return
+	}
+	if ierr := call.Finish(&reply, &err); ierr != nil {
+		err = ierr
+	}
+	return
+}
+
+// ServerStubRevoker wraps a server that implements
+// RevokerService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubRevoker struct {
+	service RevokerService
+}
+
+func (__gen_s *ServerStubRevoker) GetMethodTags(call _gen_ipc.ServerCall, method string) ([]interface{}, error) {
+	// TODO(bprosnitz) GetMethodTags() will be replaces with Signature().
+	// Note: This exhibits some weird behavior like returning a nil error if the method isn't found.
+	// This will change when it is replaced with Signature().
+	switch method {
+	case "Revoke":
+		return []interface{}{security.Label(2)}, nil
+	default:
+		return nil, nil
+	}
+}
+
+func (__gen_s *ServerStubRevoker) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+	result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+	result.Methods["Revoke"] = _gen_ipc.MethodSignature{
+		InArgs: []_gen_ipc.MethodArgument{
+			{Name: "caveatPreimage", Type: 66},
+		},
+		OutArgs: []_gen_ipc.MethodArgument{
+			{Name: "", Type: 67},
+		},
+	}
+
+	result.TypeDefs = []_gen_vdlutil.Any{
+		_gen_wiretype.NamedPrimitiveType{Type: 0x32, Name: "byte", Tags: []string(nil)}, _gen_wiretype.ArrayType{Elem: 0x41, Len: 0x10, Name: "veyron/services/security.RevocationToken", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+	return result, nil
+}
+
+func (__gen_s *ServerStubRevoker) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+	if unresolver, ok := __gen_s.service.(_gen_ipc.Unresolver); ok {
+		return unresolver.UnresolveStep(call)
+	}
+	if call.Server() == nil {
+		return
+	}
+	var published []string
+	if published, err = call.Server().Published(); err != nil || published == nil {
+		return
+	}
+	reply = make([]string, len(published))
+	for i, p := range published {
+		reply[i] = _gen_naming.Join(p, call.Name())
+	}
+	return
+}
+
+func (__gen_s *ServerStubRevoker) Revoke(call _gen_ipc.ServerCall, caveatPreimage RevocationToken) (err error) {
+	err = __gen_s.service.Revoke(call, caveatPreimage)
+	return
+}
diff --git a/services/security/simpledischarged/main.go b/services/security/simpledischarged/main.go
deleted file mode 100644
index 427942d..0000000
--- a/services/security/simpledischarged/main.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package main
-
-import (
-	"errors"
-	"flag"
-	"fmt"
-	"log"
-	"time"
-
-	"veyron/lib/signals"
-	isecurity "veyron/services/security"
-	"veyron2/ipc"
-	"veyron2/rt"
-	"veyron2/security"
-	"veyron2/vdl/vdlutil"
-)
-
-// TODO(ataly, andreser): ideally, the expiration time (and other caveats) of
-// the discharge would be determined by a function much like ThirdPartyCaveat.Validate
-var expiration = flag.String("expiration", "10s", "time interval after which a discharge will expire")
-var protocol = flag.String("protocol", "bluetooth", "protocol to listen on")
-var address = flag.String("address", "", "address to listen on")
-var port = flag.Int("port", 0, "port to listen on")
-var publish = flag.String("publish", "", "the namespace where to publish this service")
-
-type dischargeAuthorizer struct{}
-
-func (dischargeAuthorizer) Authorize(c security.Context) error {
-	if c.Method() == "Discharge" {
-		return nil
-	}
-	return fmt.Errorf("Only authorized for method \"Discharge\"")
-}
-
-// discharged implements ipc.Dispatcher. It issues discharges for all caveats
-// present in the current namespace with no additional caveats iff the caveat
-// is valid.
-type discharged struct {
-	id         security.PrivateID
-	expiration time.Duration
-}
-
-func (d *discharged) Discharge(ctx ipc.ServerContext, Caveat vdlutil.Any) (
-	Discharge vdlutil.Any, err error) {
-	caveat, ok := Caveat.(security.ThirdPartyCaveat)
-	if !ok {
-		err = errors.New("unknown caveat")
-		return
-	}
-	return d.id.MintDischarge(caveat, ctx, d.expiration, nil)
-}
-
-func main() {
-	flag.Parse()
-	expiration, err := time.ParseDuration(*expiration)
-	if err != nil {
-		log.Fatalf("--expiration: ", err)
-	}
-
-	r := rt.Init()
-	server, err := r.NewServer()
-	if err != nil {
-		log.Fatal(err)
-	}
-
-	discharger := isecurity.NewServerDischarger(&discharged{
-		id: r.Identity(), expiration: expiration})
-	dispatcher := ipc.SoloDispatcher(discharger, dischargeAuthorizer{})
-	endpoint, err := server.Listen(*protocol, *address+":"+fmt.Sprint(*port))
-	if err != nil {
-		log.Fatal(err)
-	}
-	if err := server.Serve(*publish, dispatcher); err != nil {
-		log.Fatal(err)
-	}
-	fmt.Println(endpoint)
-	<-signals.ShutdownOnSignals()
-}