How can I send and receive private messages with js-ipfs-api?

How can I expand the ipfs-js-api to use the HTTP API path /p2p/stream/dial or /p2p/listener/open to send and receive private messages with? As you can see the /p2p path is not included in its implementation:

Currently this is where I am:

const pkg = require('../package.json');
const sendRequest = require('ipfs-api/src/utils/send-request')({
  'api-path': '/api/v0/',
  'user-agent': `/node-${pkg.name}/${pkg.version}/`,
  host: 'localhost',
  port: '5001',
  protocol: 'http'
});

sendRequest({path: '/p2p/stream/dial'}, (err, result) => {
  console.log(result);
});

Other questions I have are:

  1. Does ipfs.util.crypto supports pipable/pullable crypto streams with AES like Node’s createCipheriv, and if so how?
  2. How can I have an on ready event within the ipfs-js-api like the js-ipfs implementation has?
  3. Why does the js-ipfs node always have much less connections with swarm peers, compared with the go-ipfs node?
  4. Can I add a password to join a pubsub topic, or restrict it to a set of peer IDs?
  5. Can a node get its own public IP, like then ones you can get when you list swarm peers?

p2p…

The p2p feature is currently experimental and is undergoing a rewrite. You can manually send a request but the API will change in the near future. See: https://github.com/ipfs/go-ipfs/issues/4895

Other questions I have are:

3

Are you running it in a browser? If so, that’s likely because:

  1. Browser nodes can’t be directly dialed.
  2. Most non-browser nodes are go-ipfs nodes.
  3. go-ipfs nodes don’t listen on the websocket protocol (currently, by default) so most browser nodes can’t dial these nodes. The issue here is that most browser nodes run in an https “origin” so they can only make https websocket connections. Unfortunately, we can’t listen on websocket-over-https with a valid CA cert in go without shenanigans.

Can I add a password to join a pubsub topic, or restrict it to a set of peer IDs?

Not at the moment. We have some internal support for “validators” in go but this is a real issue that’s, unfortunately, hard to tackle.

Can a node get its own public IP, like then ones you can get when you list swarm peers?

It tries but doesn’t always succeed. Try running ipfs id to see what addresses your node thinks it has.

Unfortunately, I can’t answer the first two questions.

@stebalien Thanks : ) I am using Node.js, however my point is that I don’t know how can I send a manual request, I shared the code where I am stuck… I just need choose between stream/dial or listener/open, set the request arguments and set some sort of handler when I receive a private message…

If we can’t restrict access to pubsub topics that means that everyone knowing the name of the topic can come and attack my network by flooding the topic of messages and forcing my nodes to handle all these messages resulting in high CPU usage? Can the name of topic act as a password? I mean a random string that cannot be guessed…

On average I see 300/400 swarm peers, does that mean that in the world there are around 300/400 people using IPFS?

On average I see 300/400 swarm peers, does that mean that in the world there are around 300/400 people using IPFS?

It could be connection closing (either on the go side or the javascript side). I know go-ipfs has a connection manager that actively trims some unused connections. There have also, historically, been some communication issues between go-ipfs and js-ipfs so quite a few go-ipfs nodes simply can’t talk to js-ipfs (but that’s improving as we fix bugs and as the network upgrades).


As for the stream dialing, you’ll have to pass {path: ..., args: [ remotePeerID, yourProtocol]}. The server will return an multiaddr where it’s listening waiting for a TCP connection (which will be forwarded to the remote peer).

@stebalien If we can’t restrict access to pubsub topics that means that everyone knowing the name of the topic can come and attack my network by flooding the topic of messages and forcing my nodes to handle all these messages resulting in high CPU usage? Can the name of the topic act as a password? I mean a random string that cannot be guessed…

What is the protocol? Just a random string? How can I set a message handler in Node? I mean where do I get notified that a new private message has arrived and which function should I use to send a private message? Can I test this by sending a private message to myself? This is what I get:

Error: libp2p stream mounting not enabled

Code:

const pkg = require('../package.json');
const sendRequest = require('ipfs-api/src/utils/send-request')({
  'api-path': '/api/v0/',
  'user-agent': `/node-${pkg.name}/${pkg.version}/`,
  host: 'localhost',
  port: '5001',
  protocol: 'http'
});

const args = [
  'Qmb3Ubaa3XENjkeo1SwEXaW92efUzg2PbDeqzYu1J6NPVY',
  'test'
];

sendRequest({path: 'p2p/stream/dial', args: args}, (err, result) => {
  console.log(err);
  console.log(result);
});

This is a known issue: https://github.com/libp2p/go-floodsub/issues/26. It’s one of the reasons pubsub is still marked as experimental (in go-ipfs, at least).

What is the protocol? Just a random string?

Yes. Whatever you want to call it. At the moment, we’ll use /p2p/$yourString as the protocol.

How can I set a message handler in Node? I mean where do I get notified that a new private message has arrived and which function should I use to send a private message?

Take a look at: https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#ipfs-p2p

When you call ipfs p2p stream dial, IPFS will open a new stream to the peer and bind it to a single-use port on localhost (i.e., you can connect to this port once). The command will return the port go-ipfs used. You can then dial that address using TCP.

When you all ipfs p2p stream listen ..., go-ipfs will forward all inbound connections to /p2p/$yourProtocol to the IP/port you specify.

Note: this API is in flux and will change soon. We’re going to try to make it more like ssh -L.

@stebalien I read your links and suggestions but still doesn’t work, what’s wrong with the following code? What if I just use https://github.com/libp2p/js-libp2p with the go-ipfs daemon? Is is possible? Would it be better? What’s the connection between libp2p and the IPFS daemon? It only asks for a PeerInfo no API address or anything else… How can I make it work? I have already tryed to initialize libp2p with a peerinfo created with the peerid that ipfs.id returns but gives an error. I know it will change but I need to create a demo of a project because I need to present it to an event in a few weeks. This private messages are the last part I need to finalize everything, and potentially manage to build a big project on IPFS! ipfs-pubsub-room does this so flawlessly, but they do not support the API, I’d love to create something like that but for the API!

@whyrusleeping @daviddias I read that you guys are looking for demo requests for libp2p, I would really love to get a demo to solve the problem I am describing here, which is private messages. I bet it is a very common need and having a simple demo that does this using the ipfs-js-api would be a dream! One possibility that does not comprehend libp2p is sending public messages but encrypted with the public keys of the recepients. However, private messages would help me reduce the load on the node network, since much less messages would be broadcasted on the public topic.

const net = require('net');
const pkg = require('../package.json');

function sender() {
  const sendRequest = require('ipfs-api/src/utils/send-request')({
    'api-path': '/api/v0/',
    'user-agent': `/node-${pkg.name}/${pkg.version}/`,
    host: 'localhost',
    port: '5001',
    protocol: 'http'
  });

  const cfg = {
    path: 'p2p/stream/dial',
    args: [
      '<PeerID>',
      'test',
      '/ip4/127.0.0.1/tcp/10102'
    ]
  };

  sendRequest(cfg, (err, result) => {

    var client = net.connect({port: 10102}, function() {
      console.log('sender connected to port');

      setInterval(() => {
        client.write('Hello World from sender!\r\n');
      }, 2000);

      // client.on('data', function(data) {
      //   console.log(data.toString());
      //   client.end();
      // });

      client.on('end', function() {
        console.log('sender disconnected');
      });

    });

    console.log('SENDER ' + err)

  });
}

function receiver() {
  const sendRequest = require('ipfs-api/src/utils/send-request')({
    'api-path': '/api/v0/',
    'user-agent': `/node-${pkg.name}/${pkg.version}/`,
    host: 'localhost',
    port: '5002',
    protocol: 'http'
  });

  const cfg = {
    path: 'p2p/listener/open',
    args: [
      'test',
      '/ip4/127.0.0.1/tcp/10101'
    ]
  };

  sendRequest(cfg, (err, result) => {

    var client = net.connect({port: 10101}, function() {
      console.log('receiver connected to port!');

      client.on('data', function(data) {
        console.log('client says: ' + data.toString());
        //client.end();
      });

      client.on('end', function() {
        console.log('receiver disconnected');
      });

    });

    console.log('RECEIVER ' + err)

  });
}

receiver()
sender()