SFTP Backend¶
The SFTP backend provides access to remote servers via SSH File Transfer Protocol (SFTP). It supports both password and SSH key authentication.
Installation¶
Usage¶
Password Authentication¶
backend, err := sftp.New(sftp.Config{
Host: "example.com",
User: "username",
Password: "password",
})
defer backend.Close()
SSH Key Authentication¶
backend, err := sftp.New(sftp.Config{
Host: "example.com",
User: "username",
KeyFile: "/path/to/id_rsa",
})
With Encrypted Key¶
backend, err := sftp.New(sftp.Config{
Host: "example.com",
User: "username",
KeyFile: "/path/to/id_rsa",
KeyPassphrase: "keypassword",
})
From Environment Variables¶
Environment variables:
OMNISTORAGE_SFTP_HOST- Server hostnameOMNISTORAGE_SFTP_PORT- SSH port (default: 22)OMNISTORAGE_SFTP_USER- UsernameOMNISTORAGE_SFTP_PASSWORD- PasswordOMNISTORAGE_SFTP_KEY_FILE- Path to private keyOMNISTORAGE_SFTP_KEY_PASSPHRASE- Key passphraseOMNISTORAGE_SFTP_ROOT- Base directoryOMNISTORAGE_SFTP_KNOWN_HOSTS- Path to known_hosts fileOMNISTORAGE_SFTP_TIMEOUT- Connection timeout in seconds
Using the Registry¶
import (
"github.com/grokify/omnistorage"
_ "github.com/grokify/omnistorage/backend/sftp"
)
backend, err := omnistorage.Open("sftp", map[string]string{
"host": "example.com",
"user": "username",
"password": "password",
"root": "/data",
})
Configuration¶
Config Struct¶
type Config struct {
Host string // Server hostname (required)
Port int // SSH port (default: 22)
User string // Username (required)
Password string // Password auth
KeyFile string // Path to private key
KeyPassphrase string // Passphrase for encrypted keys
Root string // Base directory for operations
KnownHostsFile string // Path to known_hosts file
Timeout int // Connection timeout in seconds (default: 30)
Concurrency int // Max concurrent operations (default: 5)
}
Registry Config¶
| Key | Description | Required |
|---|---|---|
host | Server hostname | Yes |
port | SSH port | No (default: 22) |
user | Username | Yes |
password | Password | No* |
key_file | Path to private key | No* |
key_passphrase | Key passphrase | No |
root | Base directory | No |
known_hosts | Path to known_hosts file | No |
timeout | Timeout in seconds | No |
* Either password or key_file is required.
Features¶
The SFTP backend implements ExtendedBackend:
| Feature | Supported | Notes |
|---|---|---|
| Stat | Yes | Returns file metadata |
| Copy | Yes | Client-side streaming copy |
| Move | Yes | Uses rename or copy+delete |
| Mkdir | Yes | Creates directories recursively |
| Rmdir | Yes | Removes empty directories |
| Range Read | Yes | Offset and limit supported |
Operations¶
Write¶
w, err := backend.NewWriter(ctx, "data/file.json")
if err != nil {
return err
}
w.Write([]byte(`{"key": "value"}`))
w.Close()
Read¶
r, err := backend.NewReader(ctx, "data/file.json")
if err != nil {
return err
}
defer r.Close()
data, _ := io.ReadAll(r)
Range Read¶
r, err := backend.NewReader(ctx, "large-file.bin",
omnistorage.WithOffset(1000),
omnistorage.WithLimit(500))
List¶
Extended Operations¶
ext := backend.(*sftp.Backend)
// Get file metadata
info, _ := ext.Stat(ctx, "file.txt")
fmt.Printf("Size: %d bytes\n", info.Size())
fmt.Printf("Modified: %s\n", info.ModTime())
// Create directory
ext.Mkdir(ctx, "new-folder")
// Copy file
ext.Copy(ctx, "source.txt", "dest.txt")
// Move file (uses rename if on same filesystem)
ext.Move(ctx, "old.txt", "new.txt")
Authentication¶
Password vs Key Authentication¶
Password authentication is simpler but less secure. SSH key authentication is recommended for production:
// Production: Use SSH key
backend, _ := sftp.New(sftp.Config{
Host: "prod.example.com",
User: "deploy",
KeyFile: "/home/app/.ssh/id_ed25519",
})
Host Key Verification¶
By default, host key verification is disabled for development convenience. For production, specify a known_hosts file:
backend, _ := sftp.New(sftp.Config{
Host: "prod.example.com",
User: "deploy",
KeyFile: "/home/app/.ssh/id_ed25519",
KnownHostsFile: "/home/app/.ssh/known_hosts",
})
Error Handling¶
r, err := backend.NewReader(ctx, "missing.txt")
if errors.Is(err, omnistorage.ErrNotFound) {
log.Println("File not found")
}
if errors.Is(err, omnistorage.ErrPermissionDenied) {
log.Println("Permission denied")
}
Best Practices¶
- Use SSH keys - More secure than passwords
- Set a root directory - Avoid path traversal issues
- Enable host key verification - Required for production
- Handle connection errors - Network issues are common
- Close the backend - Releases SSH connection