Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
An I2P SAM library: enabling applications to communicate through the I2P network.
I2P SAM
An I2P SAM library: enabling applications to communicate through the I2P network.
Long story short: I2P is an anonymous network layer allowing censorship-resistant and end-to-end-encrypted communication. I2P is a fully distributed, "privacy-by-design" peer-to-peer network.
To get I2P up and running, take a look at the project: https://github.com/diva-exchange/i2p
Use Cases
I2P is an instantly available peer-to-peer network which can be used for things like:
- chat, social media and alike - all private and secure
- distributed databases, aka blockchains (see https://testnet.diva.exchange as an example)
- gaming, file sharing and ... whatever else you come up with
I2P is fully distributed, well researched and gets further developed by a competent community.
This I2P SAM library helps developers to create an I2P application quickly and hassle-free.
Get Started
deno add @i2p/sam
Quick Start - Examples
How to Use Streams
Send an HTTP GET request to diva.i2p and output the response:
import { createStream } from '@i2p/sam'; (async () => { const s = await createStream({ stream: { destination: 'diva.i2p' }, sam: { // your local I2P SAM host, // like 172.19.74.11 if you use the given test // docker container (see "Unit Tests" below) host: '127.0.0.1', // your local I2P SAM port, this is the default portTCP: 7656 }, }); s.on('data', (data: Buffer) => { console.log('Incoming Data: ' + data.toString()); }); s.stream(Buffer.from('GET /hosts.txt HTTP/1.1\r\nHost: diva.i2p\r\n\r\n')); })();
Forward incoming streaming data to a local socket server:
import { createStream, createForward, I2pSamStream } from '@i2p/sam'; import net from 'net'; (async () => { const serverForward = net.createServer((c) => { console.debug('client connected'); c.on('end', () => { console.debug('client disconnected'); }); c.on('data', (data: Buffer) => { console.debug(data.toString()); c.write(`Hello Client!\n`); }); }); serverForward.listen(20222, '127.0.0.2'); const samForward: I2pSamStream = await createForward({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port }, forward: { host: '127.0.0.2', // your local listener, see above port: 20222, // your local listener, see above }, }); const samClient: I2pSamStream = await createStream({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port }, stream: { destination: samForward.getPublicKey() }, }); // event handler samClient.on('data', (data: Buffer) => { console.debug(data.toString()); }); // send some data to destination samClient.stream(Buffer.from(`Hi Server!\n`)); })();
How to Use Reply-able Datagrams
NOTE: reply-able datagrams contain the origin of the data. An "origin" is defined as the public key of a node in the I2P network.
Send reply-able UDP messages from peer A to peer B through the I2P network:
import { createDatagram, toB32 } from '@i2p/sam'; (async () => { // instantiate Peer A const peerA = await createDatagram({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }); // instantiate Peer B const peerB = await createDatagram({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port }, listen: { address: '127.0.0.1', // udp listener port: 20202 // udp listener } }).on('data', (data: Buffer, from) => { console.debug(`Incoming Data from ${toB32(from)}: ${data.toString()}`); }); // send 100 messages via UDP, every 500ms a message // IMPORTANT: UDP is not reliable. Some messages might get lost. const msg: string = 'Hello World'; await new Promise((resolve) => { let t = 0; const i = setInterval(() => { peerA.send(peerB.getPublicKey(), Buffer.from(`${t} ${msg}`)); if (t++ >= 100) { clearInterval(i); resolve(true); } }, 500); }); })();
How to Use Raw Datagrams
NOTE: raw datagrams do not contain the "origin" of the data. A typical use case for raw datagrams: broadcasting of data. Raw datagrams are lean.
Send raw UDP messages from peer A to peer B through the I2P network:
import { createRaw } from '@i2p/sam'; (async () => { // instantiate Peer A const peerA = await createRaw({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }); // instantiate Peer B const peerB = await createRaw({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port }, listen: { address: '127.0.0.1', // udp listener port: 20202 // udp listener } }).on('data', (data: Buffer) => { console.log('Incoming Data: ' + data.toString()); }); // send 100 messages via UDP, every 500ms a message // IMPORTANT: UDP is not reliable. Some messages might get lost. const msg: string = 'Hello Peer B - I am Peer A'; await new Promise((resolve) => { let t = 0; const i = setInterval(() => { peerA.send(peerB.getPublicKey(), Buffer.from(`${t} ${msg}`)); if (t++ >= 100) { clearInterval(i); resolve(true); } }, 500); }); })();
API
getPublicKey(): string
Get the public key of the local destination.
Example:
import { createDatagram } from '@i2p/sam'; createDatagram({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }).then((sam) => console.log(sam.getPublicKey()));
getPrivateKey(): string
Get the private key of the local destination.
Example:
import { createDatagram } from '@i2p/sam'; createDatagram({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }).then((sam) => console.log(sam.getPrivateKey()));
getKeyPair(): { public: string, private: string }
Get the public and private key of the local destination.
Example:
import { createStream } from '@i2p/sam'; createStream({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port }, stream: { destination: 'diva.i2p' }, }).then((sam) => console.log(sam.getKeyPair()));
close()
Close a SAM connection.
Example:
import { createRaw } from '@i2p/sam'; (async () => { const sam = await createRaw({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }); sam.close(); })();
toB32(destination: string): string
Convert a destination to a b32 address (without any extensions - just a Base32 string).
Example:
import { toB32 } from '@i2p/sam'; console.log(toB32('[some base64-encoded destination]'));
createLocalDestination(c: Configuration): Promise<{ address: string, public: string, private: string }>
Create a new local destination and return its properties.
Example:
import { createLocalDestination } from '@i2p/sam'; createLocalDestination({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }).then((obj) => console.log(obj));
lookup(c: Configuration, name: string): Promise<string>
Lookup (aka resolve) an I2P address (like diva.i2p or also a .b32.i2p address) to a destination. The destination, which is the public key, is a base64 encoded string.
Example:
import { lookup } from '@i2p/sam'; lookup({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }, 'diva.i2p').then((dest) => console.log(dest));
stream(msg: Buffer)
Example: see the Get Started: How to Use Streams above.
send(destination: string, msg: Buffer)
Example: see Get Started: How to Use Datagrams above.
Configuration and its Defaults
type tSession = { id?: string; options?: string; }; type tStream = { destination: string; }; type tForward = { host: string; port: number; silent?: boolean; }; type tListen = { address: string; port: number; hostForward?: string; portForward?: number; }; type tSam = { host: string; portTCP: number; portUDP?: number; versionMin?: string; versionMax?: string; publicKey?: string; privateKey?: string; timeout?: number; }; export type Configuration = { session?: tSession; stream?: tStream; forward?: tForward; listen?: tListen; sam?: tSam; }; type ConfigurationDefault = { session: tSession; stream: tStream; forward: tForward; listen: tListen; sam: tSam; }; const DEFAULT_CONFIGURATION: ConfigurationDefault = { session: { id: '', options: '', }, stream: { destination: '', }, forward: { host: '', port: 0, silent: false, }, listen: { address: '127.0.0.1', port: 0, hostForward: '', portForward: 0, }, sam: { host: '127.0.0.1', portTCP: 7656, portUDP: 7655, versionMin: '', versionMax: '', publicKey: '', privateKey: '', timeout: 300, }, };
Events
data
Incoming data.
error
Generic Error event - emitted if sockets report errors.
import { createRaw } from '@i2p/sam'; (async () => { const sam = await createRaw({ sam: { host: '127.0.0.1', // your local I2P SAM host portTCP: 7656 // your local I2P SAM port } }); sam.on('error', (error) => console.debug(error)); })();
close
Emitted if one of the involved sockets got closed.
How to Run Unit Tests
Assumptions:
- git and deno is available.
- docker and docker-compose is available.
Clone the source code from git
git clone https://github.com/diva-exchange/i2p-sam.git and enter the folder
i2p-sam.
Prepare the test environment by creating the docker container:
docker compose -f test/sam.diva.i2p.yml up -d
Check whether the I2P test node is properly running by accessing the local console on: http://172.19.74.11:7070.
To modify the IP address of the local console, adapt the file
test/sam.diva.i2p.yml.
After the docker container is running for about five minutes (reason: the I2P network needs some minutes to integrate), execute the unit tests:
deno task test
Executing the unit tests will take around 5 minutes. Reason: the communication via I2P gets tested - which is the purpose of this library.
Stop the container (and purge all data within):
docker compose -f test/sam.diva.i2p.yml down --volumes
Linting
To lint the code, use
deno task lint
Contributions
Contributions are very welcome. This is the general workflow:
- Fork from https://github.com/diva-exchange/i2p-sam/
- Pull the forked project to your local developer environment
- Make your changes, test, commit and push them
- Create a new pull request on github.com
It is strongly recommended to sign your commits: https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key
If you have questions, please just contact us (see below).
Donations
Your donation goes entirely to the project. Your donation makes the development of DIVA.EXCHANGE faster. Thanks a lot.
XMR
42QLvHvkc9bahHadQfEzuJJx4ZHnGhQzBXa8C9H3c472diEvVRzevwpN7VAUpCPePCiDhehH4BAWh8kYicoSxpusMmhfwgx

or via https://www.diva.exchange/en/join-in/
BTC
3Ebuzhsbs6DrUQuwvMu722LhD8cNfhG1gs

Contact the Developers
On DIVA.EXCHANGE you'll find various options to get in touch with the team.
Talk to us via Telegram (English or German).
References
SAM docs: https://geti2p.net/en/docs/api/samv3
I2Pd: https://i2pd.readthedocs.io/
License
Add Package
deno add jsr:@i2p/sam
Import symbol
import * as sam from "@i2p/sam";
Import directly with a jsr specifier
import * as sam from "jsr:@i2p/sam";
Add Package
pnpm i jsr:@i2p/sam
pnpm dlx jsr add @i2p/sam
Import symbol
import * as sam from "@i2p/sam";
Add Package
yarn add jsr:@i2p/sam
yarn dlx jsr add @i2p/sam
Import symbol
import * as sam from "@i2p/sam";
Add Package
vlt install jsr:@i2p/sam
Import symbol
import * as sam from "@i2p/sam";
Add Package
npx jsr add @i2p/sam
Import symbol
import * as sam from "@i2p/sam";
Add Package
bunx jsr add @i2p/sam
Import symbol
import * as sam from "@i2p/sam";