Using Noise Protocol tunnels in Syndicate systems

The Syndicate ecosystem includes a few ways of gluing together dataspaces over a network.

To begin with, there was no authentication or authorization framework involved at all: programs just exchanged assertions and messages using an ancestor of the Syndicate protocol over a plain socket. There was no way of referring to entities/objects/actors at all: Syndicate at this point was data-only. (It turns out this is sufficient for a surprisingly large range of programs!)

Then, I figured out how to use ideas from Macaroons to introduce capabilities on the wire. This allows recovery of session-local object references from long-lived cryptographic names for use in a protocol session. Programs “upgrade” a name like

<ref {oid: a-service, sig: #[JTTGQeYCgohMXW/2S2XH8g]}>

to a real pointer they can use to send assertions and messages to.

Finally, I incorporated the Noise Protocol Framework, making it possible to use Syndicate to open encrypted, authenticated tunnels across untrusted links. The idea is to use a cleartext socket or websocket (or anything else!) to bootstrap communication, and then use assertions and messages related to Noise to open a Noise session across that foundation.

A small example

Here’s a small example from a real system I’m running.

I run a syndicate-server Docker container configured with, among other stanzas, the following service definition (these are fresh random keys, not the ones I’m using live):

let ?shared = dataspace
let ?sk = #[0LFU4CAWxrz0VVS9nY2uSc7EWJTusZUcOP0HUpkwSkE]
let ?pk = #[Y3NCy2ZPbmaH62fFI0sl0YOZmM7K7PhVTmRFmgCAgVA]
@<connect-using # <-- this is just a comment, really
  <route [<ws "ws://LOCATION.TO.MY.SERVER.WEBSOCKET/">]
   <noise {service: a-service key: #[Y3NCy2ZPbmaH62fFI0sl0YOZmM7K7PhVTmRFmgCAgVA]}>>
<bind <noise { service: a-service, key: $pk, secretKey: $sk }> $shared #f>

Then I can securely access the $shared dataspace from a remote syndicate-server instance using a stanza like this:

? <resolve-path
   <route [<ws "ws://LOCATION.TO.MY.SERVER.WEBSOCKET/">]
    <noise {service: a-service key: #[Y3NCy2ZPbmaH62fFI0sl0YOZmM7K7PhVTmRFmgCAgVA]}>>
   <accepted ?sharedDs>>
<the-shared-dataspace-is $sharedDs>

A lightly tricky piece of the puzzle is that the resolve-path handler isn’t built-in to syndicate-server yet: for that part, I’m using a tiny node.js service until I have a chance to implement the resolution logic in Rust. Watch this space.