An SFTP client with structured logging.
npm install unnbound-sftp-sdkA TypeScript SFTP client with structured logging and distributed tracing capabilities, built on top of ssh2-sftp-client and integrated with the Unnbound logging system.
- Full SFTP Operations: Complete set of SFTP operations including file transfer, directory management, and file system operations
- Structured Logging: Built-in integration with Unnbound Logger for comprehensive operation tracking
- Distributed Tracing: Automatic span creation for all SFTP operations with detailed payload information
- Type Safety: Full TypeScript support with comprehensive type definitions
- Performance Monitoring: Built-in metrics collection for file sizes, operation counts, and timing
- Flexible Configuration: Support for various authentication methods and connection options
``bash`
npm install unnbound-sftp-sdkor
pnpm add unnbound-sftp-sdkor
yarn add unnbound-sftp-sdk
`typescript
import { UnnboundSftpClient } from 'unnbound-sftp-sdk';
import { logger } from 'unnbound-logger-sdk';
const sftp = new UnnboundSftpClient({
host: 'your-sftp-server.com',
port: 22,
username: 'your-username',
privateKey: 'your-private-key', // or use password
});
// Connect to the SFTP server
await sftp.connect();
// Get current working directory
const currentPath = await sftp.cwd();
console.log('Current path:', currentPath);
// Create a directory
await sftp.mkdir('./uploads', true); // recursive
// Upload a file
const fileContent = Buffer.from('Hello, World!');
await sftp.put(fileContent, './uploads/hello.txt');
// Download a file
const downloadedContent = await sftp.get('./uploads/hello.txt');
console.log('Downloaded:', downloadedContent.toString());
// List files in a directory
const files = await sftp.list('./uploads');
console.log(
'Files:',
files.map((f) => f.name)
);
// Close the connection
await sftp.close();
`
`typescript`
new UnnboundSftpClient(options: UnnboundSftpClientOptions)
#### Options
| Property | Type | Required | Description |
| ------------------- | ---------------- | -------- | ----------------------------------------------------- |
| host | string | ✅ | SFTP server hostname |port
| | number | ❌ | SFTP server port (default: 22) |username
| | string | ❌ | Username for authentication |password
| | string | ❌ | Password for authentication |privateKey
| | string | ❌ | Private key for key-based authentication |passphrase
| | string | ❌ | Passphrase for encrypted private keys |localHostname
| | string | ❌ | Local hostname for hostbased authentication |localUsername
| | string | ❌ | Local username for hostbased authentication |keepaliveInterval
| | number | ❌ | SSH keepalive interval in milliseconds (0 to disable) |keepaliveCountMax
| | number | ❌ | Max unanswered keepalive packets before disconnection |readyTimeout
| | number | ❌ | SSH handshake timeout in milliseconds |localAddress
| | string | ❌ | Local network interface IP address |localPort
| | number | ❌ | Local port number |timeout
| | number | ❌ | Socket timeout in milliseconds |ident
| | string | ❌ | Server software identifier (default: 'unnbound') |logger
| | UnnboundLogger | ❌ | Custom logger instance (uses default if not provided) |
#### connect(): Promise
Establishes a connection to the SFTP server.
`typescript`
await sftp.connect();
#### close(): Promise
Closes the SFTP connection.
`typescript`
const closed = await sftp.close();
#### get(path: string): Promise
Downloads a file from the remote server.
`typescript`
const content = await sftp.get('/remote/path/file.txt');
#### put(input: Buffer, path: string): Promise
Uploads a buffer to the remote server.
`typescript`
const data = Buffer.from('file content');
await sftp.put(data, '/remote/path/file.txt');
#### append(input: Buffer | NodeJS.ReadableStream, path: string): Promise
Appends data to an existing file.
`typescript`
const additionalData = Buffer.from('more content');
await sftp.append(additionalData, '/remote/path/file.txt');
#### list(path: string, filter?: ListFilterFunction): Promise
Lists files and directories in the specified path.
`typescript`
const files = await sftp.list('/remote/directory');
const onlyFiles = await sftp.list('/remote/directory', (item) => item.type === '-');
#### mkdir(path: string, recursive?: boolean): Promise
Creates a directory.
`typescript`
await sftp.mkdir('/remote/new-directory');
await sftp.mkdir('/remote/nested/path', true); // recursive
#### rmdir(path: string, recursive?: boolean): Promise
Removes a directory.
`typescript`
await sftp.rmdir('/remote/empty-directory');
await sftp.rmdir('/remote/directory-with-files', true); // recursive
#### uploadDir(sourcePath: string, destinationPath: string, options?: UploadDirOptions): Promise
Uploads an entire directory.
`typescript`
await sftp.uploadDir('./local/directory', '/remote/destination');
#### downloadDir(sourcePath: string, destinationPath: string, options?: DownloadDirOptions): Promise
Downloads an entire directory.
`typescript`
await sftp.downloadDir('/remote/directory', './local/destination');
#### exists(path: string): Promise
Checks if a file or directory exists.
`typescript`
const exists = await sftp.exists('/remote/path/file.txt');
// Returns: false, 'd' (directory), '-' (file), or 'l' (symlink)
#### stat(path: string): Promise
Gets file or directory statistics.
`typescript`
const stats = await sftp.stat('/remote/path/file.txt');
console.log('Size:', stats.size);
console.log('Modified:', stats.modifyTime);
#### realPath(path: string): Promise
Resolves the real path of a file or directory.
`typescript`
const realPath = await sftp.realPath('/remote/symlink');
#### cwd(): Promise
Gets the current working directory.
`typescript`
const currentDir = await sftp.cwd();
#### delete(path: string, noErrorOK?: boolean): Promise
Deletes a file.
`typescript`
await sftp.delete('/remote/path/file.txt');
#### rename(sourcePath: string, destinationPath: string): Promise
Renames or moves a file or directory.
`typescript`
await sftp.rename('/remote/old-name.txt', '/remote/new-name.txt');
#### chmod(path: string, mode: number | string): Promise
Changes file or directory permissions.
`typescript`
await sftp.chmod('/remote/path/file.txt', 0o644);
await sftp.chmod('/remote/path/file.txt', '644');
`typescript`
interface FileInfo {
type: FileInfoType; // 'd' | '-' | 'l' (directory, file, symlink)
name: string;
size: number;
modifyTime: number;
accessTime: number;
rights: {
user: string;
group: string;
other: string;
};
owner: number;
group: number;
}
`typescript`
interface FileStats {
mode: number;
uid: number;
gid: number;
size: number;
accessTime: number;
modifyTime: number;
isDirectory: boolean;
isFile: boolean;
isBlockDevice: boolean;
isCharacterDevice: boolean;
isSymbolicLink: boolean;
isFIFO: boolean;
isSocket: boolean;
}
`typescript
interface DirOptions {
filter?: DirFilterFunction;
}
interface UploadDirOptions extends DirOptions {
useFastput?: boolean;
}
interface DownloadDirOptions extends DirOptions {
useFastget?: boolean;
}
`
The Unnbound SFTP Client automatically creates spans for all operations and logs detailed information including:
- Operation type and timing
- File paths and sizes
- Host information
- Error details (if any)
- File counts for directory operations
- Content previews for file operations
- Byte counts for transfers
You can provide a custom logger instance:
`typescript
import { UnnboundSftpClient } from 'unnbound-sftp-sdk';
import { logger } from 'unnbound-logger-sdk';
const sftp = new UnnboundSftpClient({
host: 'sftp.example.com',
username: 'user',
password: 'pass',
logger: logger, // Custom logger instance
});
`
`json`
{"level":"info","spanId":"556c53e6-3bb8-4118-b3b8-570e41c27654","type":"sftp","sftp":{"host":"sftp.domain.com","operation":"connect"},"message":"SFTP connect started."}
{"level":"info","spanId":"556c53e6-3bb8-4118-b3b8-570e41c27654","type":"sftp","sftp":{"host":"sftp.domain.com","operation":"connect"},"duration":2771,"message":"SFTP connect completed."}
{"level":"info","spanId":"556c53e6-3bb8-4118-b3b8-570e41c27654","type":"sftp","sftp":{"host":"sftp.domain.com","operation":"put","path":"/uploads/file.txt","bytes":1024,"content":"Hello World"},"message":"SFTP put started."}
{"level":"info","spanId":"556c53e6-3bb8-4118-b3b8-570e41c27654","type":"sftp","sftp":{"host":"sftp.domain.com","operation":"put","path":"/uploads/file.txt","bytes":1024,"content":"Hello World"},"duration":150,"message":"SFTP put completed."}
All methods return promises that will reject with detailed error information:
`typescript`
try {
await sftp.get('/non-existent-file.txt');
} catch (error) {
console.error('SFTP operation failed:', error.message);
// Error details are automatically logged with the span
}
`typescript`
const sftp = new UnnboundSftpClient({
host: 'sftp.example.com',
username: 'myuser',
password: 'mypassword',
});
`typescript-----BEGIN OPENSSH PRIVATE KEY-----
const sftp = new UnnboundSftpClient({
host: 'sftp.example.com',
username: 'myuser',
privateKey:
...your private key content...
-----END OPENSSH PRIVATE KEY-----,`
});
`typescript`
const sftp = new UnnboundSftpClient({
host: 'sftp.example.com',
username: 'myuser',
privateKey: 'your-private-key',
passphrase: 'your-passphrase',
});
The client automatically normalizes private keys from environment variables by converting \n escape sequences to actual newlines:
`typescript
// Environment variable: PRIVATE_KEY="-----BEGIN OPENSSH PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END OPENSSH PRIVATE KEY-----"
const sftp = new UnnboundSftpClient({
host: 'sftp.example.com',
username: 'myuser',
privateKey: process.env.PRIVATE_KEY, // Automatically normalized
});
`
- Node.js >= 22.0.0
- TypeScript (for TypeScript projects)
- ssh2-sftp-client: Core SFTP functionalityunnbound-logger-sdk`: Structured logging and tracing
-
For support, please open an issue on the GitHub repository.