The Vanadium naming system enables discovery of devices, regardless of their physical locations - with or without an internet connection.
Vanadium names, usually abbreviated to "names" refer to objects.
Objects implement RPC methods. In other words, methods are invoked on object names. The basic primitive is thus:
name.method(args) -> results
For example, if the name
/host:8080/a/y/foo.jpg represents a JPEG file, then
/host:8080/a/y/foo.jpg.Get() will return the contents of that file.
Object names are hierarchical consisting of components separated by slashes (/). Glob patterns, as on Unix, are restricted to matching name components.
Object names are resolved to obtain an object address. The underlying RPC protocol uses object addresses to establish communication with the process containing the named object prior to invoking methods on the object. The service that implements name resolution consists of servers, called mount tables, and a client library called the namespace library.
Mount tables and namespaces
Mount tables are similar to DNS servers and the namespace library to the
DNS resolver. Similar to DNS and the Unix filesystem, mount
tables may be arranged in layered hierarchies. The resolution process
implemented by the namespace library may iteratively communicate with multiple
mount tables to resolve a single name. This is illustrated in the diagram
below, which shows 6 mount tables.
ns1.v.io:8101 is the root and
mount table a,
mount table b and
mount table c are mounted in it as
mount table y and
mount table z are mounted in
mount table a as
z. To resolve the name
a/y, the mount tables on
mount table a must be consulted. To resolve the name
mount table a/y must also be consulted, since it serves
mounts made below
The first element of a name that begins with
/ points to the mount table at
which to begin the resolution. For example, the name
resolution with the mount table
ns1.v.io:8101. These names are called rooted
because they need no additional state to perform the resolution, they stand by
Names that don't begin with a
/ begin the resolution at a default (or
current) mount table set in the process' namespace library. We
call those names relative because they need the state of the namespace
library to determine how they are resolved.
This is illustrated below, where process
Client 1 has a namespace
that is relative to
ns1.v.io:8101, its root, and hence can resolve
In contrast, process
Client 2 with
ns2.v.io:8102 as its root can
The advantage of using a relative namespace is that it can define a context. For example, for debugging or testing one would set up the namespace to contain the emulated environment. It is similarly possible to provide context of nearby devices, or one local to a single machine.
Rooted names can be used to specify an arbitrary server where name resolution
will begin, thus overriding the root of the namespace asked to perform the
resolution. This would allow
Client 2 to refer to
The element immediately after the leading slash must be an address, either in
ipv4:port format, or in the Vanadium
endpoint format that encodes more detailed information about the
server supporting the object (such as protocol versions or a globally unique
id). In all cases they provide a starting point in the forest of
namespaces. The object addressed by that element can be another mount table or
the terminal server.
In essence, both rooted and relative names are the same. They represent a walk through a namespace that consists of a directed cyclic graph of mount tables ending in a server of the object we're trying to get at. The only difference with relative names is that we're assuming a "current directory" to start from as opposed to providing it in the name.
User-supplied server code that implements RPCs also appears as names in mount
tables. In the diagram above
Server 1 and
Server 2 provide a server called
srv that can be accessed as
/ns1.v.io:8101/srv, which will
resolve to the server hosted by
Server 1 and
which will resolve to the server hosted by
Server 2. The mount table at
ns1.v.io:8101 contains the entry for
Server 1 (as
srv) and the
mount table at
mount table a contains the entry for
Server 2 (also called
will result in
Server 1 serving that method, whereas
/ns1.v.io:8101/a/srv.Get() will be served by
The name resolution process is iterative. For
Client 1 to resolve
it will first ask
ns1.v.io:8101 to resolve
will reply with
mount table a's address and
y/bar as the "unresolved"
portion of the name.
Client 1 will then ask
mount table a to resolve
y/bar, and so on.
If the resolution reaches a leaf server (i.e. one that isn't
a mount table such as
Serve 1 or
Server 2) and there
are still remaining components to the name, those
"unresolved" components will be passed to the leaf server along with the operation.
Thus the call
/ns1.v.io:8101/srv/foo/bar.Get() will result in
Server 1 receiving
It is possible to create cycles by creating mount tables that mount themselves through other mount tables. Cycles are handled by limiting the number of iterations the resolution algorithm will execute. That is, regardless of whether cycles are present, the resolution algorithm will bound the number of iterations it executes.
Our examples above have shown mount tables pointing to other mount tables or to leaf servers but we haven't said what those pointers are. They are a set of equivalent rooted names. They either specify different addresses for the same server reachable via different networks (for example IP, IPv6, and Bluetooth), or they specify addresses for servers that are themselves equivalent either because they are stateless or because they transparently synchronize their state. Thus, we can send an RPC to whichever one is available and that we can reach.
Normally these rooted names point to the root of the server's namespace and that
is what we have shown in our examples. However, you can also mount objects
further inside the server's namespace. In the example above, we could have
/foo/bar onto the name
/ns1.v.io:8101/quux. In that
/ns1.v.io:8101/quux would resolve
to the same object. You can use this to create aliases or nicknames for objects,
or to create namespaces that represent some context (like all left handed green
eyed poker players) without creating a specific server to provice such a