Skip to content

latticeHQ/md-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@latticeruntime/md-client

Client library for openagent.md encrypted file sharing with signature support.

Inspired by Coder's approach to encrypted file sharing. Built with Claude.

Installation

npm install @latticeruntime/md-client
# or
bun add @latticeruntime/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 '@latticeruntime/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 } // 7 days
);

console.log(result.url);       // https://openagent.md/Ab3Xy#key...
console.log(result.mutateKey); // Store for deletion/expiration updates

Download content

import { download } from '@latticeruntime/md-client';

const { data, info, signature } = await download('https://openagent.md/Ab3Xy#key...');
console.log(new TextDecoder().decode(data));

if (signature) {
  console.log('Signed by:', signature.publicKey);
}

Upload with signature

Signed uploads embed plaintext as JSON ({ content: string, sig: SignatureEnvelope }), so the content must be valid UTF-8 text.

Option A: let the library create the signature

import { upload } from '@latticeruntime/md-client';

const content = new TextEncoder().encode('# Signed Message');

const result = await upload(
  content,
  { name: 'signed.md', type: 'text/markdown', size: content.length },
  {
    sign: {
      privateKey, // Uint8Array (Ed25519: 32 bytes)
      publicKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...', // OpenSSH public key
      githubUser: 'username', // optional attribution
    },
  }
);

Option B: provide a precomputed SignatureEnvelope

import { upload, createSignatureEnvelope } from '@latticeruntime/md-client';

const content = new TextEncoder().encode('# Signed Message');

const signature = await createSignatureEnvelope(
  content,
  privateKey,
  'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...',
  { githubUser: 'username' }
);

const result = await upload(
  content,
  { name: 'signed.md', type: 'text/markdown', size: content.length },
  { signature }
);

Option C (Node.js): sign via SSH agent (e.g., 1Password)

import { upload } from '@latticeruntime/md-client';
import { createSshAgentSigner } from '@latticeruntime/md-client/ssh-agent';

const content = new TextEncoder().encode('# Signed Message');

const signer = await createSshAgentSigner({ githubUser: 'username' });

const result = await upload(
  content,
  { name: 'signed.md', type: 'text/markdown', size: content.length },
  { sign: { signer } }
);

Verify signatures

import { parsePublicKey, verifySignature, base64Decode } from '@latticeruntime/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 '@latticeruntime/md-client';

// Delete file
await deleteFile(result.id, result.mutateKey);

// Update expiration
await setExpiration(result.id, result.mutateKey, Date.now() + 30 * 24 * 60 * 60 * 1000);

// Remove expiration
await setExpiration(result.id, result.mutateKey, 'never');

API Reference

Client Operations

Function Description
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 openagent.md URL into id + key
buildUrl(id, key, baseUrl?) Build openagent.md URL from components

Signing

Function Description
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

Node-only SSH agent helpers

These helpers are available from @latticeruntime/md-client/ssh-agent (Node.js only):

Function Description
createSshAgentSigner(options?) Create a signer callback compatible with upload(..., { sign: { signer } })
createSshAgentSignatureEnvelope(data, options?) One-shot helper that returns a SignatureEnvelope

Types

interface FileInfo {
  name: string;
  type: string;
  size: number;
  model?: string;    // AI model (e.g., "claude-sonnet-4-20250514")
  thinking?: string; // Thinking level (e.g., "medium")
}

interface SignatureEnvelope {
  sig: string;        // Base64-encoded signature
  publicKey: string;  // SSH format public key
  githubUser?: string; // Optional: claimed GitHub username for attribution
}

type KeyType = 'ed25519' | 'ecdsa-p256' | 'ecdsa-p384' | 'ecdsa-p521';

Publishing

npm publish --access public

License

MIT

Built with Claude, inspired by Coder.

About

Encrypted file sharing client for the Lattice ecosystem — openagent.md protocol

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors