One of the key words that describes capabilities is unforgeable.
A pointer in C is forgeable, because untrusted code could cast an integer to a pointer, thus forging access to whatever that pointer value points to.
MVP WebAssembly doesn't have unforgeable references, but what we can do instead is just use integer values which are indices into a table that's held outside the reach of untrusted code. The indices themselves are forgeable, but ultimately the table is the thing which holds the actual capabilities, and its elements are unforgeable. There's no way to gain access to a new resource by making up a new index.
When the reference-types proposal lands, references will be unforgeable, and will likely subsume the current integer-based APIs, at the WASI API layer.
There are two levels of capabilities that we can describe: static and dynamic.
The static capabilities of a wasm module are its imports. These essentially declare the set of "rights" the module itself will be able to request. An important caveat though is that this doesn't consider capabilities which may be passed into an instance at runtime.
The dynamic capabilities of a wasm module are a set of boolean values associated with a file descriptor, indicating individual "rights". This includes things like the right to read, or to write, using a given file descriptor.
It happens that integer indices representing capabilities is same thing that POSIX does, except that POSIX calls these indices file descriptors.
One difference though is that POSIX normally allows processes to request a file descriptor for any file in the entire filesystem hierarchy, which is granted based on whatever security policies are in place. This doesn't violate the capability model, but it doesn't take full advantage of it.
CloudABI, Fuchsia, and other capability-oriented systems prefer to take advantage of the hierarchical nature of the filesystem and require untrusted code to have a capability for a directory in order to access things inside that directory.
This way, you can launch untrusted code, and at runtime give it access to specific directories, without having to set permissions in the filesystem or in per-application or per-user configuration settings.
See this tutorial for an example of how this can look in practice.
Sockets aren't naturally hierarchical though, so we'll need to decide what capabilities look like. This is an area that isn't yet implemented.
In CloudABI, users launch programs with the sockets they need already created. That's potentially a starting point, which might be enough for simple cases.
We also anticipate an eventual extension to that, where we create a capability that represents a set of possible sockets that can be created. A set might be described by ranges of permitted ports, ranges of permitted addresses, or sets of permitted protocols. In this case the actual socket wouldn't be created until the application actually requests it.
CloudABI's intro to capability-based OS security provides additional background info:
https://github.com/NuxiNL/cloudabi#capability-based-security
The Fuchsia project has a blog post on the topic of capability-based OS security:
https://fuchsia.googlesource.com/docs/+/HEAD/the-book/dotdot.md