Introducing EdgePort

devtypescriptcloudflare
Introducing EdgePort
6 min. read

I built my own TCP Library, edgeport. Let's talk about what it can do.

What is it?

edgeport is a TCP connection library for the Cloudflare Workers runtime, built solely on the cloudflare:sockets module from the ground up. Cloudflare advertises that it can be used for things like SSH, FTP, NATS, STOMP, LDAP, and other TCP protocols, but there isn't a widely maintained library that wraps around this; it's all just the raw socket header.

So I decided to build my own.

Why?

Two projects I'm working on would benefit from this.

earth-app/smoke

With the release of sky on the iOS app store, I was working on my own customer support software in Cloudflare Workers. Cloudflare's Email Routing service is an inbound-only, reply-only service that couldn't have agents send email notifications in threads, which was a real limitation. Cloudflare has a different application called Email Service that allows you to use your API token as SMTP credentials, but I would still need an SMTP client to actually use it. EdgePort is that SMTP client.

I actually discovered this mid-way through making it; I didn't know Cloudflare had its own freely available SMTP server, and I may switch The Earth App to using it instead of paying for Mailersend, depending on the mechanics and whether it would be worth migrating now, especially as growth continues to go up.

MyLoRA

MyLoRA is a new personal project for managing your own LoRA adapters. As I get ready for college, I turned my gaming PC into a homelab, repurposing my RTX 4070 for training models on small datasets, mostly my own personal documents and information (like my debate cases), to do some experiments.

I wanted to build a feature where it could set up that training automatically - I built doc2lora under The Earth App as a tool to generate it based on said documents rather than writing an entire python file and going through that whole process; so I was hoping to turn it into a UI. That requires some kind of SSH and SFTP/FTP protocol implementation, and it would've required me to recreate it inside the library itself. So I made edgeport instead.

Small rant: Cloudflare doesn't have a single-GET or DELETE endpoints for LoRA adapter upload, which is a genuine gap in the API and a real bug that I tried to report, and they kind of brushed me off in the actual support (having workers paid != paid cloudflare account); guess I just needed to do a GitHub issue submission, which was updated as of writing it and submitting it a few days ago, but he threw it in the Backlog initially, so we'll see.

Snippets

Taken from the README itself:

import { exec } from 'edgeport/ssh';

const { stdout, stderr, code } = await exec({
	hostname: 'host',
	username: 'user',
	privateKey: { pem: env.SSH_KEY }, // PKCS8 PEM, or pass a CryptoKey
	command: 'ls -la /var/log'
});

Simple one-shot executable over SSH with passed credentials. Solves one half MyLoRA use case directly, and opens the door for your worker to communicate with other machines and do automated tasks on a serverless platform.

import { connect, getFile, putFile } from 'edgeport/sftp';

// one-shots
const bytes = await getFile({ hostname: 'h', username: 'u', password: p, path: '/etc/hostname' });
await putFile({ hostname: 'h', username: 'u', password: p, path: '/tmp/x', data: bytes });

// a session
await using sftp = await connect({ hostname: 'h', username: 'u', password: p });
await sftp.mkdir('/tmp/reports');
await sftp.writeFile('/tmp/reports/today.csv', new TextEncoder().encode('a,b,c\n'));
for (const entry of await sftp.list('/tmp/reports')) {
	console.log(entry.filename, entry.attrs.size);
}

// stream a large download
const stream = sftp.createReadStream('/var/log/big.log');

Other half of the MyLoRA problem solved: EdgePort uses SSH's utilities to connect via SFTP, meaning I would be able to transfer over my training data, poll, and then download the safetensors output to upload to my finetune catalog in the Cloudflare API. v1.0.1 comes with additonal hardening that helps with resuming an existing download/upload task if connections become wonky.

import { send } from 'edgeport/smtp';
import { connect as imapConnect } from 'edgeport/imap';
import { search as ldapSearch } from 'edgeport/ldap';
import { connect as syslogConnect, Severity } from 'edgeport/syslog';

const auth = { username: env.MAIL_USER, password: env.MAIL_PW };
await using audit = await syslogConnect({
	hostname: env.SIEM,
	port: 6514,
	tls: 'implicit',
	appName: 'mta'
});

// 1. validate the recipient against the directory (reject unknown addresses)
const found = await ldapSearch({
	hostname: env.LDAP,
	bindDN: env.SVC_DN,
	password: env.SVC_PW,
	base: 'ou=people,dc=example,dc=org',
	filter: `(mail=${recipient})`
});
if (found.length === 0) {
	await audit.log({
		severity: Severity.warning,
		message: `rejected unknown recipient ${recipient}`
	});
	throw new Error('unknown recipient');
}

// 2. submit (tls:'off' for a trusted internal relay; or 587/STARTTLS, 465/implicit)
await send({
	hostname: env.MAIL,
	tls: 'off',
	auth,
	from: 'bot@example.org',
	to: recipient,
	subject: 'Hi',
	text: 'body'
});
await audit.log({ severity: Severity.info, message: `delivered to ${recipient}` });

// 3. the same message is visible via IMAP and POP3; a POP3 read (no DELE) doesn't disturb IMAP
await using imap = await imapConnect({ hostname: env.MAIL, tls: 'off', auth });
await imap.select('INBOX');
const uids = await imap.search({ subject: 'Hi' });
const messages = await imap.fetch(uids, { body: true });

This one's a longer one, and helps with the potential for the Email Service stack. Using SMTP, POP3, and IMAP, you could create a fullstack read+write email service, either with your existing server setup or entirely within Cloudflare's ecosystem around workers and the Email Service. Pretty cool stuff.

import { connect as ldapConnect } from 'edgeport/ldaps';
import { connect as wsConnect } from 'edgeport/ws';
import { connect as natsConnect } from 'edgeport/nats';
import { connect as mqttConnect } from 'edgeport/mqtt';

// 1. authenticate the user (LDAP bind is the login gate)
await using dir = await ldapConnect({
	hostname: env.LDAP,
	bindDN: `uid=${user},ou=people,dc=example,dc=org`,
	password
});

// 2. presence via an MQTT last-will: if this client drops, the broker announces 'offline'
await using presence = await mqttConnect({
	hostname: env.BROKER,
	clientId: user,
	will: { topic: `presence/${user}`, payload: 'offline', qos: 1, retain: true }
});
await presence.publish(`presence/${user}`, 'online', { retain: true });

// 3. fan messages out across chat servers with a queue group (one server handles each)
await using nc = await natsConnect({ hostname: env.NATS, token: env.NATS_TOKEN });
const room = nc.subscribe('room.general', { queue: 'chat-servers' });

// 4. bridge the client's WebSocket into the bus
const ws = await wsConnect(`wss://${env.GW}/chat?token=${sessionToken}`);
for await (const frame of ws) {
	if (frame.type === 'text') await nc.publish('room.general', frame.data);
}

Last big snippet: I decided to add a websocket module that could be used, alongside some more niche TCP protocols like MQTT and NATS, just for general use. This one details a chat app you could build based on your LDAP configurations and available software you could host.

Conclusion

This was a nice little side project to architect and build. Testing was probably my favorite part: I intentionally had it wrap up the connect function around cloudflare:sockets, and then had Claude design the actual wrappers and protocols, and then I helped orient tests around real-world recipes like that. That's what I think AI coding should be used: you scaffhold, and ask it to build very specific things or boilerplate you already know how to write. This is essentially like when I learned how to hand-send and intercept NMS Packets in Minecraft in 2022, just on a different platform.

Copyright © 2025 NuxtPress. All Rights Reserved.