diff --git a/package.json b/package.json index 4515fc2584..c36b0ac005 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "release": "run-s build docs:no-publish npm:release docs", "npm:release": "aegir exec --bail false npm -- publish", "release:rc": "aegir release-rc", - "docs": "NODE_OPTIONS=--max_old_space_size=8192 aegir docs -- --exclude interop --exclude doc", - "docs:no-publish": "NODE_OPTIONS=--max_old_space_size=8192 aegir docs --publish false -- --exclude interop --exclude doc" + "docs": "aegir docs", + "docs:no-publish": "aegir docs --publish false -- --exclude interop --exclude doc" }, "devDependencies": { "aegir": "^41.0.2" diff --git a/packages/crypto/README.md b/packages/crypto/README.md index a1ab936c74..740c1e2074 100644 --- a/packages/crypto/README.md +++ b/packages/crypto/README.md @@ -1,5 +1,3 @@ -# @libp2p/crypto - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,45 +5,13 @@ > Crypto primitives for libp2p -## Table of contents - -- [Install](#install) - - [Browser ` ``` -This repo contains the JavaScript implementation of the crypto primitives needed for libp2p. This is based on this [go implementation](https://github.com/libp2p/go-libp2p-crypto). - -## Lead Maintainer - -[Jacob Heun](https://github.com/jacobheun/) - -## Usage - -```js -const crypto = require('libp2p-crypto') - -// Now available to you: -// -// crypto.aes -// crypto.hmac -// crypto.keys -// etc. -// -// See full API details below... -``` - -### Web Crypto API - -The `libp2p-crypto` library depends on the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) in the browser. Web Crypto is available in all modern browsers, however browsers restrict its usage to [Secure Contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). - -**This means you will not be able to use some `libp2p-crypto` functions in the browser when the page is served over HTTP.** To enable the Web Crypto API and allow `libp2p-crypto` to work fully, please serve your page over HTTPS. - -## API - -### `crypto.aes` - -Exposes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197. - -This uses `CTR` mode. - -#### `crypto.aes.create(key, iv)` - -- `key: Uint8Array` The key, if length `16` then `AES 128` is used. For length `32`, `AES 256` is used. -- `iv: Uint8Array` Must have length `16`. - -Returns `Promise<{decrypt, encrypt}>` - -##### `decrypt(data)` - -- `data: Uint8Array` - -Returns `Promise` - -##### `encrypt(data)` - -- `data: Uint8Array` - -Returns `Promise` - -```js -const crypto = require('libp2p-crypto') - -// Setting up Key and IV - -// A 16 bytes array, 128 Bits, AES-128 is chosen -const key128 = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) - -// A 16 bytes array, 128 Bits, -const IV = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) - -async function main () { - const decryptedMessage = 'Hello, world!' - - // Encrypting - const cipher = await crypto.aes.create(key128, IV) - const encryptedBuffer = await cipher.encrypt(Uint8Array.from(decryptedMessage)) - console.log(encryptedBuffer) - // prints: - - // Decrypting - const decipher = await crypto.aes.create(key128, IV) - const decryptedBuffer = await cipher.decrypt(encryptedBuffer) - - console.log(decryptedBuffer) - // prints: - - console.log(decryptedBuffer.toString('utf-8')) - // prints: Hello, world! -} - -main() -``` - -### `crypto.hmac` - -Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as defined in U.S. Federal Information Processing Standards Publication 198. An HMAC is a cryptographic hash that uses a key to sign a message. The receiver verifies the hash by recomputing it using the same key. - -#### `crypto.hmac.create(hash, secret)` - -- `hash: String` -- `secret: Uint8Array` - -Returns `Promise<{digest}>` - -##### `digest(data)` - -- `data: Uint8Array` - -Returns `Promise` - -Example: - -```js -const crypto = require('libp2p-crypto') - -async function main () { - const hash = 'SHA1' // 'SHA256' || 'SHA512' - const hmac = await crypto.hmac.create(hash, uint8ArrayFromString('secret')) - const sig = await hmac.digest(uint8ArrayFromString('hello world')) - console.log(sig) -} - -main() -``` - -### `crypto.keys` - -**Supported Key Types** - -The [`generateKeyPair`](#generatekeypairtype-bits), [`marshalPublicKey`](#marshalpublickeykey-type), and [`marshalPrivateKey`](#marshalprivatekeykey-type) functions accept a string `type` argument. - -Currently the `'RSA'`, `'ed25519'`, and `secp256k1` types are supported, although ed25519 and secp256k1 keys support only signing and verification of messages. For encryption / decryption support, RSA keys should be used. - -### `crypto.keys.generateKeyPair(type, bits)` - -- `type: String`, see [Supported Key Types](#supported-key-types) above. -- `bits: Number` Minimum of 1024 - -Returns `Promise<{privateKey, publicKey}>` - -Generates a keypair of the given type and bitsize. - -### `crypto.keys.generateEphemeralKeyPair(curve)` - -- `curve: String`, one of `'P-256'`, `'P-384'`, `'P-521'` is currently supported - -Returns `Promise` - -Generates an ephemeral public key and returns a function that will compute the shared secret key. - -Focuses only on ECDH now, but can be made more general in the future. - -Resolves to an object of the form: - -```js -{ - key: Uint8Array, - genSharedKey: Function -} -``` - -### `crypto.keys.keyStretcher(cipherType, hashType, secret)` - -- `cipherType: String`, one of `'AES-128'`, `'AES-256'`, `'Blowfish'` -- `hashType: String`, one of `'SHA1'`, `SHA256`, `SHA512` -- `secret: Uint8Array` - -Returns `Promise` - -Generates a set of keys for each party by stretching the shared key. - -Resolves to an object of the form: - -```js -{ - k1: { - iv: Uint8Array, - cipherKey: Uint8Array, - macKey: Uint8Array - }, - k2: { - iv: Uint8Array, - cipherKey: Uint8Array, - macKey: Uint8Array - } -} -``` - -### `crypto.keys.marshalPublicKey(key, [type])` - -- `key: keys.rsa.RsaPublicKey | keys.ed25519.Ed25519PublicKey | keys.secp256k1.Secp256k1PublicKey` -- `type: String`, see [Supported Key Types](#supported-key-types) above. Defaults to 'rsa'. - -Returns `Uint8Array` - -Converts a public key object into a protobuf serialized public key. - -### `crypto.keys.unmarshalPublicKey(buf)` - -- `buf: Uint8Array` - -Returns `RsaPublicKey|Ed25519PublicKey|Secp256k1PublicKey` - -Converts a protobuf serialized public key into its representative object. - -### `crypto.keys.marshalPrivateKey(key, [type])` - -- `key: keys.rsa.RsaPrivateKey | keys.ed25519.Ed25519PrivateKey | keys.secp256k1.Secp256k1PrivateKey` -- `type: String`, see [Supported Key Types](#supported-key-types) above. - -Returns `Uint8Array` - -Converts a private key object into a protobuf serialized private key. - -### `crypto.keys.unmarshalPrivateKey(buf)` - -- `buf: Uint8Array` - -Returns `Promise` - -Converts a protobuf serialized private key into its representative object. - -### `crypto.keys.import(encryptedKey, password)` - -- `encryptedKey: string` -- `password: string` - -Returns `Promise` - -Converts an exported private key into its representative object. Supported formats are 'pem' (RSA only) and 'libp2p-key'. - -### `privateKey.export(password, format)` - -- `password: string` -- `format: string` the format to export to: 'pem' (rsa only), 'libp2p-key' - -Returns `string` - -Exports the password protected `PrivateKey`. RSA keys will be exported as password protected PEM by default. Ed25519 and Secp256k1 keys will be exported as password protected AES-GCM base64 encoded strings ('libp2p-key' format). - -### `crypto.randomBytes(number)` - -- `number: Number` - -Returns `Uint8Array` - -Generates a Uint8Array with length `number` populated by random bytes. - -### `crypto.pbkdf2(password, salt, iterations, keySize, hash)` - -- `password: String` -- `salt: String` -- `iterations: Number` -- `keySize: Number` in bytes -- `hash: String` the hashing algorithm ('sha1', 'sha2-512', ...) - -Computes the Password Based Key Derivation Function 2; returning a new password. - -## Contribute +# Contribute Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-crypto/issues)! @@ -314,17 +27,17 @@ This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/c [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md) -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/crypto/src/aes/index.ts b/packages/crypto/src/aes/index.ts index cf1dd8b875..6a453cf074 100644 --- a/packages/crypto/src/aes/index.ts +++ b/packages/crypto/src/aes/index.ts @@ -1,3 +1,44 @@ +/** + * @packageDocumentation + * + * Exposes an interface to AES encryption (formerly Rijndael), as defined in U.S. Federal Information Processing Standards Publication 197. + * + * This uses `CTR` mode. + * + * /** + * @example + * + * ```js + * import { create } from '@libp2p/crypto/aes' + * + * // Setting up Key and IV + * + * // A 16 bytes array, 128 Bits, AES-128 is chosen + * const key128 = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) + * + * // A 16 bytes array, 128 Bits, + * const IV = Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) + * + * const decryptedMessage = 'Hello, world!' + * + * // Encrypting + * const cipher = await crypto.aes.create(key128, IV) + * const encryptedBuffer = await encrypt(Uint8Array.from(decryptedMessage)) + * console.log(encryptedBuffer) + * // prints: + * + * // Decrypting + * const decipher = await crypto.aes.create(key128, IV) + * const decryptedBuffer = await decrypt(encryptedBuffer) + * + * console.log(decryptedBuffer) + * // prints: + * + * console.log(decryptedBuffer.toString('utf-8')) + * // prints: Hello, world! + * ``` + */ + import { cipherMode } from './cipher-mode.js' import * as ciphers from './ciphers.js' @@ -6,6 +47,10 @@ export interface AESCipher { decrypt(data: Uint8Array): Promise } +/** + * @param key - The key, if length `16` then `AES 128` is used. For length `32`, `AES 256` is used + * @param iv - Must have length `16` + */ export async function create (key: Uint8Array, iv: Uint8Array): Promise { const mode = cipherMode(key) const cipher = ciphers.createCipheriv(mode, key, iv) diff --git a/packages/crypto/src/hmac/index.ts b/packages/crypto/src/hmac/index.ts index dc2ec68894..f61e3c28fc 100644 --- a/packages/crypto/src/hmac/index.ts +++ b/packages/crypto/src/hmac/index.ts @@ -1,3 +1,20 @@ +/** + * @packageDocumentation + * + * Exposes an interface to the Keyed-Hash Message Authentication Code (HMAC) as defined in U.S. Federal Information Processing Standards Publication 198. An HMAC is a cryptographic hash that uses a key to sign a message. The receiver verifies the hash by recomputing it using the same key. + * + * @example + * + * ```js + * import { create } from '@libp2p/hmac' + * + * const hash = 'SHA1' // 'SHA256' || 'SHA512' + * const hmac = await crypto.hmac.create(hash, uint8ArrayFromString('secret')) + * const sig = await hmac.digest(uint8ArrayFromString('hello world')) + * console.log(sig) + * ``` + */ + import crypto from 'crypto' import lengths from './lengths.js' diff --git a/packages/crypto/src/index.ts b/packages/crypto/src/index.ts index 9e368358eb..6396d1dbd4 100644 --- a/packages/crypto/src/index.ts +++ b/packages/crypto/src/index.ts @@ -1,3 +1,13 @@ +/** + * @packageDocumentation + * + * The `libp2p-crypto` library depends on the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) in the browser. Web Crypto is available in all modern browsers, however browsers restrict its usage to [Secure Contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). + * + * *This means you will not be able to use some `@libp2p/crypto` functions in the browser when the page is served over HTTP.* + * + * To enable the Web Crypto API and allow `@libp2p/crypto` to work fully, please serve your page over HTTPS. + */ + import * as aes from './aes/index.js' import * as hmac from './hmac/index.js' import * as keys from './keys/index.js' diff --git a/packages/crypto/src/keys/ecdh.ts b/packages/crypto/src/keys/ecdh.ts index 3e0fd30a75..992ee4777b 100644 --- a/packages/crypto/src/keys/ecdh.ts +++ b/packages/crypto/src/keys/ecdh.ts @@ -11,6 +11,11 @@ const curves = { const curveTypes = Object.keys(curves) const names = curveTypes.join(' / ') +/** + * Generates an ephemeral public key and returns a function that will compute the shared secret key. + * + * Focuses only on ECDH now, but can be made more general in the future. + */ export async function generateEphmeralKeyPair (curve: string): Promise { if (curve !== 'P-256' && curve !== 'P-384' && curve !== 'P-521') { throw new CodeError(`Unknown curve: ${curve}. Must be ${names}`, 'ERR_INVALID_CURVE') diff --git a/packages/crypto/src/keys/index.ts b/packages/crypto/src/keys/index.ts index d8192d5abb..f805d81afa 100644 --- a/packages/crypto/src/keys/index.ts +++ b/packages/crypto/src/keys/index.ts @@ -1,3 +1,15 @@ +/** + * @packageDocumentation + * + * **Supported Key Types** + * + * The {@link generateKeyPair}, {@link marshalPublicKey}, and {@link marshalPrivateKey} functions accept a string `type` argument. + * + * Currently the `'RSA'`, `'ed25519'`, and `secp256k1` types are supported, although ed25519 and secp256k1 keys support only signing and verification of messages. + * + * For encryption / decryption support, RSA keys should be used. + */ + import 'node-forge/lib/asn1.js' import 'node-forge/lib/pbe.js' import { CodeError } from '@libp2p/interface/errors' @@ -40,13 +52,21 @@ function typeToKey (type: string): typeof RSA | typeof Ed25519 | typeof Secp256k throw unsupportedKey(type) } -// Generates a keypair of the given type and bitsize +/** + * Generates a keypair of the given type and bitsize + * + * @param type + * @param bits - Minimum of 1024 + */ export async function generateKeyPair (type: KeyTypes, bits?: number): Promise { return typeToKey(type).generateKeyPair(bits ?? 2048) } -// Generates a keypair of the given type and bitsize -// seed is a 32 byte uint8array +/** + * Generates a keypair of the given type and bitsize. + * + * Seed is a 32 byte uint8array + */ export async function generateKeyPairFromSeed (type: KeyTypes, seed: Uint8Array, bits?: number): Promise { if (type.toLowerCase() !== 'ed25519') { throw new CodeError('Seed key derivation is unimplemented for RSA or secp256k1', 'ERR_UNSUPPORTED_KEY_DERIVATION_TYPE') @@ -55,8 +75,9 @@ export async function generateKeyPairFromSeed (type: KeyTypes, seed: Uint8Array, return Ed25519.generateKeyPairFromSeed(seed) } -// Converts a protobuf serialized public key into its -// representative object +/** + * Converts a protobuf serialized public key into its representative object + */ export function unmarshalPublicKey (buf: Uint8Array): PublicKey { const decoded = keysPBM.PublicKey.decode(buf) const data = decoded.Data ?? new Uint8Array() @@ -73,15 +94,18 @@ export function unmarshalPublicKey (buf: Uint8Array): PublicKey { } } -// Converts a public key object into a protobuf serialized public key +/** + * Converts a public key object into a protobuf serialized public key + */ export function marshalPublicKey (key: { bytes: Uint8Array }, type?: string): Uint8Array { type = (type ?? 'rsa').toLowerCase() typeToKey(type) // check type return key.bytes } -// Converts a protobuf serialized private key into its -// representative object +/** + * Converts a protobuf serialized private key into its representative object + */ export async function unmarshalPrivateKey (buf: Uint8Array): Promise { const decoded = keysPBM.PrivateKey.decode(buf) const data = decoded.Data ?? new Uint8Array() @@ -98,7 +122,9 @@ export async function unmarshalPrivateKey (buf: Uint8Array): Promise } } -// Converts a private key object into a protobuf serialized private key +/** + * Converts a private key object into a protobuf serialized private key + */ export function marshalPrivateKey (key: { bytes: Uint8Array }, type?: string): Uint8Array { type = (type ?? 'rsa').toLowerCase() typeToKey(type) // check type @@ -106,9 +132,9 @@ export function marshalPrivateKey (key: { bytes: Uint8Array }, type?: string): U } /** + * Converts an exported private key into its representative object. * - * @param {string} encryptedKey - * @param {string} password + * Supported formats are 'pem' (RSA only) and 'libp2p-key'. */ export async function importKey (encryptedKey: string, password: string): Promise { try { diff --git a/packages/crypto/src/random-bytes.ts b/packages/crypto/src/random-bytes.ts index a85542661c..5de614474b 100644 --- a/packages/crypto/src/random-bytes.ts +++ b/packages/crypto/src/random-bytes.ts @@ -1,6 +1,9 @@ import { CodeError } from '@libp2p/interface/errors' import { randomBytes as randB } from '@noble/hashes/utils' +/** + * Generates a Uint8Array with length `number` populated by random bytes + */ export default function randomBytes (length: number): Uint8Array { if (isNaN(length) || length <= 0) { throw new CodeError('random bytes length must be a Number bigger than 0', 'ERR_INVALID_LENGTH') diff --git a/packages/interface-compliance-tests/README.md b/packages/interface-compliance-tests/README.md index 906d059731..04270ac05e 100644 --- a/packages/interface-compliance-tests/README.md +++ b/packages/interface-compliance-tests/README.md @@ -1,5 +1,3 @@ -# @libp2p/interface-compliance-tests - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,22 +5,13 @@ > Compliance tests for JS libp2p interfaces -## Table of contents - -- [Install](#install) - - [Browser ` ``` -## Usage +# Usage Each [interface](https://npmjs.org/packages/@libp2p/interfaces) has its documentation on how to use the compliance tests and should be used as the source of truth. -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/interface-internal/README.md b/packages/interface-internal/README.md index 4d6b738f26..dafd72596c 100644 --- a/packages/interface-internal/README.md +++ b/packages/interface-internal/README.md @@ -1,5 +1,3 @@ -# @libp2p/interface-internal - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,30 +5,23 @@ > Interfaces implemented by internal libp2p components -## Table of contents - -- [Install](#install) -- [API Docs](#api-docs) -- [License](#license) -- [Contribution](#contribution) - -## Install +# Install ```console $ npm i @libp2p/interface-internal ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/interface/README.md b/packages/interface/README.md index 384bcb1b91..2ed3298f4a 100644 --- a/packages/interface/README.md +++ b/packages/interface/README.md @@ -1,5 +1,3 @@ -# @libp2p/interface - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,21 +5,13 @@ > The interface implemented by a libp2p node -## Table of contents - -- [Install](#install) - - [Browser ` ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/kad-dht/README.md b/packages/kad-dht/README.md index bd231f8c1d..e9fd6de682 100644 --- a/packages/kad-dht/README.md +++ b/packages/kad-dht/README.md @@ -1,5 +1,3 @@ -# @libp2p/kad-dht - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,28 +5,13 @@ > JavaScript implementation of the Kad-DHT for libp2p -## Table of contents - -- [Install](#install) - - [Browser ` -``` - -## Features +# About - Manages the lifecycle of a key - Keys are encrypted at rest @@ -43,7 +15,7 @@ Loading this module through a script tag will make it's exports available as `Li - Enforces NIST SP 800-131A and NIST SP 800-132 - Delays reporting errors to slow down brute force attacks -### KeyInfo +## KeyInfo The key management and naming service API all return a `KeyInfo` object. The `id` is a universally unique identifier for the key. The `name` is local to the key chain. @@ -54,9 +26,11 @@ The key management and naming service API all return a `KeyInfo` object. The `i } ``` -The **key id** is the SHA-256 [multihash](https://github.com/multiformats/multihash) of its public key. The *public key* is a [protobuf encoding](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/keys.proto.js) containing a type and the [DER encoding](https://en.wikipedia.org/wiki/X.690) of the PKCS [SubjectPublicKeyInfo](https://www.ietf.org/rfc/rfc3279.txt). +The **key id** is the SHA-256 [multihash](https://github.com/multiformats/multihash) of its public key. -### Private key storage +The *public key* is a [protobuf encoding](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/keys.proto.js) containing a type and the [DER encoding](https://en.wikipedia.org/wiki/X.690) of the PKCS [SubjectPublicKeyInfo](https://www.ietf.org/rfc/rfc3279.txt). + +## Private key storage A private key is stored as an encrypted PKCS 8 structure in the PEM format. It is protected by a key generated from the key chain's *passPhrase* using **PBKDF2**. @@ -64,7 +38,7 @@ The default options for generating the derived encryption key are in the `dek` o ```js const defaultOptions = { - //See https://cryptosense.com/parameter-choice-for-pbkdf2/ +// See https://cryptosense.com/parameter-choice-for-pbkdf2/ dek: { keyLength: 512 / 8, iterationCount: 1000, @@ -76,9 +50,25 @@ const defaultOptions = { ![key storage](./doc/private-key.png?raw=true) -### Physical storage +## Physical storage + +The actual physical storage of an encrypted key is left to implementations of [interface-datastore](https://github.com/ipfs/interface-datastore/). + +A key benefit is that now the key chain can be used in browser with the [js-datastore-level](https://github.com/ipfs/js-datastore-level) implementation. + +# Install + +```console +$ npm i @libp2p/keychain +``` + +## Browser ` +``` ## API Docs diff --git a/packages/keychain/src/index.ts b/packages/keychain/src/index.ts index f13cc0367f..8507c4c7bf 100644 --- a/packages/keychain/src/index.ts +++ b/packages/keychain/src/index.ts @@ -1,3 +1,55 @@ +/** + * @packageDocumentation + * + * - Manages the lifecycle of a key + * - Keys are encrypted at rest + * - Enforces the use of safe key names + * - Uses encrypted PKCS 8 for key storage + * - Uses PBKDF2 for a "stetched" key encryption key + * - Enforces NIST SP 800-131A and NIST SP 800-132 + * - Delays reporting errors to slow down brute force attacks + * + * ## KeyInfo + * + * The key management and naming service API all return a `KeyInfo` object. The `id` is a universally unique identifier for the key. The `name` is local to the key chain. + * + * ```js + * { + * name: 'rsa-key', + * id: 'QmYWYSUZ4PV6MRFYpdtEDJBiGs4UrmE6g8wmAWSePekXVW' + * } + * ``` + * + * The **key id** is the SHA-256 [multihash](https://github.com/multiformats/multihash) of its public key. + * + * The *public key* is a [protobuf encoding](https://github.com/libp2p/js-libp2p-crypto/blob/master/src/keys/keys.proto.js) containing a type and the [DER encoding](https://en.wikipedia.org/wiki/X.690) of the PKCS [SubjectPublicKeyInfo](https://www.ietf.org/rfc/rfc3279.txt). + * + * ## Private key storage + * + * A private key is stored as an encrypted PKCS 8 structure in the PEM format. It is protected by a key generated from the key chain's *passPhrase* using **PBKDF2**. + * + * The default options for generating the derived encryption key are in the `dek` object. This, along with the passPhrase, is the input to a `PBKDF2` function. + * + * ```js + * const defaultOptions = { + * // See https://cryptosense.com/parameter-choice-for-pbkdf2/ + * dek: { + * keyLength: 512 / 8, + * iterationCount: 1000, + * salt: 'at least 16 characters long', + * hash: 'sha2-512' + * } + * } + * ``` + * ![key storage](./doc/private-key.png?raw=true) + * + * ## Physical storage + * + * The actual physical storage of an encrypted key is left to implementations of [interface-datastore](https://github.com/ipfs/interface-datastore/). + * + * A key benefit is that now the key chain can be used in browser with the [js-datastore-level](https://github.com/ipfs/js-datastore-level) implementation. + */ + /* eslint max-nested-callbacks: ["error", 5] */ import { pbkdf2, randomBytes } from '@libp2p/crypto' diff --git a/packages/libp2p/README.md b/packages/libp2p/README.md index 28dca9a77e..8e2c9bb35d 100644 --- a/packages/libp2p/README.md +++ b/packages/libp2p/README.md @@ -1,48 +1,3 @@ -# libp2p - -[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) -[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) -[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) -[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p/main.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p/actions/workflows/main.yml?query=branch%3Amaster) - -> JavaScript implementation of libp2p, a modular peer to peer network stack - -## Table of contents - -- [Install](#install) - - [Browser ` -``` -

libp2p hex logo

@@ -71,7 +26,9 @@ Loading this module through a script tag will make it's exports available as `Li

-### Project status +> JavaScript implementation of libp2p, a modular peer to peer network stack + +# Project status We've come a long way, but this project is still in Alpha, lots of development is happening, API might change, beware of the Dragons 🐉.. @@ -82,7 +39,7 @@ If you are looking for the documentation of the latest release, you can view the **Want to update libp2p in your project?** Check our [migrations folder](./doc/migrations). -## Background +# Background libp2p is the product of a long and arduous quest to understand the evolution of the Internet networking stack. In order to build P2P applications, devs have long had to make custom ad-hoc solutions to fit their needs, sometimes making some hard assumptions about their runtimes and the state of the network at the time of their development. Today, looking back more than 20 years, we see a clear pattern in the types of mechanisms built around the Internet Protocol, IP, which can be found throughout many layers of the OSI layer system, libp2p distils these mechanisms into flat categories and defines clear interfaces that once exposed, enable other protocols and applications to use and swap them, enabling upgradability and adaptability for the runtime, without breaking the API. @@ -99,7 +56,7 @@ We are in the process of writing better documentation, blog posts, tutorials and To sum up, libp2p is a "network stack" -- a protocol suite -- that cleanly separates concerns, and enables sophisticated applications to only use the protocols they absolutely need, without giving up interoperability and upgradeability. libp2p grew out of IPFS, but it is built so that lots of people can use it, for lots of different projects. -## Roadmap +# Roadmap The js-libp2p roadmap can be found here: @@ -107,25 +64,25 @@ It represents current projects the js-libp2p maintainers are focused on and prov It is complementary to the overarching libp2p project roadmap: -## Usage +# Usage ### Configuration For all the information on how you can configure libp2p see [CONFIGURATION.md](./doc/CONFIGURATION.md). -### Limits +## Limits For help configuring your node to resist malicious network peers, see [LIMITS.md](./doc/LIMITS.md) -### Getting started +## Getting started If you are starting your journey with `js-libp2p`, read the [GETTING\_STARTED.md](./doc/GETTING_STARTED.md) guide. -### Tutorials and Examples +## Tutorials and Examples You can find multiple examples on the [examples repo](https://github.com/libp2p/js-libp2p-examples) that will guide you through using libp2p for several scenarios. -## Development +# Development **Clone and install dependencies:** @@ -136,9 +93,9 @@ You can find multiple examples on the [examples repo](https://github.com/libp2p/ > npm run build ``` -### Tests +# Tests -#### Run unit tests +## Run unit tests ```sh # run all the unit tsts @@ -151,7 +108,7 @@ You can find multiple examples on the [examples repo](https://github.com/libp2p/ > npm run test:chrome ``` -### Packages +# Packages List of packages currently in existence for libp2p @@ -195,7 +152,7 @@ List of packages currently in existence for libp2p | [`@ChainSafe/libp2p-gossipsub`](//github.com/ChainSafe/js-libp2p-gossipsub) | [![npm](https://img.shields.io/npm/v/%40ChainSafe%2Flibp2p-gossipsub.svg?maxAge=86400\&style=flat-square)](//github.com/ChainSafe/js-libp2p-gossipsub/releases) | [![Deps](https://img.shields.io/librariesio/release/npm/%40ChainSafe%2Flibp2p-gossipsub?logo=Libraries.io\&logoColor=white\&style=flat-square)](//libraries.io/npm/%40ChainSafe%2Flibp2p-gossipsub) | [![GitHub CI](https://img.shields.io/github/actions/workflow/status/ChainSafe/js-libp2p-gossipsub/main.yml?branch=master\&label=ci\&style=flat-square)](//github.com/ChainSafe/js-libp2p-gossipsub/actions?query=branch%3Amaster+workflow%3Aci+) | [![codecov](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/ChainSafe/js-libp2p-gossipsub) | | [`@libp2p/floodsub`](//github.com/libp2p/js-libp2p-floodsub) | [![npm](https://img.shields.io/npm/v/%40libp2p%2Ffloodsub.svg?maxAge=86400\&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/releases) | [![Deps](https://img.shields.io/librariesio/release/npm/%40libp2p%2Ffloodsub?logo=Libraries.io\&logoColor=white\&style=flat-square)](//libraries.io/npm/%40libp2p%2Ffloodsub) | [![GitHub CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-floodsub/js-test-and-release.yml?branch=master\&label=ci\&style=flat-square)](//github.com/libp2p/js-libp2p-floodsub/actions?query=branch%3Amaster+workflow%3Aci+) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-floodsub/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-floodsub) | -## Used by +# Used by

@@ -207,7 +164,7 @@ List of packages currently in existence for libp2p And [many others...](https://github.com/libp2p/js-libp2p/network/dependents) -## Contribute +# Contribute The libp2p implementation in JavaScript is a work in progress. As such, there are a few things you can do right now to help out: @@ -215,17 +172,17 @@ The libp2p implementation in JavaScript is a work in progress. As such, there ar - **Perform code reviews**. Most of this has been developed by @diasdavid, which means that more eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs. - **Add tests**. There can never be enough tests. -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/logger/README.md b/packages/logger/README.md index def89c67a1..ce77232376 100644 --- a/packages/logger/README.md +++ b/packages/logger/README.md @@ -1,5 +1,3 @@ -# @libp2p/logger - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,31 +5,7 @@ > A logging component for use in js-libp2p modules -## Table of contents - -- [Install](#install) - - [Browser ` -``` - -## Description +# About A map that reports it's size to the libp2p [Metrics](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/metrics#readme) system. @@ -61,17 +35,31 @@ with this base58btc: Qmfoo with this base32: bafyfoo ``` -## API Docs +# Install + +```console +$ npm i @libp2p/logger +``` + +## Browser ` +``` + +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 87f1354d50..1bb24ea8dc 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,3 +1,34 @@ +/** + * @packageDocumentation + * + * A map that reports it's size to the libp2p [Metrics](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/metrics#readme) system. + * + * If metrics are disabled a regular map is used. + * + * @example + * + * ```JavaScript + * import { logger } from '@libp2p/logger' + * + * const log = logger('libp2p:my:component:name') + * + * log('something happened: %s', 'it was ok') + * log.error('something bad happened: %o', err) + * + * log('with this peer: %p', aPeerId) + * log('and this base58btc: %b', aUint8Array) + * log('and this base32: %t', aUint8Array) + * ``` + * ```console + * $ DEBUG=libp2p:* node index.js + * something happened: it was ok + * something bad happened: + * with this peer: 12D3Foo + * with this base58btc: Qmfoo + * with this base32: bafyfoo + * ``` + */ + import debug from 'debug' import { base32 } from 'multiformats/bases/base32' import { base58btc } from 'multiformats/bases/base58' diff --git a/packages/metrics-prometheus/README.md b/packages/metrics-prometheus/README.md index 6dadf9e4e2..42bb982e6f 100644 --- a/packages/metrics-prometheus/README.md +++ b/packages/metrics-prometheus/README.md @@ -1,5 +1,3 @@ -# @libp2p/prometheus-metrics - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,27 +5,7 @@ > Collect libp2p metrics for scraping by Prometheus or Graphana -## Table of contents - -- [Install](#install) -- [Usage](#usage) - - [Queries](#queries) - - [Data sent/received](#data-sentreceived) - - [CPU usage](#cpu-usage) - - [Memory usage](#memory-usage) - - [DHT query time](#dht-query-time) - - [TCP transport dialer errors](#tcp-transport-dialer-errors) -- [API Docs](#api-docs) -- [License](#license) -- [Contribution](#contribution) - -## Install - -```console -$ npm i @libp2p/prometheus-metrics -``` - -## Usage +# About Configure your libp2p node with Prometheus metrics: @@ -93,17 +71,112 @@ libp2p_kad_dht_lan_query_time_seconds rate(libp2p_tcp_dialer_errors_total[30s]) ``` -## API Docs +## Example + +```typescript +import { prometheusMetrics } from '@libp2p/prometheus-metrics' + +const metrics = prometheusMetrics()() +const myMetric = metrics.registerMetric({ + name: 'my_metric', + label: 'my_label', + help: 'my help text' +}) + +myMetric.update(1) +``` + +## Example + +A metric that is expensive to calculate can be created by passing a `calculate` function that will only be invoked when metrics are being scraped: + +```typescript +import { prometheusMetrics } from '@libp2p/prometheus-metrics' + +const metrics = prometheusMetrics()() +const myMetric = metrics.registerMetric({ + name: 'my_metric', + label: 'my_label', + help: 'my help text', + calculate: async () => { + // do something expensive + return 1 + } +}) +``` + +## Example + +If several metrics should be grouped together (e.g. for graphing purposes) `registerMetricGroup` can be used instead: + +```typescript +import { prometheusMetrics } from '@libp2p/prometheus-metrics' + +const metrics = prometheusMetrics()() +const myMetricGroup = metrics.registerMetricGroup({ + name: 'my_metric_group', + label: 'my_label', + help: 'my help text' +}) + +myMetricGroup.increment({ my_label: 'my_value' }) +``` + +There are specific metric groups for tracking libp2p connections and streams: + +## Example + +Track a newly opened multiaddr connection: + +```typescript +import { prometheusMetrics } from '@libp2p/prometheus-metrics' +import { createLibp2p } from 'libp2p' + +const metrics = prometheusMetrics()() + +const libp2p = await createLibp2p({ + metrics: metrics, + }) +// set up a multiaddr connection +const connection = await libp2p.dial('multiaddr') +const connections = metrics.trackMultiaddrConnection(connection) +``` + +## Example + +Track a newly opened stream: + +```typescript +import { prometheusMetrics } from '@libp2p/prometheus-metrics' +import { createLibp2p } from 'libp2p' + +const metrics = prometheusMetrics()() + +const libp2p = await createLibp2p({ + metrics: metrics, +}) + +const stream = await connection.newStream('/my/protocol') +const streams = metrics.trackProtocolStream(stream) +``` + +# Install + +```console +$ npm i @libp2p/prometheus-metrics +``` + +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/metrics-prometheus/src/index.ts b/packages/metrics-prometheus/src/index.ts index 46675605d8..ebc0b83d2e 100644 --- a/packages/metrics-prometheus/src/index.ts +++ b/packages/metrics-prometheus/src/index.ts @@ -1,10 +1,69 @@ /** * @packageDocumentation * - * Collect libp2p metrics for scraping by Prometheus or Graphana. - * @module libp2p-prometheus-metrics + * Configure your libp2p node with Prometheus metrics: * - * A tracked metric can be created by calling either `registerMetric` on the metrics object + * ```js + * import { createLibp2p } from 'libp2p' + * import { prometheusMetrics } from '@libp2p/prometheus-metrics' + * + * const node = await createLibp2p({ + * metrics: prometheusMetrics() + * }) + * ``` + * + * Then use the `prom-client` module to supply metrics to the Prometheus/Graphana client using your http framework: + * + * ```js + * import client from 'prom-client' + * + * async handler (request, h) { + * return h.response(await client.register.metrics()) + * .type(client.register.contentType) + * } + * ``` + * + * All Prometheus metrics are global so there's no other work required to extract them. + * + * ### Queries + * + * Some useful queries are: + * + * #### Data sent/received + * + * ``` + * rate(libp2p_data_transfer_bytes_total[30s]) + * ``` + * + * #### CPU usage + * + * ``` + * rate(process_cpu_user_seconds_total[30s]) * 100 + * ``` + * + * #### Memory usage + * + * ``` + * nodejs_memory_usage_bytes + * ``` + * + * #### DHT query time + * + * ``` + * libp2p_kad_dht_wan_query_time_seconds + * ``` + * + * or + * + * ``` + * libp2p_kad_dht_lan_query_time_seconds + * ``` + * + * #### TCP transport dialer errors + * + * ``` + * rate(libp2p_tcp_dialer_errors_total[30s]) + * ``` * * @example * @@ -20,10 +79,11 @@ * * myMetric.update(1) * ``` - * A metric that is expensive to calculate can be created by passing a `calculate` function that will only be invoked when metrics are being scraped: * * @example * + * A metric that is expensive to calculate can be created by passing a `calculate` function that will only be invoked when metrics are being scraped: + * * ```typescript * import { prometheusMetrics } from '@libp2p/prometheus-metrics' * @@ -39,10 +99,10 @@ * }) * ``` * - * If several metrics should be grouped together (e.g. for graphing purposes) `registerMetricGroup` can be used instead: - * * @example * + * If several metrics should be grouped together (e.g. for graphing purposes) `registerMetricGroup` can be used instead: + * * ```typescript * import { prometheusMetrics } from '@libp2p/prometheus-metrics' * @@ -58,14 +118,14 @@ * * There are specific metric groups for tracking libp2p connections and streams: * - * Track a newly opened multiaddr connection: * @example * + * Track a newly opened multiaddr connection: + * * ```typescript * import { prometheusMetrics } from '@libp2p/prometheus-metrics' * import { createLibp2p } from 'libp2p' * - * * const metrics = prometheusMetrics()() * * const libp2p = await createLibp2p({ @@ -76,9 +136,10 @@ * const connections = metrics.trackMultiaddrConnection(connection) * ``` * - * Track a newly opened stream: * @example * + * Track a newly opened stream: + * * ```typescript * import { prometheusMetrics } from '@libp2p/prometheus-metrics' * import { createLibp2p } from 'libp2p' diff --git a/packages/multistream-select/README.md b/packages/multistream-select/README.md index ecb617ad0b..4412a86756 100644 --- a/packages/multistream-select/README.md +++ b/packages/multistream-select/README.md @@ -1,5 +1,3 @@ -# @libp2p/multistream-select - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,38 +5,11 @@ > JavaScript implementation of multistream-select -## Table of contents - -- [Install](#install) - - [Browser ` -``` - -## Background - -### What is `multistream-select`? +# About -TLDR; multistream-select is protocol multiplexing per connection/stream. [Full spec here](https://github.com/multiformats/multistream-select) +multistream-select is protocol multiplexing per connection/stream. [Full spec here](https://github.com/multiformats/multistream-select) -### Select a protocol flow +## Select a protocol flow The caller will send "interactive" messages, expecting for some acknowledgement from the callee, which will "select" the handler for the desired and supported protocol: @@ -54,17 +25,31 @@ The caller will send "interactive" messages, expecting for some acknowledgement > ``` -## API Docs +# Install + +```console +$ npm i @libp2p/multistream-select +``` + +## Browser ` +``` + +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/multistream-select/src/index.ts b/packages/multistream-select/src/index.ts index efe37ef346..84968842b0 100644 --- a/packages/multistream-select/src/index.ts +++ b/packages/multistream-select/src/index.ts @@ -1,3 +1,25 @@ +/** + * @packageDocumentation + * + * multistream-select is protocol multiplexing per connection/stream. [Full spec here](https://github.com/multiformats/multistream-select) + * + * ## Select a protocol flow + * + * The caller will send "interactive" messages, expecting for some acknowledgement from the callee, which will "select" the handler for the desired and supported protocol: + * + * ``` + * < /multistream-select/0.3.0 # i speak multistream-select/0.3.0 + * > /multistream-select/0.3.0 # ok, let's speak multistream-select/0.3.0 + * > /ipfs-dht/0.2.3 # i want to speak ipfs-dht/0.2.3 + * < na # ipfs-dht/0.2.3 is not available + * > /ipfs-dht/0.1.9 # What about ipfs-dht/0.1.9 ? + * < /ipfs-dht/0.1.9 # ok let's speak ipfs-dht/0.1.9 -- in a sense acts as an ACK + * > + * > + * > + * ``` + */ + import { PROTOCOL_ID } from './constants.js' import type { AbortOptions } from '@libp2p/interface' import type { Duplex, Source } from 'it-stream-types' diff --git a/packages/peer-collections/README.md b/packages/peer-collections/README.md index 9292ed7402..3112e317a7 100644 --- a/packages/peer-collections/README.md +++ b/packages/peer-collections/README.md @@ -1,5 +1,3 @@ -# @libp2p/peer-collections - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,22 +5,19 @@ > Stores values against a peer id -## Table of contents +# About -- [Install](#install) - - [Browser ` ``` -## Description - -We can't use PeerIds as collection keys because collection keys are compared using same-value-zero equality, so this is just a group of collections that stringifies PeerIds before storing them. - -PeerIds cache stringified versions of themselves so this should be a cheap operation. - -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/peer-collections/src/index.ts b/packages/peer-collections/src/index.ts index db2767e619..39bd9479d3 100644 --- a/packages/peer-collections/src/index.ts +++ b/packages/peer-collections/src/index.ts @@ -1,3 +1,11 @@ +/** + * @packageDocumentation + * + * We can't use PeerIds as collection keys because collection keys are compared using same-value-zero equality, so this is just a group of collections that stringifies PeerIds before storing them. + * + * PeerIds cache stringified versions of themselves so this should be a cheap operation. + */ + export { PeerMap } from './map.js' export { PeerSet } from './set.js' export { PeerList } from './list.js' diff --git a/packages/peer-discovery-bootstrap/README.md b/packages/peer-discovery-bootstrap/README.md index c48965d55c..9176c04857 100644 --- a/packages/peer-discovery-bootstrap/README.md +++ b/packages/peer-discovery-bootstrap/README.md @@ -1,5 +1,3 @@ -# @libp2p/bootstrap - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,37 +5,11 @@ > Peer discovery via a list of bootstrap peers -## Table of contents - -- [Install](#install) - - [Browser ` -``` +The configured bootstrap peers will be discovered after the configured timeout. This will ensure there are some peers in the peer store for the node to use to discover other peers. -## Usage - -The configured bootstrap peers will be discovered after the configured timeout. This will ensure -there are some peers in the peer store for the node to use to discover other peers. - -They will be tagged with a tag with the name `'bootstrap'` tag, the value `50` and it will expire -after two minutes which means the nodes connections may be closed if the maximum number of -connections is reached. +They will be tagged with a tag with the name `'bootstrap'` tag, the value `50` and it will expire after two minutes which means the nodes connections may be closed if the maximum number of connections is reached. Clients that need constant connections to bootstrap nodes (e.g. browsers) can set the TTL to `Infinity`. @@ -73,31 +45,38 @@ let options = { ] } -async function start () { - let libp2p = await createLibp2p(options) +const libp2p = await createLibp2p(options) - libp2p.on('peer:discovery', function (peerId) { - console.log('found peer: ', peerId.toB58String()) - }) +libp2p.on('peer:discovery', function (peerId) { + console.log('found peer: ', peerId.toB58String()) +}) +``` - await libp2p.start() +# Install -} +```console +$ npm i @libp2p/bootstrap +``` + +## Browser ` ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/peer-discovery-bootstrap/src/index.ts b/packages/peer-discovery-bootstrap/src/index.ts index 7400d494f4..999b14f5e0 100644 --- a/packages/peer-discovery-bootstrap/src/index.ts +++ b/packages/peer-discovery-bootstrap/src/index.ts @@ -1,3 +1,52 @@ +/** + * @packageDocumentation + * + * The configured bootstrap peers will be discovered after the configured timeout. This will ensure there are some peers in the peer store for the node to use to discover other peers. + * + * They will be tagged with a tag with the name `'bootstrap'` tag, the value `50` and it will expire after two minutes which means the nodes connections may be closed if the maximum number of connections is reached. + * + * Clients that need constant connections to bootstrap nodes (e.g. browsers) can set the TTL to `Infinity`. + * + * ```JavaScript + * import { createLibp2p } from 'libp2p' + * import { bootstrap } from '@libp2p/bootstrap' + * import { tcp } from 'libp2p/tcp' + * import { noise } from '@libp2p/noise' + * import { mplex } from '@libp2p/mplex' + * + * let options = { + * transports: [ + * tcp() + * ], + * streamMuxers: [ + * mplex() + * ], + * connectionEncryption: [ + * noise() + * ], + * peerDiscovery: [ + * bootstrap({ + * list: [ // a list of bootstrap peer multiaddrs to connect to on node startup + * "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + * "/dnsaddr/bootstrap.libp2p.io/ipfs/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", + * "/dnsaddr/bootstrap.libp2p.io/ipfs/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa" + * ], + * timeout: 1000, // in ms, + * tagName: 'bootstrap', + * tagValue: 50, + * tagTTL: 120000 // in ms + * }) + * ] + * } + * + * const libp2p = await createLibp2p(options) + * + * libp2p.on('peer:discovery', function (peerId) { + * console.log('found peer: ', peerId.toB58String()) + * }) + * ``` + */ + import { TypedEventEmitter } from '@libp2p/interface/events' import { peerDiscovery } from '@libp2p/interface/peer-discovery' import { logger } from '@libp2p/logger' diff --git a/packages/peer-discovery-mdns/README.md b/packages/peer-discovery-mdns/README.md index be587d8220..11ebefd4f3 100644 --- a/packages/peer-discovery-mdns/README.md +++ b/packages/peer-discovery-mdns/README.md @@ -1,5 +1,3 @@ -# @libp2p/mdns - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,24 +5,13 @@ > Node.js libp2p mDNS discovery implementation for peer discovery -## Table of contents +# About -- [Install](#install) -- [Usage](#usage) -- [MDNS messages](#mdns-messages) -- [API Docs](#api-docs) -- [License](#license) -- [Contribution](#contribution) +A peer discover mechanism that uses [mDNS](https://datatracker.ietf.org/doc/html/rfc6762) to discover peers on the local network. -## Install +## Example -```console -$ npm i @libp2p/mdns -``` - -## Usage - -```Typescript +```ts import { mdns } from '@libp2p/mdns' const options = { @@ -33,81 +20,84 @@ const options = { ] } -async function start () { - const libp2p = await createLibp2p(options) - - libp2p.on('peer:discovery', function (peerId) { - console.log('found peer: ', peerId.toB58String()) - }) +const libp2p = await createLibp2p(options) - await libp2p.start() -} +libp2p.on('peer:discovery', function (peerId) { + console.log('found peer: ', peerId.toB58String()) +}) +await libp2p.start() ``` -- options - - `peerName` - Peer name to announce (should not be peeer id), default random string - - `multiaddrs` - multiaddrs to announce - - `broadcast` - (true/false) announce our presence through mDNS, default `true` - - `interval` - query interval, default 10 \* 1000 (10 seconds) - - `serviceTag` - name of the service announce , default '_p2p._udp.local\` - ## MDNS messages A query is sent to discover the libp2p nodes on the local network ```js { - type: 'query', - questions: [ { name: '_p2p._udp.local', type: 'PTR' } ] + type: 'query', + questions: [ { name: '_p2p._udp.local', type: 'PTR' } ] } ``` When a query is detected, each libp2p node sends an answer about itself ```js - [ { name: '_p2p._udp.local', - type: 'PTR', - class: 'IN', - ttl: 120, - data: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local' }, - { name: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local', - type: 'SRV', - class: 'IN', - ttl: 120, - data: - { priority: 10, - weight: 1, - port: '20002', - target: 'LAPTOP-G5LJ7VN9' } }, - { name: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local', - type: 'TXT', - class: 'IN', - ttl: 120, - data: ['QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK'] }, - { name: 'LAPTOP-G5LJ7VN9', - type: 'A', - class: 'IN', - ttl: 120, - data: '127.0.0.1' }, - { name: 'LAPTOP-G5LJ7VN9', - type: 'AAAA', - class: 'IN', - ttl: 120, - data: '::1' } ] +[{ + name: '_p2p._udp.local', + type: 'PTR', + class: 'IN', + ttl: 120, + data: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local' +}, { + name: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local', + type: 'SRV', + class: 'IN', + ttl: 120, + data: { + priority: 10, + weight: 1, + port: '20002', + target: 'LAPTOP-G5LJ7VN9' + } +}, { + name: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local', + type: 'TXT', + class: 'IN', + ttl: 120, + data: ['QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK'] +}, { + name: 'LAPTOP-G5LJ7VN9', + type: 'A', + class: 'IN', + ttl: 120, + data: '127.0.0.1' +}, { + name: 'LAPTOP-G5LJ7VN9', + type: 'AAAA', + class: 'IN', + ttl: 120, + data: '::1' +}] +``` + +# Install + +```console +$ npm i @libp2p/mdns ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/peer-discovery-mdns/src/index.ts b/packages/peer-discovery-mdns/src/index.ts index 98d568640e..ed37439b57 100644 --- a/packages/peer-discovery-mdns/src/index.ts +++ b/packages/peer-discovery-mdns/src/index.ts @@ -1,3 +1,81 @@ +/** + * @packageDocumentation + * + * A peer discover mechanism that uses [mDNS](https://datatracker.ietf.org/doc/html/rfc6762) to discover peers on the local network. + * + * @example + * + * ```ts + * import { mdns } from '@libp2p/mdns' + * + * const options = { + * peerDiscovery: [ + * mdns() + * ] + * } + * + * const libp2p = await createLibp2p(options) + * + * libp2p.on('peer:discovery', function (peerId) { + * console.log('found peer: ', peerId.toB58String()) + * }) + * + * await libp2p.start() + * ``` + * + * ## MDNS messages + * + * A query is sent to discover the libp2p nodes on the local network + * + * ```js + * { + * type: 'query', + * questions: [ { name: '_p2p._udp.local', type: 'PTR' } ] + * } + * ``` + * + * When a query is detected, each libp2p node sends an answer about itself + * + * ```js + * [{ + * name: '_p2p._udp.local', + * type: 'PTR', + * class: 'IN', + * ttl: 120, + * data: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local' + * }, { + * name: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local', + * type: 'SRV', + * class: 'IN', + * ttl: 120, + * data: { + * priority: 10, + * weight: 1, + * port: '20002', + * target: 'LAPTOP-G5LJ7VN9' + * } + * }, { + * name: 'QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK._p2p._udp.local', + * type: 'TXT', + * class: 'IN', + * ttl: 120, + * data: ['QmNPubsDWATVngE3d5WDSNe7eVrFLuk38qb9t6vdLnu2aK'] + * }, { + * name: 'LAPTOP-G5LJ7VN9', + * type: 'A', + * class: 'IN', + * ttl: 120, + * data: '127.0.0.1' + * }, { + * name: 'LAPTOP-G5LJ7VN9', + * type: 'AAAA', + * class: 'IN', + * ttl: 120, + * data: '::1' + * }] + * ``` + */ + import { CustomEvent, TypedEventEmitter } from '@libp2p/interface/events' import { peerDiscovery } from '@libp2p/interface/peer-discovery' import { logger } from '@libp2p/logger' @@ -12,11 +90,33 @@ import type { AddressManager } from '@libp2p/interface-internal/address-manager' const log = logger('libp2p:mdns') export interface MulticastDNSInit { + /** + * (true/false) announce our presence through mDNS, default `true` + */ broadcast?: boolean + + /** + * query interval, default 10 \* 1000 (10 seconds) + */ interval?: number + + /** + * name of the service announce , default '_p2p._udp.local\` + */ serviceTag?: string + /** + * Peer name to announce (should not be peeer id), default random string + */ peerName?: string + + /** + * UDP port to broadcast to + */ port?: number + + /** + * UDP IP to broadcast to + */ ip?: string } diff --git a/packages/peer-id-factory/README.md b/packages/peer-id-factory/README.md index 7c353cd96b..b430276ed2 100644 --- a/packages/peer-id-factory/README.md +++ b/packages/peer-id-factory/README.md @@ -1,5 +1,3 @@ -# @libp2p/peer-id-factory - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,31 +5,7 @@ > Create PeerId instances -## Table of contents - -- [Install](#install) - - [Browser ` -``` - -## Description +# About Generate, import, and export PeerIDs, for use with [IPFS](https://github.com/ipfs/ipfs). @@ -52,17 +26,31 @@ console.log(id.toString()) 12D3KooWRm8J3iL796zPFi2EtGGtUJn58AG67gcqzMFHZnnsTzqD ``` -## API Docs +# Install + +```console +$ npm i @libp2p/peer-id-factory +``` + +## Browser ` +``` + +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/peer-id-factory/src/index.ts b/packages/peer-id-factory/src/index.ts index 626f01eec3..f40646befe 100644 --- a/packages/peer-id-factory/src/index.ts +++ b/packages/peer-id-factory/src/index.ts @@ -1,3 +1,26 @@ +/** + * @packageDocumentation + * + * Generate, import, and export PeerIDs, for use with [IPFS](https://github.com/ipfs/ipfs). + * + * A Peer ID is the SHA-256 [multihash](https://github.com/multiformats/multihash) of a public key. + * + * The public key is a base64 encoded string of a protobuf containing an RSA DER buffer. This uses a node buffer to pass the base64 encoded public key protobuf to the multihash for ID generation. + * + * @example + * + * ```JavaScript + * import { createEd25519PeerId } from '@libp2p/peer-id-factory' + * + * const peerId = await createEd25519PeerId() + * console.log(id.toString()) + * ``` + * + * ```bash + * 12D3KooWRm8J3iL796zPFi2EtGGtUJn58AG67gcqzMFHZnnsTzqD + * ``` + */ + import { generateKeyPair, marshalPrivateKey, unmarshalPrivateKey, marshalPublicKey, unmarshalPublicKey } from '@libp2p/crypto/keys' import { peerIdFromKeys, peerIdFromBytes } from '@libp2p/peer-id' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' diff --git a/packages/peer-id/README.md b/packages/peer-id/README.md index 7c2c8c23bc..aafa9396d7 100644 --- a/packages/peer-id/README.md +++ b/packages/peer-id/README.md @@ -1,29 +1,15 @@ -# @libp2p/peer-id - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) [![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p/main.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p/actions/workflows/main.yml?query=branch%3Amaster) -> Implementation of @libp2p/interface-peer-id - -## Table of contents - -- [Install](#install) - - [Browser ` ``` -## Description - -A basic implementation of a peer id - -## Example - -```JavaScript -import { peerIdFromString } from '@libp2p/peer-id' - -const peer = peerIdFromString('k51qzi5uqu5dkwkqm42v9j9kqcam2jiuvloi16g72i4i4amoo2m8u3ol3mqu6s') - -console.log(peer.toCid()) // CID(bafzaa...) -console.log(peer.toString()) // "12D3K..." -``` - -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/peer-id/src/index.ts b/packages/peer-id/src/index.ts index b1dfd2fabe..db722aa171 100644 --- a/packages/peer-id/src/index.ts +++ b/packages/peer-id/src/index.ts @@ -1,3 +1,19 @@ +/** + * @packageDocumentation + * + * An implementation of a peer id + * + * @example + * + * ```JavaScript + * import { peerIdFromString } from '@libp2p/peer-id' + * const peer = peerIdFromString('k51qzi5uqu5dkwkqm42v9j9kqcam2jiuvloi16g72i4i4amoo2m8u3ol3mqu6s') + * + * console.log(peer.toCid()) // CID(bafzaa...) + * console.log(peer.toString()) // "12D3K..." + * ``` + */ + import { CodeError } from '@libp2p/interface/errors' import { type Ed25519PeerId, type PeerIdType, type RSAPeerId, type Secp256k1PeerId, symbol, type PeerId } from '@libp2p/interface/peer-id' import { base58btc } from 'multiformats/bases/base58' diff --git a/packages/peer-record/README.md b/packages/peer-record/README.md index 358efe226b..ae3adc5144 100644 --- a/packages/peer-record/README.md +++ b/packages/peer-record/README.md @@ -1,5 +1,3 @@ -# @libp2p/peer-record - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,44 +5,11 @@ > Used to transfer signed peer data across the network -## Table of contents - -- [Install](#install) - - [Browser ` -``` - -## Description +# About Libp2p nodes need to store data in a public location (e.g. a DHT), or rely on potentially untrustworthy intermediaries to relay information over its lifetime. Accordingly, libp2p nodes need to be able to verify that the data came from a specific peer and that it hasn't been tampered with. -### Envelope +## Envelope Libp2p provides an all-purpose data container called **envelope**. It was created to enable the distribution of verifiable records, which we can prove originated from the addressed peer itself. The envelope includes a signature of the data, so that its authenticity is verified. @@ -52,9 +17,9 @@ This envelope stores a marshaled record implementing the [interface-record](http You can read further about the envelope in [libp2p/specs#217](https://github.com/libp2p/specs/pull/217). -## Usage +## Example -- create an envelope with an instance of an [interface-record](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/record) implementation and prepare it for being exchanged: +Create an envelope with an instance of an [interface-record](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/record) implementation and prepare it for being exchanged: ```js // interface-record implementation example with the "libp2p-example" namespace @@ -83,7 +48,9 @@ const e = await PeerEnvelope.seal(rec, peerId) const wireData = e.marshal() ``` -- consume a received envelope (`wireData`) and transform it back to a record: +## Example + +Consume a received envelope (`wireData`) and transform it back to a record: ```js import { PeerEnvelope } from '@libp2p/peer-record' @@ -109,9 +76,9 @@ A peer record contains the peers' publicly reachable listen addresses, and may b You can read further about the Peer Record in [libp2p/specs#217](https://github.com/libp2p/specs/pull/217). -### Usage +## Example -- create a new Peer Record +Create a new Peer Record ```js import { PeerRecord } from '@libp2p/peer-record' @@ -122,7 +89,9 @@ const pr = new PeerRecord({ }) ``` -- create a Peer Record from a protobuf +## Example + +Create a Peer Record from a protobuf ```js import { PeerRecord } from '@libp2p/peer-record' @@ -130,23 +99,21 @@ import { PeerRecord } from '@libp2p/peer-record' const pr = PeerRecord.createFromProtobuf(data) ``` -### Libp2p Flows +## Libp2p Flows -#### Self Record +### Self Record Once a libp2p node has started and is listening on a set of multiaddrs, its own peer record can be created. The identify service is responsible for creating the self record when the identify protocol kicks in for the first time. This record will be stored for future needs of the identify protocol when connecting with other peers. -#### Self record Updates - -***NOT\_YET\_IMPLEMENTED*** +### Self record Updates While creating peer records is fairly trivial, addresses are not static and might be modified at arbitrary times. This can happen via an Address Manager API, or even through AutoRelay/AutoNAT. When a libp2p node changes its listen addresses, the identify service will be informed. Once that happens, the identify service creates a new self record and stores it. With the new record, the identify push/delta protocol will be used to communicate this change to the connected peers. -#### Subsystem receiving a record +### Subsystem receiving a record Considering that a node can discover other peers' addresses from a variety of sources, Libp2p Peerstore can differentiate the addresses that were obtained through a signed peer record. @@ -154,34 +121,48 @@ Once a record is received and its signature properly validated, its envelope is The AddressBook Addresses will be updated with the content of the envelope with a certified property. This allows other subsystems to identify the known certified addresses of a peer. -#### Subsystem providing a record +### Subsystem providing a record Libp2p subsystems that exchange other peers information will provide the envelope that they received by those peers. As a result, other peers can verify if the envelope was really created by the addressed peer. When a subsystem wants to provide a record, it will get it from the AddressBook, if it exists. Other subsystems are also able to provide the self record, since it is also stored in the AddressBook. -### Future Work +## Future Work - Persistence only considering certified addresses? - Peers may not know their own addresses. It's often impossible to automatically infer one's own public address, and peers may need to rely on third party peers to inform them of their observed public addresses. - A peer may inadvertently or maliciously sign an address that they do not control. In other words, a signature isn't a guarantee that a given address is valid. - Some addresses may be ambiguous. For example, addresses on a private subnet are valid within that subnet but are useless on the public internet. - Once all these pieces are in place, we will also need a way to prioritize addresses based on their authenticity, that is, the dialer can prioritize self-certified addresses over addresses from an unknown origin. - - Modular dialer? (taken from go PR notes) - - With the modular dialer, users should easily be able to configure precedence. With dialer v1, anything we do to prioritise dials is gonna be spaghetti and adhoc. With the modular dialer, you’d be able to specify the order of dials when instantiating the pipeline. - - Multiple parallel dials. We already have the issue where new addresses aren't added to existing dials. +- Modular dialer? (taken from go PR notes) + - With the modular dialer, users should easily be able to configure precedence. With dialer v1, anything we do to prioritise dials is gonna be spaghetti and adhoc. With the modular dialer, you’d be able to specify the order of dials when instantiating the pipeline. + - Multiple parallel dials. We already have the issue where new addresses aren't added to existing dials. + +# Install + +```console +$ npm i @libp2p/peer-record +``` + +## Browser ` +``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/peer-record/src/index.ts b/packages/peer-record/src/index.ts index 51238b79ac..c3207bac39 100644 --- a/packages/peer-record/src/index.ts +++ b/packages/peer-record/src/index.ts @@ -1,3 +1,138 @@ +/** + * @packageDocumentation + * + * Libp2p nodes need to store data in a public location (e.g. a DHT), or rely on potentially untrustworthy intermediaries to relay information over its lifetime. Accordingly, libp2p nodes need to be able to verify that the data came from a specific peer and that it hasn't been tampered with. + * + * ## Envelope + * + * Libp2p provides an all-purpose data container called **envelope**. It was created to enable the distribution of verifiable records, which we can prove originated from the addressed peer itself. The envelope includes a signature of the data, so that its authenticity is verified. + * + * This envelope stores a marshaled record implementing the [interface-record](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/record). These Records are designed to be serialized to bytes and placed inside of the envelopes before being shared with other peers. + * + * You can read further about the envelope in [libp2p/specs#217](https://github.com/libp2p/specs/pull/217). + * + * @example + * + * Create an envelope with an instance of an [interface-record](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/record) implementation and prepare it for being exchanged: + * + * ```js + * // interface-record implementation example with the "libp2p-example" namespace + * import { PeerRecord } from '@libp2p/peer-record' + * import { fromString } from 'uint8arrays/from-string' + * + * class ExampleRecord extends PeerRecord { + * constructor () { + * super ('libp2p-example', fromString('0302', 'hex')) + * } + * + * marshal () {} + * + * equals (other) {} + * } + * + * ExampleRecord.createFromProtobuf = () => {} + * ``` + * + * ```js + * import { PeerEnvelope } from '@libp2p/peer-record' + * import { ExampleRecord } from './example-record.js' + * + * const rec = new ExampleRecord() + * const e = await PeerEnvelope.seal(rec, peerId) + * const wireData = e.marshal() + * ``` + * + * @example + * + * Consume a received envelope (`wireData`) and transform it back to a record: + * + * ```js + * import { PeerEnvelope } from '@libp2p/peer-record' + * import { ExampleRecord } from './example-record.js' + * + * const domain = 'libp2p-example' + * let e + * + * try { + * e = await PeerEnvelope.openAndCertify(wireData, domain) + * } catch (err) {} + * + * const rec = ExampleRecord.createFromProtobuf(e.payload) + * ``` + * + * ## Peer Record + * + * All libp2p nodes keep a `PeerStore`, that among other information stores a set of known addresses for each peer, which can come from a variety of sources. + * + * Libp2p peer records were created to enable the distribution of verifiable address records, which we can prove originated from the addressed peer itself. With such guarantees, libp2p is able to prioritize addresses based on their authenticity, with the most strict strategy being to only dial certified addresses (no strategies have been implemented at the time of writing). + * + * A peer record contains the peers' publicly reachable listen addresses, and may be extended in the future to contain additional metadata relevant to routing. It also contains a `seqNumber` field, a timestamp per the spec, so that we can verify the most recent record. + * + * You can read further about the Peer Record in [libp2p/specs#217](https://github.com/libp2p/specs/pull/217). + * + * @example + * + * Create a new Peer Record + * + * ```js + * import { PeerRecord } from '@libp2p/peer-record' + * + * const pr = new PeerRecord({ + * peerId: node.peerId, + * multiaddrs: node.multiaddrs + * }) + * ``` + * + * @example + * + * Create a Peer Record from a protobuf + * + * ```js + * import { PeerRecord } from '@libp2p/peer-record' + * + * const pr = PeerRecord.createFromProtobuf(data) + * ``` + * + * ## Libp2p Flows + * + * ### Self Record + * + * Once a libp2p node has started and is listening on a set of multiaddrs, its own peer record can be created. + * + * The identify service is responsible for creating the self record when the identify protocol kicks in for the first time. This record will be stored for future needs of the identify protocol when connecting with other peers. + * + * ### Self record Updates + * + * While creating peer records is fairly trivial, addresses are not static and might be modified at arbitrary times. This can happen via an Address Manager API, or even through AutoRelay/AutoNAT. + * + * When a libp2p node changes its listen addresses, the identify service will be informed. Once that happens, the identify service creates a new self record and stores it. With the new record, the identify push/delta protocol will be used to communicate this change to the connected peers. + * + * ### Subsystem receiving a record + * + * Considering that a node can discover other peers' addresses from a variety of sources, Libp2p Peerstore can differentiate the addresses that were obtained through a signed peer record. + * + * Once a record is received and its signature properly validated, its envelope is stored in the AddressBook in its byte representation. The `seqNumber` remains unmarshalled so that we can quickly compare it against incoming records to determine the most recent record. + * + * The AddressBook Addresses will be updated with the content of the envelope with a certified property. This allows other subsystems to identify the known certified addresses of a peer. + * + * ### Subsystem providing a record + * + * Libp2p subsystems that exchange other peers information will provide the envelope that they received by those peers. As a result, other peers can verify if the envelope was really created by the addressed peer. + * + * When a subsystem wants to provide a record, it will get it from the AddressBook, if it exists. Other subsystems are also able to provide the self record, since it is also stored in the AddressBook. + * + * ## Future Work + * + * - Persistence only considering certified addresses? + * - Peers may not know their own addresses. It's often impossible to automatically infer one's own public address, and peers may need to rely on third party peers to inform them of their observed public addresses. + * - A peer may inadvertently or maliciously sign an address that they do not control. In other words, a signature isn't a guarantee that a given address is valid. + * - Some addresses may be ambiguous. For example, addresses on a private subnet are valid within that subnet but are useless on the public internet. + * - Once all these pieces are in place, we will also need a way to prioritize addresses based on their authenticity, that is, the dialer can prioritize self-certified addresses over addresses from an unknown origin. + * - Modular dialer? (taken from go PR notes) + * - With the modular dialer, users should easily be able to configure precedence. With dialer v1, anything we do to prioritise dials is gonna be spaghetti and adhoc. With the modular dialer, you’d be able to specify the order of dials when instantiating the pipeline. + * - Multiple parallel dials. We already have the issue where new addresses aren't added to existing dials. + */ + export { RecordEnvelope } from './envelope/index.js' export type { RecordEnvelopeInit } from './envelope/index.js' export { PeerRecord } from './peer-record/index.js' diff --git a/packages/peer-store/README.md b/packages/peer-store/README.md index 41a310aad6..7b7d85b490 100644 --- a/packages/peer-store/README.md +++ b/packages/peer-store/README.md @@ -1,5 +1,3 @@ -# @libp2p/peer-store - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,21 +5,13 @@ > Stores information about peers libp2p knows on the network -## Table of contents - -- [Install](#install) - - [Browser ` ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. - -[peer-id]: https://github.com/libp2p/js-peer-id - -[peer-store-events]: [https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#libp2ppeerStore] diff --git a/packages/protocol-perf/README.md b/packages/protocol-perf/README.md index 15ca3f4ada..915e7420ea 100644 --- a/packages/protocol-perf/README.md +++ b/packages/protocol-perf/README.md @@ -1,5 +1,3 @@ -# @libp2p/perf - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,39 +5,58 @@ > Implementation of Perf Protocol -## Table of contents +# About + +The `performanceService` implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which is used to measure performance within and across libp2p implementations +addresses. -- [Install](#install) - - [Browser ` ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/pubsub-floodsub/README.md b/packages/pubsub-floodsub/README.md index 9fadcbc2ca..a1b7c91b9b 100644 --- a/packages/pubsub-floodsub/README.md +++ b/packages/pubsub-floodsub/README.md @@ -1,5 +1,3 @@ -# @libp2p/floodsub - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,31 +5,9 @@ > libp2p-floodsub, also known as pubsub-flood or just dumbsub, this implementation of pubsub focused on delivering an API for Publish/Subscribe, but with no CastTree Forming (it just floods the network). -## Table of contents - -- [Install](#install) - - [Browser ` -``` +# About -## Don't use this module +> Don't use this module This module is a naive implementation of pubsub. It broadcasts all messages to all network peers, cannot provide older messages and has no protection against bad actors. @@ -39,7 +15,7 @@ It exists for academic purposes only, you should not use it in production. Instead please use [gossipsub](https://www.npmjs.com/package/@chainsafe/libp2p-gossipsub) - a more complete implementation which is also compatible with floodsub. -## Usage +## Example ```JavaScript import { createLibp2pNode } from 'libp2p' @@ -59,17 +35,31 @@ node.pubsub.addEventListener('message', (evt) => { node.pubsub.publish('fruit', new TextEncoder().encode('banana')) ``` -## API Docs +# Install + +```console +$ npm i @libp2p/floodsub +``` + +## Browser ` +``` + +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/pubsub-floodsub/src/index.ts b/packages/pubsub-floodsub/src/index.ts index 4d50605173..53a0e41677 100644 --- a/packages/pubsub-floodsub/src/index.ts +++ b/packages/pubsub-floodsub/src/index.ts @@ -1,3 +1,35 @@ +/** + * @packageDocumentation + * + * > Don't use this module + * + * This module is a naive implementation of pubsub. It broadcasts all messages to all network peers, cannot provide older messages and has no protection against bad actors. + * + * It exists for academic purposes only, you should not use it in production. + * + * Instead please use [gossipsub](https://www.npmjs.com/package/@chainsafe/libp2p-gossipsub) - a more complete implementation which is also compatible with floodsub. + * + * @example + * + * ```JavaScript + * import { createLibp2pNode } from 'libp2p' + * import { floodsub } from '@libp2p/floodsub' + * + * const node = await createLibp2pNode({ + * pubsub: floodsub() + * //... other options + * }) + * await node.start() + * + * node.pubsub.subscribe('fruit') + * node.pubsub.addEventListener('message', (evt) => { + * console.log(evt) + * }) + * + * node.pubsub.publish('fruit', new TextEncoder().encode('banana')) + * ``` + */ + import { logger } from '@libp2p/logger' import { PubSubBaseProtocol, type PubSubComponents } from '@libp2p/pubsub' import { toString } from 'uint8arrays/to-string' diff --git a/packages/pubsub/README.md b/packages/pubsub/README.md index a985a40c40..7962231142 100644 --- a/packages/pubsub/README.md +++ b/packages/pubsub/README.md @@ -1,5 +1,3 @@ -# @libp2p/pubsub - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,22 +5,13 @@ > libp2p pubsub base class -## Table of contents - -- [Install](#install) - - [Browser ` ``` -## Usage - -```console -npm i @libp2p/pubsub -``` - -```javascript -import { PubSubBaseProtocol } from '@libp2p/pubsub' - -class MyPubsubImplementation extends PubSubBaseProtocol { - // .. extra methods here -} -``` - -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/pubsub/src/index.ts b/packages/pubsub/src/index.ts index 2de4fa3fce..af8fa223de 100644 --- a/packages/pubsub/src/index.ts +++ b/packages/pubsub/src/index.ts @@ -1,3 +1,18 @@ +/** + * @packageDocumentation + * + * A set of components to be extended in order to create a pubsub implementation. + * + * @example + * ```javascript + * import { PubSubBaseProtocol } from '@libp2p/pubsub' + * + * class MyPubsubImplementation extends PubSubBaseProtocol { + * // .. extra methods here + * } + * ``` + */ + import { CodeError } from '@libp2p/interface/errors' import { TypedEventEmitter, CustomEvent } from '@libp2p/interface/events' import { type PubSub, type Message, type StrictNoSign, type StrictSign, type PubSubInit, type PubSubEvents, type PeerStreams, type PubSubRPCMessage, type PubSubRPC, type PubSubRPCSubscription, type SubscriptionChangeData, type PublishResult, type TopicValidatorFn, TopicValidatorResult } from '@libp2p/interface/pubsub' diff --git a/packages/stream-multiplexer-mplex/README.md b/packages/stream-multiplexer-mplex/README.md index ee8b856784..dc8954f6c2 100644 --- a/packages/stream-multiplexer-mplex/README.md +++ b/packages/stream-multiplexer-mplex/README.md @@ -1,5 +1,3 @@ -# @libp2p/mplex - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,22 +5,13 @@ > JavaScript implementation of -## Table of contents - -- [Install](#install) - - [Browser ` ``` -[![](https://github.com/libp2p/interface-stream-muxer/raw/master/img/badge.png)](https://github.com/libp2p/interface-stream-muxer) - -## Usage - -```js -import { mplex } from '@libp2p/mplex' -import { pipe } from 'it-pipe' - -const factory = mplex() - -const muxer = factory.createStreamMuxer(components, { - onStream: stream => { // Receive a duplex stream from the remote - // ...receive data from the remote and optionally send data back - }, - onStreamEnd: stream => { - // ...handle any tracking you may need of stream closures - } -}) - -pipe(conn, muxer, conn) // conn is duplex connection to another peer - -const stream = muxer.newStream() // Create a new duplex stream to the remote - -// Use the duplex stream to send some data to the remote... -pipe([1, 2, 3], stream) -``` - ## API Docs - diff --git a/packages/stream-multiplexer-mplex/examples/dialer.js b/packages/stream-multiplexer-mplex/examples/dialer.js deleted file mode 100644 index 9c33ba34ba..0000000000 --- a/packages/stream-multiplexer-mplex/examples/dialer.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-console */ -'use strict' - -import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import tcp from 'net' -import { pipe } from 'it-pipe' -import { toIterable } from './util.js' -import { Mplex } from '../dist/src/index.js' - -const socket = toIterable(tcp.connect(9999)) -console.log('[dialer] socket stream opened') - -const controller = new AbortController() - -const factory = new Mplex({ signal: controller.signal }) -const muxer = factory.createStreamMuxer() - -const pipeMuxerToSocket = async () => { - await pipe(muxer, socket, muxer) - console.log('[dialer] socket stream closed') -} - -const sendAndReceive = async () => { - const muxedStream = muxer.newStream('hello') - console.log('[dialer] muxed stream opened') - - await pipe( - [uint8ArrayFromString('hey, how is it going. I am the dialer')], - muxedStream, - async source => { - for await (const chunk of source) { - console.log('[dialer] received:') - console.log(uint8ArrayToString(chunk.slice())) - } - } - ) - console.log('[dialer] muxed stream closed') - - // Close the socket stream after 1s - setTimeout(() => controller.abort(), 1000) -} - -pipeMuxerToSocket() -sendAndReceive() diff --git a/packages/stream-multiplexer-mplex/examples/listener.js b/packages/stream-multiplexer-mplex/examples/listener.js deleted file mode 100644 index 189c945531..0000000000 --- a/packages/stream-multiplexer-mplex/examples/listener.js +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable no-console */ -'use strict' - -import tcp from 'net' -import { pipe } from 'it-pipe' -import { toIterable } from './util.js' -import { Mplex } from '../dist/src/index.js' -import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { toString as uint8ArrayToString } from 'uint8arrays/to-string' - -const listener = tcp.createServer(async socket => { - console.log('[listener] Got connection!') - - const factory = new Mplex() - socket = toIterable(socket) - const muxer = factory.createStreamMuxer({ - onIncomingStream: async (stream) => { - console.log('[listener] muxed stream opened, id:', stream.id) - await pipe( - stream, - source => (async function * () { - for await (const chunk of source) { - console.log('[listener] received:') - console.log(uint8ArrayToString(chunk.slice())) - yield uint8ArrayFromString('thanks for the message, I am the listener') - } - })(), - stream - ) - console.log('[listener] muxed stream closed') - } - }) - await pipe(socket, muxer, socket) - console.log('[listener] socket stream closed') -}) - -listener.listen(9999, () => console.log('[listener] listening on 9999')) diff --git a/packages/stream-multiplexer-mplex/examples/util.js b/packages/stream-multiplexer-mplex/examples/util.js deleted file mode 100644 index 057c0c6787..0000000000 --- a/packages/stream-multiplexer-mplex/examples/util.js +++ /dev/null @@ -1,17 +0,0 @@ -// Simple convertion of Node.js duplex to iterable duplex (no backpressure) -export const toIterable = socket => { - return { - sink: async source => { - try { - for await (const chunk of source) { - socket.write(chunk) - } - } catch (err) { - // If not an abort then destroy the socket with an error - return socket.destroy(err.code === 'ABORT_ERR' ? null : err) - } - socket.end() - }, - source: socket - } -} diff --git a/packages/stream-multiplexer-mplex/src/index.ts b/packages/stream-multiplexer-mplex/src/index.ts index c7ef9549df..75913bb7b5 100644 --- a/packages/stream-multiplexer-mplex/src/index.ts +++ b/packages/stream-multiplexer-mplex/src/index.ts @@ -1,3 +1,36 @@ +/** + * @packageDocumentation + * + * This is a [simple stream multiplexer(https://docs.libp2p.io/concepts/multiplex/mplex/) that has been deprecated. + * + * Please use [@chainsafe/libp2p-yamux](https://www.npmjs.com/package/@chainsafe/libp2p-yamux) instead. + * + * @example + * + * ```js + * import { mplex } from '@libp2p/mplex' + * import { pipe } from 'it-pipe' + * + * const factory = mplex() + * + * const muxer = factory.createStreamMuxer(components, { + * onStream: stream => { // Receive a duplex stream from the remote + * // ...receive data from the remote and optionally send data back + * }, + * onStreamEnd: stream => { + * // ...handle any tracking you may need of stream closures + * } + * }) + * + * pipe(conn, muxer, conn) // conn is duplex connection to another peer + * + * const stream = muxer.newStream() // Create a new duplex stream to the remote + * + * // Use the duplex stream to send some data to the remote... + * pipe([1, 2, 3], stream) + * ``` + */ + import { MplexStreamMuxer } from './mplex.js' import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer' diff --git a/packages/transport-tcp/README.md b/packages/transport-tcp/README.md index 83fe40f6fc..a914a9db81 100644 --- a/packages/transport-tcp/README.md +++ b/packages/transport-tcp/README.md @@ -1,5 +1,3 @@ -# @libp2p/tcp - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,21 +5,11 @@ > A TCP transport for libp2p -## Table of contents - -- [Install](#install) -- [Usage](#usage) -- [API Docs](#api-docs) -- [License](#license) -- [Contribution](#contribution) - -## Install +# About -```console -$ npm i @libp2p/tcp -``` +A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on the TCP networking stack. -## Usage +## Example ```js import { tcp } from '@libp2p/tcp' @@ -71,17 +59,23 @@ new connection opened Value: hello World! ``` -## API Docs +# Install + +```console +$ npm i @libp2p/tcp +``` + +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/transport-tcp/src/index.ts b/packages/transport-tcp/src/index.ts index ed526a2182..bf67f831bb 100644 --- a/packages/transport-tcp/src/index.ts +++ b/packages/transport-tcp/src/index.ts @@ -1,3 +1,59 @@ +/** + * @packageDocumentation + * + * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on the TCP networking stack. + * + * @example + * + * ```js + * import { tcp } from '@libp2p/tcp' + * import { multiaddr } from '@multiformats/multiaddr' + * import { pipe } from 'it-pipe' + * import all from 'it-all' + * + * // A simple upgrader that just returns the MultiaddrConnection + * const upgrader = { + * upgradeInbound: async maConn => maConn, + * upgradeOutbound: async maConn => maConn + * } + * + * const transport = tcp()() + * + * const listener = transport.createListener({ + * upgrader, + * handler: (socket) => { + * console.log('new connection opened') + * pipe( + * ['hello', ' ', 'World!'], + * socket + * ) + * } + * }) + * + * const addr = multiaddr('/ip4/127.0.0.1/tcp/9090') + * await listener.listen(addr) + * console.log('listening') + * + * const socket = await transport.dial(addr, { upgrader }) + * const values = await pipe( + * socket, + * all + * ) + * console.log(`Value: ${values.toString()}`) + * + * // Close connection after reading + * await listener.close() + * ``` + * + * Outputs: + * + * ```sh + * listening + * new connection opened + * Value: hello World! + * ``` + */ + import net from 'net' import { AbortError, CodeError } from '@libp2p/interface/errors' import { type CreateListenerOptions, type DialOptions, symbol, type Transport, type Listener } from '@libp2p/interface/transport' diff --git a/packages/transport-webrtc/README.md b/packages/transport-webrtc/README.md index 2c0aa57200..c196f307fc 100644 --- a/packages/transport-webrtc/README.md +++ b/packages/transport-webrtc/README.md @@ -1,5 +1,3 @@ -# @libp2p/webrtc - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,40 +5,11 @@ > A libp2p transport using WebRTC connections -## Table of contents - -- [Install](#install) - - [Browser ` -``` +A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebRTC datachannels](https://webrtc.org/). -## Usage +## Example ```js import { createLibp2p } from 'libp2p' @@ -54,7 +23,7 @@ import { webRTC } from '@libp2p/webrtc' const node = await createLibp2p({ transports: [webRTC()], connectionEncryption: [noise()], -}); +}) await node.start() @@ -65,118 +34,31 @@ const response = await pipe([fromString(message)], stream, async (source) => awa const responseDecoded = toString(response.slice(0, response.length)) ``` -## Interfaces - -### Transport - -![https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/interface-transport](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/interface-transport/img/badge.png) - -Browsers can usually only `dial`, but `listen` is supported in the WebRTC -transport when paired with another listener like CircuitV2, where you listen on -a relayed connection. - -### Connection - -![https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/interface-connection](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/interface-connection/img/badge.png) - -```js -interface MultiaddrConnection extends Duplex { - close: (err?: Error) => Promise - remoteAddr: Multiaddr - timeline: MultiaddrConnectionTimeline -} - -class WebRTCMultiaddrConnection implements MultiaddrConnection { } -``` - -## Development - -Contributions are welcome! The libp2p implementation in JavaScript is a work in progress. As such, there's a few things you can do right now to help out: - -- [Check out the existing issues](//github.com/little-bear-labs/js-libp2p-webrtc/issues). -- **Perform code reviews**. -- **Add tests**. There can never be enough tests. -- Go through the modules and **check out existing issues**. This is especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically. - -Please be aware that all interactions related to libp2p are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). - -Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. - -This module leans heavily on (Aegir)\[] for most of the `package.json` scripts. - -### Build - -The build script is a wrapper to `aegir build`. To build this package: +# Install -```shell -npm run build -``` - -The build will be located in the `/dist` folder. - -### Protocol Buffers - -There is also `npm run generate:proto` script that uses protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files. - -### Test - -To run all tests: - -```shell -npm test -``` - -To run tests for Chrome only: - -```shell -npm run test:chrome -``` - -To run tests for Firefox only: - -```shell -npm run test:firefox -``` - -### Lint - -Aegir is also used to lint the code, which follows the [Standard](https://github.com/standard/standard) JS linter. -The VS Code plugin for this standard is located at . -To lint this repo: - -```shell -npm run lint -``` - -You can also auto-fix when applicable: - -```shell -npm run lint:fix +```console +$ npm i @libp2p/webrtc ``` -### Clean - -```shell -npm run clean -``` +## Browser ` ``` -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/transport-webrtc/src/index.ts b/packages/transport-webrtc/src/index.ts index a8bbf37e75..6e27a5da6d 100644 --- a/packages/transport-webrtc/src/index.ts +++ b/packages/transport-webrtc/src/index.ts @@ -1,3 +1,34 @@ +/** + * @packageDocumentation + * + * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebRTC datachannels](https://webrtc.org/). + * + * @example + * + * ```js + * import { createLibp2p } from 'libp2p' + * import { noise } from '@chainsafe/libp2p-noise' + * import { multiaddr } from '@multiformats/multiaddr' + * import first from 'it-first' + * import { pipe } from 'it-pipe' + * import { fromString, toString } from 'uint8arrays' + * import { webRTC } from '@libp2p/webrtc' + * + * const node = await createLibp2p({ + * transports: [webRTC()], + * connectionEncryption: [noise()], + * }) + * + * await node.start() + * + * const ma = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ') + * const stream = await node.dialProtocol(ma, ['/my-protocol/1.0.0']) + * const message = `Hello js-libp2p-webrtc\n` + * const response = await pipe([fromString(message)], stream, async (source) => await first(source)) + * const responseDecoded = toString(response.slice(0, response.length)) + * ``` + */ + import { WebRTCTransport } from './private-to-private/transport.js' import { WebRTCDirectTransport, type WebRTCTransportDirectInit, type WebRTCDirectTransportComponents } from './private-to-public/transport.js' import type { WebRTCTransportComponents, WebRTCTransportInit } from './private-to-private/transport.js' diff --git a/packages/transport-websockets/README.md b/packages/transport-websockets/README.md index 79d7aee6ea..7f61346cbb 100644 --- a/packages/transport-websockets/README.md +++ b/packages/transport-websockets/README.md @@ -1,5 +1,3 @@ -# @libp2p/websockets - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,27 +5,13 @@ > JavaScript implementation of the WebSockets module that libp2p uses and that implements the interface-transport spec -## Table of contents - -- [Install](#install) - - [Browser ` ``` -[![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)](https://github.com/libp2p/interface-transport) -[![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection) - -## Usage - -```sh -> npm i @libp2p/websockets -``` - -### Constructor properties - -```js -import { createLibp2pNode } from 'libp2p' -import { webSockets } from '@libp2p/webrtc-direct' - -const node = await createLibp2p({ - transports: [ - webSockets() - ] - //... other config -}) -await node.start() -await node.dial('/ip4/127.0.0.1/tcp/9090/ws') -``` - -| Name | Type | Description | Default | -| -------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| upgrader | [`Upgrader`](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/transport#upgrader) | connection upgrader object with `upgradeOutbound` and `upgradeInbound` | **REQUIRED** | -| filter | `(multiaddrs: Array) => Array` | override transport addresses filter | **Browser:** DNS+WSS multiaddrs / **Node.js:** DNS+\[WS, WSS] multiaddrs | - -You can create your own address filters for this transports, or rely in the filters [provided](./src/filters.js). - -The available filters are: - -- `filters.all` - - Returns all TCP and DNS based addresses, both with `ws` or `wss`. -- `filters.dnsWss` - - Returns all DNS based addresses with `wss`. -- `filters.dnsWsOrWss` - - Returns all DNS based addresses, both with `ws` or `wss`. - -## Libp2p Usage Example - -```js -import { createLibp2pNode } from 'libp2p' -import { websockets } from '@libp2p/websockets' -import filters from '@libp2p/websockets/filters' -import { mplex } from '@libp2p/mplex' -import { noise } from '@libp2p/noise' - -const transportKey = Websockets.prototype[Symbol.toStringTag] -const node = await Libp2p.create({ - transport: [ - websockets({ - // connect to all sockets, even insecure ones - filter: filters.all - }) - ], - streamMuxers: [ - mplex() - ], - connectionEncryption: [ - noise() - ] -}) -``` - -For more information see [libp2p/js-libp2p/doc/CONFIGURATION.md#customizing-transports](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-transports). - -## API - -### Transport - -[![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)](https://github.com/libp2p/interface-transport) - -### Connection - -[![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection) - -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/transport-websockets/src/index.ts b/packages/transport-websockets/src/index.ts index 932b1a042e..09dbec822e 100644 --- a/packages/transport-websockets/src/index.ts +++ b/packages/transport-websockets/src/index.ts @@ -1,3 +1,68 @@ +/** + * @packageDocumentation + * + * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). + * + * @example + * + * ```js + * import { createLibp2pNode } from 'libp2p' + * import { webSockets } from '@libp2p/webrtc-direct' + * + * const node = await createLibp2p({ + * transports: [ + * webSockets() + * ] + * //... other config + * }) + * await node.start() + * await node.dial('/ip4/127.0.0.1/tcp/9090/ws') + * ``` + * + * ## Filters + * + * When run in a browser by default this module will only connect to secure web socket addresses. + * + * To change this you should pass a filter to the factory function. + * + * You can create your own address filters for this transports, or rely in the filters [provided](./src/filters.js). + * + * The available filters are: + * + * - `filters.all` + * - Returns all TCP and DNS based addresses, both with `ws` or `wss`. + * - `filters.dnsWss` + * - Returns all DNS based addresses with `wss`. + * - `filters.dnsWsOrWss` + * - Returns all DNS based addresses, both with `ws` or `wss`. + * + * @example + * + * ```js + * import { createLibp2pNode } from 'libp2p' + * import { websockets } from '@libp2p/websockets' + * import filters from '@libp2p/websockets/filters' + * import { mplex } from '@libp2p/mplex' + * import { noise } from '@libp2p/noise' + * + * const transportKey = Websockets.prototype[Symbol.toStringTag] + * const node = await Libp2p.create({ + * transport: [ + * websockets({ + * // connect to all sockets, even insecure ones + * filter: filters.all + * }) + * ], + * streamMuxers: [ + * mplex() + * ], + * connectionEncryption: [ + * noise() + * ] + * }) + * ``` + */ + import { AbortError, CodeError } from '@libp2p/interface/errors' import { type Transport, type MultiaddrFilter, symbol, type CreateListenerOptions, type DialOptions, type Listener } from '@libp2p/interface/transport' import { logger } from '@libp2p/logger' diff --git a/packages/transport-webtransport/README.md b/packages/transport-webtransport/README.md index b5bb4e72d0..f3338b7948 100644 --- a/packages/transport-webtransport/README.md +++ b/packages/transport-webtransport/README.md @@ -1,5 +1,3 @@ -# @libp2p/webtransport - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,48 +5,11 @@ > JavaScript implementation of the WebTransport module that libp2p uses and that implements the interface-transport spec -## Table of contents - -- [Install](#install) - - [Browser ` -``` - -[![](https://raw.githubusercontent.com/libp2p/interface-transport/master/img/badge.png)](https://github.com/libp2p/interface-transport) -[![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection) - -## Description +# About -`libp2p-webtransport` is the WebTransport transport implementation compatible with libp2p. +A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebTransport](https://www.w3.org/TR/webtransport/). -## Usage - -```sh -> npm i @libp2p/webtransport -``` - -## Libp2p Usage Example +## Example ```js import { createLibp2pNode } from 'libp2p' @@ -65,30 +26,34 @@ const node = await createLibp2pNode({ }) ``` -For more information see [libp2p/js-libp2p/doc/CONFIGURATION.md#customizing-transports](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-transports). +# Install -## API +```console +$ npm i @libp2p/webtransport +``` -### Transport +## Browser ` +``` -[![](https://raw.githubusercontent.com/libp2p/interface-connection/master/img/badge.png)](https://github.com/libp2p/interface-connection) +For more information see [libp2p/js-libp2p/doc/CONFIGURATION.md#customizing-transports](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-transports). -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/transport-webtransport/src/index.ts b/packages/transport-webtransport/src/index.ts index d37e5a6564..33b272fee6 100644 --- a/packages/transport-webtransport/src/index.ts +++ b/packages/transport-webtransport/src/index.ts @@ -1,3 +1,26 @@ +/** + * @packageDocumentation + * + * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebTransport](https://www.w3.org/TR/webtransport/). + * + * @example + * + * ```js + * import { createLibp2pNode } from 'libp2p' + * import { webTransport } from '@libp2p/webtransport' + * import { noise } from 'libp2p-noise' + * + * const node = await createLibp2pNode({ + * transports: [ + * webTransport() + * ], + * connectionEncryption: [ + * noise() + * ] + * }) + * ``` + */ + import { noise } from '@chainsafe/libp2p-noise' import { type Transport, symbol, type CreateListenerOptions, type DialOptions, type Listener } from '@libp2p/interface/transport' import { logger } from '@libp2p/logger' diff --git a/packages/utils/README.md b/packages/utils/README.md index 4fbd0f1a5f..a8be91fe20 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -1,5 +1,3 @@ -# @libp2p/utils - [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p) @@ -7,22 +5,13 @@ > Package to aggregate shared logic and dependencies for the libp2p ecosystem -## Table of contents - -- [Install](#install) - - [Browser ` ``` -The libp2p ecosystem has lots of repos with it comes several problems like: - -- Domain logic dedupe - all modules shared a lot of logic like validation, streams handling, etc. -- Dependencies management - it's really easy with so many repos for dependencies to go out of control, they become outdated, different repos use different modules to do the same thing (like merging defaults options), browser bundles ends up with multiple versions of the same package, bumping versions is cumbersome to do because we need to go through several repos, etc. - -These problems are the motivation for this package, having shared logic in this package avoids creating cyclic dependencies, centralizes common use modules/functions (exactly like aegir does for the tooling), semantic versioning for 3rd party dependencies is handled in one single place (a good example is going from streams 2 to 3) and maintainers should only care about having `libp2p-utils` updated. - -## Usage - -Each function should be imported directly. - -```js -import ipAndPortToMultiaddr from '@libp2p/utils/ip-port-to-multiaddr' - -const ma = ipAndPortToMultiaddr('127.0.0.1', 9000) -``` - -You can check the [API docs](https://libp2p.github.io/js-libp2p-utils). - -## API Docs +# API Docs - -## License +# License Licensed under either of - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / ) - MIT ([LICENSE-MIT](LICENSE-MIT) / ) -## Contribution +# Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 336ce12bb9..cd0c2aa243 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1 +1,22 @@ +/** + * @packageDocumentation + * + * The libp2p ecosystem has lots of repos with it comes several problems like: + * + * - Domain logic dedupe - all modules shared a lot of logic like validation, streams handling, etc. + * - Dependencies management - it's really easy with so many repos for dependencies to go out of control, they become outdated, different repos use different modules to do the same thing (like merging defaults options), browser bundles ends up with multiple versions of the same package, bumping versions is cumbersome to do because we need to go through several repos, etc. + * + * These problems are the motivation for this package, having shared logic in this package avoids creating cyclic dependencies, centralizes common use modules/functions (exactly like aegir does for the tooling), semantic versioning for 3rd party dependencies is handled in one single place (a good example is going from streams 2 to 3) and maintainers should only care about having `libp2p-utils` updated. + * + * @example + * + * Each function should be imported directly. + * + * ```js + * import ipAndPortToMultiaddr from '@libp2p/utils/ip-port-to-multiaddr' + * + * const ma = ipAndPortToMultiaddr('127.0.0.1', 9000) + * ``` + */ + export {} diff --git a/typedoc.json b/typedoc.json index 55b1f87a5f..ecae64aac9 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,4 +1,8 @@ { "$schema": "https://typedoc.org/schema.json", - "name": "libp2p" + "name": "libp2p", + "exclude": [ + "interop", + "doc" + ] }