@coder/mux-md-client
Client library for mux.md encrypted file sharing with signature support.
Installation
npm install @coder/mux-md-client
bun add @coder/mux-md-client
Features
- End-to-end encryption — AES-256-GCM with HKDF key derivation
- Message signing — Ed25519 and ECDSA (P-256, P-384, P-521) support
- SSH key format — Parse and use standard OpenSSH public keys
- Zero-knowledge — Server never sees plaintext content or signatures
- Minimal dependencies — Only
@noble/* for cryptographic primitives
Usage
Upload content
import { upload } from '@coder/mux-md-client';
const content = new TextEncoder().encode('# Hello World');
const result = await upload(
content,
{ name: 'message.md', type: 'text/markdown', size: content.length },
{ expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000 }
);
console.log(result.url);
console.log(result.mutateKey);
Download content
import { download } from '@coder/mux-md-client';
const { data, info, signature } = await download('https://mux.md/Ab3Xy#key...');
console.log(new TextDecoder().decode(data));
if (signature) {
console.log('Signed by:', signature.publicKey);
}
Upload with signature
import { upload, createSignatureEnvelope } from '@coder/mux-md-client';
const content = new TextEncoder().encode('# Signed Message');
const signature = await createSignatureEnvelope(
content,
privateKey,
'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...',
{ email: 'user@example.com' }
);
const result = await upload(
content,
{ name: 'signed.md', type: 'text/markdown', size: content.length },
{ signature }
);
Verify signatures
import { parsePublicKey, verifySignature, base64Decode } from '@coder/mux-md-client';
const parsedKey = parsePublicKey('ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...');
const sigBytes = base64Decode(signature.sig);
const messageBytes = new TextEncoder().encode(content);
const valid = await verifySignature(parsedKey, messageBytes, sigBytes);
Delete or update expiration
import { deleteFile, setExpiration } from '@coder/mux-md-client';
await deleteFile(result.id, result.mutateKey);
await setExpiration(result.id, result.mutateKey, Date.now() + 30 * 24 * 60 * 60 * 1000);
await setExpiration(result.id, result.mutateKey, 'never');
API Reference
Client Operations
upload(data, fileInfo, options?) | Encrypt and upload content |
download(url, key?, options?) | Download and decrypt content |
getMeta(url, key?, options?) | Get file metadata without downloading |
deleteFile(id, mutateKey, options?) | Delete a file |
setExpiration(id, mutateKey, expiresAt, options?) | Update file expiration |
parseUrl(url) | Parse mux.md URL into id + key |
buildUrl(id, key, baseUrl?) | Build mux.md URL from components |
Signing
createSignatureEnvelope(content, privateKey, publicKey, options?) | Create a signature envelope for upload |
signEd25519(message, privateKey) | Sign with Ed25519 |
signECDSA(message, privateKey, curve) | Sign with ECDSA |
verifySignature(parsedKey, message, signature) | Verify a signature |
parsePublicKey(keyString) | Parse SSH public key |
computeFingerprint(publicKey) | Compute SHA256 fingerprint |
formatFingerprint(fingerprint) | Format fingerprint for display |
Types
interface FileInfo {
name: string;
type: string;
size: number;
model?: string;
thinking?: string;
}
interface SignatureEnvelope {
sig: string;
publicKey: string;
email?: string;
githubUser?: string;
}
type KeyType = 'ed25519' | 'ecdsa-p256' | 'ecdsa-p384' | 'ecdsa-p521';
Publishing
This package uses NPM Trusted Publishing with OIDC for automated CI/CD deployments.
No NPM tokens are required - authentication happens via GitHub Actions OIDC.
Initial setup (one-time, requires npm account with publish access to @coder scope):
-
Create the package on npm with an initial publish:
cd packages/client
npm publish --access public
-
Configure trusted publisher on npmjs.com:
-
Subsequent publishes happen automatically when packages/client/** changes are pushed to main.
License
MIT