npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bicycle-codes/link

v0.0.7

Published

Link multiple machiines via websocket.

Downloads

12

Readme

link

tests types module semantic versioning license

Link multiple devices via websocket. Linking means that both devices share the same AES key.

This depends on each device having a keystore that stores the private keys. Also, you need a websocket server, for example partykit.

We have two devices, a parent and a child. To securely send an AES key to another device, the parent first opens a websocket connection at a random URL. The URL for the websocket needs to be transmitted out-of-band.

When the new device (the child) connects to the websocket, it tells the parent its public key. The parent then encrypts its AES key to the child's public key. The child gets the key, which it is able to decrypt with its private key.

docs

See the docs page

install

npm i -S @bicycle-codes/link

use

import { Parent, Child } from '@bicycle-codes/link'
import type { Certificate, NewDeviceMessage } from '@bicycle-codes/link'

example

Connect two devices, a phone and computer, for example. They must both know code, which by default is a 6 digit numberic code. The code must be transmitted out of band.

import { program as Program } from '@oddjs/odd'
import { create as createID } from '@bicycle-codes/identity'
import { Parent } from '@bicycle-codes/link'

const program = await Program({
    namespace: {
        name: 'link-example',
        creator: 'bicycle-computing'
    }
})
const { crypto } = program.components

const myId = await createID(crypto, {
    humanName: 'alice',
    humanReadableDeviceName: 'phone'
})

/**
 * 'phone' is the parent device. The parent should connect first.
 * The resolved promise is for a new `Identity`, which is a new ID, including
 * the child device
 */
const newIdentity = await Parent(myId, crypto, {
    host: 'localhost:1999',
    code: '1234'
})

...On a different machine...

import { program as Program } from '@oddjs/odd'
import { Child } from '@bicycle-codes/link'

const program = await Program({
    namespace: {
        name: 'link-example',
        creator: 'bicycle-computing'
    }
})
const { crypto } = program.components

const { identity, certificate } = await Child(crypto, {
    host: PARTY_URL,
    code: '1234',
    humanReadableDeviceName: 'computer'
})

Both machines now have an ID that looks like this:

{
  "username": "vnhq32ybnanplsklhfd2cd6cdqaoeygl",
  "humanName": "alice",
  "rootDID": "did:key:z13V3Sog2YaU...",
  "devices": {
    "vnhq32ybnanplsklhfd2cd6cdqaoeygl": {
      "aes": "Cj1XnlPQA35VroF...",
      "name": "vnhq32ybnanplsklhfd2cd6cdqaoeygl",
      "humanReadableName": "phone",
      "did": "did:key:z13V3Sog2Y...",
      "exchange": "MIIBIjANBgkqhkiG..."
    },
    "5ngvlbhsrfvpua3qnhllakwnnd2tzwzo": {
      "name": "5ngvlbhsrfvpua3qnhllakwnnd2tzwzo",
      "humanReadableName": "computer",
      "aes": "oAbLoAtJawSbA3r2tI4BDEmb...",
      "did": "did:key:z13V3Sog2YaUKhdGCmg...",
      "exchange": "MIIBIjANBgkqhkiG9w0BAQEFA..."
    }
  }
}

serverside

This depends on a websocket server existing. We provide the export server to help with this.

This should be ergonomic to use with partykit.

server example

import type * as Party from 'partykit/server'
import { onConnect, onMessage } from '@bicycle-codes/link/server'

export default class Server implements Party.Server {
    existingDevice:string|undefined

    constructor (readonly room: Party.Room) {
        this.room = room
    }

    /**
     * Parent device must connect first
     */
    onConnect (conn:Party.Connection) {
        onConnect(this, conn)
    }

    onMessage (message:string, sender:Party.Connection) {
        onMessage(this, message, sender)
    }
}

Server satisfies Party.Worker

API

Parent

Call this from the "parent" device. It returns a promise that will resolve with a new identity, that includes the child devices.

import type { Crypto, Identity } from '@bicycle-codes/identity'

async function Parent (identity:Identity, oddCrypto:Crypto, {
    host,
    code,
    query
}:{
    host:string;
    code:string;
    query?:string;
}):Promise<Identity>

Child

Call this from the "child" device. It returns a promise that will resolve with { identity, certificate }, where certificate is a signed message from the parent device, serving as proof that the child is authorized.

import type { Crypto, Identity } from '@bicycle-codes/identity'

async function Child (oddCrypto:Crypto, {
    host,
    code,
    query,
    humanReadableDeviceName
}:{
    host:string;
    code:string;
    query?:string;
    humanReadableDeviceName:string;
}):Promise<{ identity:Identity, certificate:Certificate }>

Code

Need to create a code before connecting the parent device. The code should be transmitted out-of-band; it serves as verification that the two devices want to connect.

By default this will create a random 6 digit numeric code. Internally we are using nanoid to create the code.

To create your own code, use the nanoid-dictionary package.

function Code (alphabet?:string, length?:number):string {
    return customAlphabet(alphabet || numbers, length ?? 6)()
}

Code example

import { Code } from '@bicycle-codes/link'
const code = Code()
// => 942814

create your own random code

Pass in a dictionary and the desired length of the code.

import { Code } from '@bicycle-codes/link'
import { alphanumeric } from 'nanoid-dictionary'

function myCodeGenerator () {
    // return a 10 character, alphanumeric random code
    return Code(alphanumeric, 10)
}

types

Certificate

The certificate is a signed message from the "parent" device, saying that the new device is authorized.

import { create as createMessage } from '@bicycle-codes/message'

type Certificate = Awaited<
    ReturnType<typeof createMessage<{
        exp?:number;  /* <-- Expiration, unix timestamp,
            after which this certificate is no longer valid.
            Default is no expiration. */
        nbf?:number  /* <-- Not Before, unix timestamp of when the certificate
            becomes valid. */
        recipient:DID  // <-- DID of who this certificate is intended for
    }>>
>

NewDeviceMessage

A message from the new, "child" device

export type NewDeviceMessage = {
    newDid:`did:key:z${string}`;  // <-- DID for the new device
    deviceName:string;  // <-- the auto generated random string
    exchangeKey:string;
    humanReadableDeviceName:string;  // <-- a name for the new device
}

The certificate will also have keys author and signature, via the message module, with the DID and signature for this data.