anchor.diff:          type = types.bool;
   	default = false;
           description = ''
  -          Enable this option to start a instance of the 
  -          cjdns network encryption and and routing engine.
  -          Configuration will be read from <literal>confFile</literal>.
  +          Whether to enable the cjdns network encryption
  +          and routing engine. A file at /etc/cjdns.keys will
  +          be created if it does not exist to contain a random
  +          secret key that your IPv6 address will be derived from.
           '';
         };
   
  -      confFile = mkOption {
  -	default = "/etc/cjdroute.conf";
  -        description = ''
  -          Configuration file to pipe to cjdroute.
  +      authorizedPasswords = mkOption {
  +        type = types.listOf types.str;
  +	default = [ ];
  +	example = [
  +          "snyrfgkqsc98qh1y4s5hbu0j57xw5s0"
  +	  "z9md3t4p45mfrjzdjurxn4wuj0d8swv"
  +	  "49275fut6tmzu354pq70sr5b95qq0vj"
  +        ];
  +	description = ''
  +	  Any remote cjdns nodes that offer these passwords on 
  +	  connection will be allowed to route through this node.
           '';
         };
  -
  -      /*
  +    
         admin = {
           bind = mkOption {
  +          type = types.string;
   	  default = "127.0.0.1:11234";
   	  description = ''
               Bind the administration port to this address and port.
   	  '';
           };
  -
  -	passwordFile = mkOption {
  -	  example = "/root/cjdns.adminPassword";
  +      };
  +
  +      UDPInterface = {
  +        bind = mkOption {
  +          type = types.string;
  +	  default = "";
  +          example = "192.168.1.32:43211";
  +          description = ''
  +	    Address and port to bind UDP tunnels to.
  +	  '';
  + 	};
  +        connectTo = mkOption {
  +          type = types.attrsOf ( types.submodule (
  +	    { options, ... }:
  +            { options = {
  +                # TODO make host an option, and add it to networking.extraHosts
  +                password = mkOption {
  +                  type = types.str;
  +                  description = "Authorized password to the opposite end of the tunnel.";
  +                };
  +                publicKey = mkOption {
  +                  type = types.str;
  +                  description = "Public key at the opposite end of the tunnel.";
  +                };
  +              };
  +            }
  +          ));
  +	  default = { };
  +          example = {
  +            "192.168.1.1:27313" = {
  +	      password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
  +              publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
  +            };
  +          };
  +          description = ''
  +	    Credentials for making UDP tunnels.
  +	  '';
  +	};
  +      };
  +
  +      ETHInterface = {
  +        bind = mkOption {
  +	  default = "";
  +	  example = "eth0";
   	  description = ''
  -	    File containing a password to the administration port.
  +	    Bind to this device for native ethernet operation.
  +	  '';
  +        };
  +
  +        beacon = mkOption {
  +	  type = types.int;
  +          default = 2;
  +          description = ''
  +            Auto-connect to other cjdns nodes on the same network.
  +            Options:
  +	      0: Disabled.
  +              1: Accept beacons, this will cause cjdns to accept incoming
  +                 beacon messages and try connecting to the sender.
  +              2: Accept and send beacons, this will cause cjdns to broadcast
  +                 messages on the local network which contain a randomly
  +                 generated per-session password, other nodes which have this
  +                 set to 1 or 2 will hear the beacon messages and connect
  +                 automatically.
  +          '';
  +        };
  +
  +        connectTo = mkOption {
  +          type = types.attrsOf ( types.submodule (
  +	    { options, ... }:
  +            { options = {
  +                password = mkOption {
  +                  type = types.str;
  +                  description = "Authorized password to the opposite end of the tunnel.";
  +                };
  +                publicKey = mkOption {
  +                  type = types.str;
  +                  description = "Public key at the opposite end of the tunnel.";
  +                };
  +              };
  +            }
  +          ));
  +	  default = { };
  +          example = {
  +            "01:02:03:04:05:06" = {
  +	      password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM";
  +              publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k";
  +            };
  +          };
  +	  description = ''
  +	    Credentials for connecting look similar to UDP credientials
  +            except they begin with the mac address.
   	  '';
   	};
         };
   
  -      keyFile = mkOption {
  -        type = types.str;
  -	example = "/root/cjdns.key";
  -	description = ''
  -	  Path to a file containing a cjdns private key on a single line.
  -	'';
  -      };
  -      
  -      passwordsFile = mkOption {
  -        type = types.str;
  -	default = null;
  -	example = "/root/cjdns.authorizedPasswords";
  -	description = ''
  -	  A file containing a list of json dictionaries with passwords.
  -	  For example:
  -	    {"password": "s8xf5z7znl4jt05g922n3wpk75wkypk"},
  -	    { "name": "nice guy",
  -	      "password": "xhthk1mglz8tpjrbbvdlhyc092rhpx5"},
  -	    {"password": "3qfxyhmrht7uwzq29pmhbdm9w4bnc8w"}
  -	  '';
  -	};
  -
  -      interfaces = {
  -        udp = {
  -	  bind = { 
  -            address = mkOption {
  -	      default = "0.0.0.0";
  -	      description = ''
  -	        Address to bind UDP tunnels to; disable by setting to null;
  -	      '';
  - 	    };
  -	    port = mkOption {
  -	      type = types.int;
  -	      default = null;
  -	      description = ''
  -	        Port to bind UDP tunnels to.
  -	        A port will be choosen at random if this is not set.
  -	        This option is required to act as the server end of 
  -	        a tunnel.
  -	      '';
  - 	    };
  -	  };
  -	};
  -
  -	eth = {
  -	  bind = mkOption {
  -	    default = null;
  -	    example = "eth0";
  -	    description = ''
  -	      Bind to this device and operate with native wire format.
  -	    '';
  -	  };
  -
  -	  beacon = mkOption {
  -	    default = 2;
  -	    description = ''
  -	      Auto-connect to other cjdns nodes on the same network.
  -	      Options:
  -	        0 -- Disabled.
  -
  -                1 -- Accept beacons, this will cause cjdns to accept incoming
  -		     beacon messages and try connecting to the sender.
  -
  -		2 -- Accept and send beacons, this will cause cjdns to broadcast
  -		     messages on the local network which contain a randomly
  -		     generated per-session password, other nodes which have this
  -                     set to 1 or 2 will hear the beacon messages and connect
  -                     automatically.
  -            '';
  -	  };
  -	  
  -	  connectTo = mkOption {
  -	    type = types.listOf types.str;
  -	    default = [];
  -	    description = ''
  -	      Credentials for connecting look similar to UDP credientials
  -              except they begin with the mac address, for example:
  -              "01:02:03:04:05:06":{"password":"a","publicKey":"b"}
  -	    '';
  -	  };
  -        };
  -      };
  -      */
  -    };
  +    };
  +
     };
   
     config = mkIf config.services.cjdns.enable {
   
       boot.kernelModules = [ "tun" ];
   
  -    /*
  -    networking.firewall.allowedUDPPorts = mkIf (cfg.udp.bind.port != null) [
  -      cfg.udp.bind.port
  -    ];
  -    */
  +    # networking.firewall.allowedUDPPorts = ...
   
       systemd.services.cjdns = {
         description = "encrypted networking for everybody";
         wantedBy = [ "multi-user.target" ];
  -      wants = [ "network.target" ];
  -      before = [ "network.target" ];
  -      path = [ pkgs.cjdns ];
  +      after = [ "network-interfaces.target" ];
  +
  +      script = ''
  +        source /etc/cjdns.keys
  +        echo '${cjdrouteConf}' | sed \
  +	  -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \
  +          -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \
  +            | ${pkgs.cjdns}/sbin/cjdroute
  +      '';
   
         serviceConfig = {
           Type = "forking";
  -	ExecStart = ''
  -          ${pkgs.stdenv.shell} -c "${pkgs.cjdns}/sbin/cjdroute < ${cfg.confFile}"
  -	'';
   	Restart = "on-failure";
         };
       };
  +
  +    system.activationScripts.cjdns = ''
  +      grep -q "CJDNS_PRIVATE_KEY=" /etc/cjdns.keys || \
  +        echo "CJDNS_PRIVATE_KEY=$(${pkgs.cjdns}/sbin/makekey)" \
  +	  >> /etc/cjdns.keys
  +
  +      grep -q "CJDNS_ADMIN_PASSWORD=" /etc/cjdns.keys || \
  +        echo "CJDNS_ADMIN_PASSWORD=$(${pkgs.coreutils}/bin/head -c 96 /dev/urandom | ${pkgs.coreutils}/bin/tr -dc A-Za-z0-9)" \
  +	  >> /etc/cjdns.keys
  +
  +      chmod 600 /etc/cjdns.keys
  +    '';
  +
  +    assertions = [
  +      { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" );
  +        message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined.";
  +      }
  +      { assertion = config.networking.enableIPv6;
  +        message = "networking.enableIPv6 must be enabled for CJDNS to work";
  +      }
  +    ];
  +
     };
  -}
  +
  +}
positive.code: 
    cfg = config.services.cjdns;
  
    /*
    # can't keep keys and passwords in the nix store,
    # but don't want to deal with this stdin quagmire.
  
    cjdrouteConf = '' {
      "admin": {"bind": "${cfg.admin.bind}", "password": "\${CJDNS_ADMIN}" },
      "privateKey": "\${CJDNS_KEY}",
  
      "interfaces": {
      ''
  
      + optionalString (cfg.interfaces.udp.bind.address != null) ''
        "UDPInterface": [ {
          "bind": "${cfg.interfaces.udp.bind.address}:"''
  	   ${if cfg.interfaces.upd.bind.port != null
               then ${toString cfg.interfaces.udp.bind.port}
  	     else ${RANDOM}
  	   fi)
        + '' } ]''
  
      + (if cfg.interfaces.eth.bind != null then ''
        "ETHInterface": [ {
          "bind": "${cfg.interfaces.eth.bind}",
          "beacon": ${toString cfg.interfaces.eth.beacon}
        } ]
      '' fi )
      + ''
      },
      "router": { "interface": { "type": "TUNInterface" }, },
      "security": [ { "setuser": "nobody" } ]
      }
      '';   
  
      cjdrouteConfFile = pkgs.writeText "cjdroute.conf" cjdrouteConf
      */
  in
  
  {
  
negative.code: { config, lib, pkgs, ... }:
  
  with lib;
  