-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MDNS
integration and NodeGraph
structure expansion
#537
Comments
Also that node addresses can now have both local node addresses and global node addresses. In the mainnet/testnet, local addresses shouldn't be given out, as that leaks local network environment and could be a security risk. However when connecting to a PKE portal, local network addresses could be shared, just like in tailscale. In that case, it might be part of the bootstrapping configuration of PK nodes in a private network, where they can be enabled to share those local addresses. Even when it is shared, receiving nodes need to intelligently decide and pick out which address to use to connect. In some cases, even in a LAN, they still cannot connect to the LAN address because they are not part of the same LAN. So in some cases, there may need to be concurrent/parallel attempts to the addresses, the node graph should then remember which ones worked and which ones didn't and sort addresses by concurrent priority. |
Some context regarding |
I tried out enabling LAN loopback, and I have an empirical sense of how it works now. Most routers do not have it enabled, and many routers don't even have this feature at all. When it is enabled, it may only be enabled on specific port forwarding rules (maybe port triggering rules), and not for the entire network. The basic idea is that when it is enabled it is possible to use the external IP address while you're in the local network. For example if you have your router forwarding
If you don't have a service listening If loopback isn't enabled, you'd also get a connection refused or a timeout. Basically this feature is primarily for convenience. It means that while you're in the local network, you can use the external IP to access internal services. It's convenient because if you are sharing scripts between others, you don't have to remember to swap the IP address from internal to external or vice versa when running the scripts from an external location or from a local location. I asked myself why is this not on by default? It seems like a convenient feature. The only security issue I discovered is mentioned here on SO: Anyway this means we generally have to assume that this feature does not exist or does not work and not rely on it. It appears most organisations (that need to expose an internal service over NAT) rely on split DNS to deal with this problem, and instead use a hostname that resolves to an external IP or local IP depending on the situation. What does this mean for the NodeGraph? Basically for P2P to work between agents on the local network, it is necessary for us to expand our This means MDNS is necessary to use multicast to find other nodes on the same local network. Multicast of course is also subject to the local network rules that the router has, if it disables multicast, then multicast becomes broken too. Finally there's also broadcast... but if multicast is disabled, most likely broadcast is disabled too. The idea is that the nodegraph will have multiple IPs that are potential IPs of the same node. To prevent leaking internal network information, one has to add classification rules (categorisation or tagging) to the node graph entries. So that internal address entries are only shared with other nodes that are on the same internal network. This can be done by inspecting and comparing the source-IP of any RPC requests. This is a fairly minimal security risk... and for private PK networks, it's fine to tell the root nodes what the internal addresses are. We can observe this behaviour in tailscale, where all nodes advertise their internal network to tailscale too. But in our public mainnet and testnet, we would not want to do this. Another thing... we might want to also expand our address types to include non-network addresses. Let's consider the generic idea of a "beacon" or "lighthouse" as a rendezvous point where nodes can discover each other. There are multiple layers of rendezvous points, each one increasing its scope of operations.
Given our desire to create a decentralised signalling and relaying system, all rendezvous point is just a "starting" point, subsequent signalling and relaying should rely on the network itself to help scale it. We can call this a "self-scaling" technology. |
I remember in a conversation we had earlier in the year I also remember one of us saying that |
It's like classification tiers. There may actually be more tiers than that, so I was thinking of adding a classification label into the node graph structure some how. Just make a quick solution for the launch, then new issue to rearchitect it properly. But try not to build too much technical debt. |
I remember at the very least we have:
Right now the goal is for MDNS to cover 1 and 2, since kademlia can't discover 1 and 2. Note that 1 should actually be found via also a filesystem discovery using like a common directory location like |
the WIP data structure of NodeData and NodeAddress: type NodeAddressScope = 'local' | 'external';
type NodeAddress = {
host: Host | Hostname;
port: Port;
scopes: Array<NodeAddressScope>;
};
type NodeData = {
addresses: Array<NodeAddress>;
lastUpdated: number;
}; |
@amydevs can improve the spec by expanding this to include each network scenario, and how different parts of nodes domain and MDNS is involved in achieving it.
I'm interested in seeing what exactly is used to deal with the fact that an agent can have multiple valid IPs (and/or hostnames). |
This is a key feature of the MatrixAI/Polykey-CLI#40 at the very least new users should be able to discover their local nodes on the same computer or local area network and share a vault between them. This has some interaction with MatrixAI/Polykey-CLI#30 if it finds out that the other local node is part of another gestalt. |
So node graph expansion is being done separately, we start with just MDNS being its own separate state. And we have to work out and model what happens with an expanded node graph. |
Removing this from the #551 because this will require further work later down the line. |
Stage 2 isn't part of CLI beta launch. So we can address it later. I believe we need to test local discovery too. But further work on this can be paused until afterwards. |
#611 fixed up getting all node records, atm it does not fetch MDNS records. This is because MDNS is a separate data source of nodes, it does not feed into the nodegraph yet. We will need to re-design the nodegraph data structure to support more sophisticated understanding of nodes. |
It should be local plus global or internal vs external. Right now the naming doesn't quite make sense. Also if something is local and external is understood that it will be shared but not require hole punching? I'm not sure if this is a good way of doing this. To really understand scopes, we need to think of it either as set intersection or set union. If an address is Being an array is just for convenient storage, it actually needs to be a set to avoid duplicate labels. I'm biased towards making it a set union. So that if an address is local and global, it just means that the address is locally routable and globally routable. I don't think it makes sense to add the semantics of holepunching here. Although @tegefaulkes has mentioned in #365 that it would be useful if it was possible to share information about whether a node requires hole punching or not, I don't have a concrete formulation for how that's supposed to work atm and I dislike the idea of just adding The real purpose of If it is set union, then we need to understand that an address that is local only, then there's no point sharing it across the network. But if it possesses the global then it should be shared. But if it is global only, and not local, then it shouldn't be shared across MDNS. ATM because MDNS data isn't even merged into the node graph, then this isn't even possible yet. Future scopes could expand this further into things like:
And also how this would work in private networks in PKE, as well as how this might work if PK were to be able to connect to multiple networks at the same time, thus creating segregated networks. Imagine |
I don't like having a set as part of the
Since it's just a number it's much easier to store or pass around. |
It's already stored as an array of strings. I think it's fine to keep it as is, I don't think storage size is a huge problem here. If we use bit-vector we would need to use a number (due to JSON encoding). That would give us almost 64 possible labels. But I'm going to be potentially using arbitrary dynamic labels, so I don't want to change to using a fixed bitvector with a fixed meaning. |
@CMCDragonkai also think a set union would make sense, if an address is both locally and globally routable |
Holepunching notes: Roger Qiu — Today at 12:51 PM |
@CMCDragonkai renaming of scopes has been addressed in 4f0ae2a, more to come will be done in PRs
Holepunching logic has also been adpated to fit this ^ Furthermore, there has been an config option I have added to allow for a shorter timeout for |
I'm linking #618 (comment) because I was reviewing the
|
It might even be related to the problem at MatrixAI/Polykey-CLI#47. |
Based on this comment #618 (comment) I believe the initial usage of scopes should be fulfilled based on where the data came from/discovered. If it was discovered from MDNS it should be If it was discovered from both, it could be both. If the user provided it, then the user may specify the appropriate scope, but if not, it may default to So then an address would be default global and only get more restrictive? |
It should not present |
Seems like to finish this issue, the node should not be presenting |
We also need policy for culling old connection information. For example we keep separate entries for each ip-port combo. With NATs the port is highly ephemiral so each new connection will have a new port assigned to it (depending on spectific nat implementation). We've seen this in manual testing already where we're accumulating new ports in the |
This seems pretty much done? I think there are some small things to address still but we can make a new issue to address these and reduce the scope. |
Moving this to |
Right, so the main part of this is determining what kind of information to keep in the node graph. Essentially work out when and what information is OK to cull from the graph. To answer this we need to start with what kind information we have. The information is structured as a type NodeContactAddress = Opaque<'NodeContactAddress', string>;
type NodeContact = Record<NodeContactAddress, NodeContactAddressData>;
type NodeContactAddressData = {
/**
* Indicates how the contact address was connected on its
* last connection establishment. The ICE procedure concurrently
* uses all methods to try to connect, however, whichever one
* succeeded first should be indicated here. When sharing this
* information to other nodes, it can hint towards whether a
* contact does not require signalling or relaying.
*/
mode: 'direct' | 'signal' | 'relay';
/**
* Unix timestamp of when the connection was last active.
* This property should be set when the connection is first
* established, but it should also be updated as long as the
* connection is active.
*/
connectedTime: number;
/**
* Scopes can be used to classify the address.
* Multiple scopes is understood as set-union.
*/
scopes: Array<NodeAddressScope>;
};
|
Specification
With the creation of
MDNS
we need to integrate it's usage into Polykey. With that comes some considerations surroundingwhat connection information needs to be stored in the
NodeGraph
and what information needs to be shared between nodes.Right now the
NodeGraph
is based on the Kademlia spec and is pretty simple. It only stores a single Address for each node. A There are only 20 nodes per bucket and only the oldest active ones are stored. This is all based on the Kademlia spec.The
NodeGraph
logic needs to be expanded to allow for more information relating to each node to be stored. We need to storeIPv4
as well asIPv6
addresses. We need to track information about if the node is publicly contactable or needs to be hole punched. We need sticky nodes such as the seed nodes or user defined static information.Step 1 - MDNS Sidecar onto NodeConnectionManager and NodeManager
I've decided as the simplest solution, MDNS can be integrated into methods of NodeConnectionManager and NodeManager related to discovery, like
findNode
, etc. This essentially allows NodeConnectionManager and NodeManager use both MDNS and NodeGraph as discovery mechanisms independent of each other.Note: MDNS should not be integrated into
findNode
directly, but as a separate method to be called by the callers offindNode
in non-kademllia contexts.The NodeGraph related data structures will still have to be edited like so to facilitate the separation of MDNS addresses and NodeGraph addresses:
This will mean that there will be overlapping NodeData that exists on both the NodeGraph and MDNS, this will have to be dealt with.
The methods on NCM:
getConnectionWithAddress
andgetConnection
will need to be amended to support multipleNodeAddress
es as input. This isn't the ideal way to do this, but it will have to be like this until the NodeGraph itself is made to support storing multiple IP addresses in Step 2. Much of the flow will need to be rewritten to support this change.Scopes
Currently in Step 1,
scopes
will only be used for disabling holepunching onlocal
addresses. An address can be eitherlocal
,external
, or both. Local means that the address is locally routable, so holepunching is not required when connecting to that address. External means the that the address is gliobally routable, but will require holepunching to establish a connection.Scopes aren't just for disabling signaling or not, they are also used for the purpose of classifying IP addresses into different levels. This means that in future, when scopes are incorporated as part of the nodegraph, we can specify for only external addresses to be shared on the public mainnet.
Being able to define scopes as an array also means that i address could have multiple classifications, which would be useful in the future if we ever needed it expanded. Also addresses with both
local
andexternal
will be understood to be shared on the mainnet, but not require holepunching to be connected to.Custom Group Address and Port
We're going to need to select a port and ipv4 + ipv6 groups for now, as to not interfere with existing MDNS stacks. However, as native integration of MDNS improves, this may eventually no longer be needed.
I've decided to take inspiration from our slogan
fait accompli
, to choose these addresses:224.0.0.250
ff02::fa17
64023
250
is decimal representation forfa
fa17
looks likefait
infait accompli
64023
is the decimal representation offa17
All these Values have been checked against IANA to be not of conflict with any existing reservations.
Step 2 - Full Integration of MDNS to Supply Data to NodeGraph
The NodeGraph is expanded to support multiple IPv4 and IPv6 addresses.
This will allow us to integrate MDNS into the NodeGraph, so that it is used as the central Node address book.
This might require alot of API changes. We'll see if we can get there in time.
Additional context
js-quic
integration and Agent migration #525MDNS
integration andNodeGraph
structure expansion #537Tasks
MDNS
and it's usage into initial network entry and discovery.NodeConnectionManager.findNode
NodeGraph
functionality needs to be expanded to handle connection metadata, support IPv6 and general policy for garbage collection, data updating and data sharing policy.NodeManager.start
and be made a public function. It's usage should be called inPolykeyAgent
when starting. We should make it optional for testing.[ ] Integrate MDNS querying intoNodeManager.syncNodeGraph
[ ] Integrate MDNS querying intoNodeManager.getNodeAddress
[ ] Integrate MDNS querying intoNodeManager.setNode
NodeManager.findNode
- this was moved out from NCM into NM now.The text was updated successfully, but these errors were encountered: