Mutations
Create directories, rename paths, remove files, and work with symlinks.
Supported Mutation Methods
The public SFTP wrapper supports:
try await sftp.setAttributes(
"/tmp/traversio-demo.txt",
attributes: SSHSFTPFileAttributes(
flags: SSHSFTPFileAttributes.permissionsFlag,
size: nil,
userID: nil,
groupID: nil,
permissions: 0o640,
accessTime: nil,
modificationTime: nil,
extensions: []
)
)
try await sftp.makeDirectory("/tmp/traversio-demo")
try await sftp.rename("/tmp/original.txt", to: "/tmp/final.txt")
try await sftp.removeFile("/tmp/final.txt")
try await sftp.removeDirectory("/tmp/traversio-demo")It also supports symlink operations:
try await sftp.createSymbolicLink(
targetPath: "/tmp/final.txt",
linkPath: "/tmp/final-link.txt"
)
let linkTarget = try await sftp.readLink("/tmp/final-link.txt")
print(linkTarget.filename)Handle-scoped attribute mutation is also public:
let handle = try await sftp.openFile(
"/tmp/traversio-demo.txt",
flags: [.read, .write]
)
try await handle.setAttributes(
SSHSFTPFileAttributes(
flags: SSHSFTPFileAttributes.permissionsFlag,
size: nil,
userID: nil,
groupID: nil,
permissions: 0o600,
accessTime: nil,
modificationTime: nil,
extensions: []
)
)
try await handle.close()Rename Behavior
When the server advertises [email protected] version 1 or later, Traversio automatically uses that extension. Otherwise it falls back to standard SSH_FXP_RENAME.
That stronger rename behavior is automatic from the caller's point of view:
try await sftp.rename("/tmp/current.txt", to: "/tmp/next.txt")Worked Example
let root = try await sftp.realPath(".")
let directory = "\(root.filename)/traversio-doc-demo"
let file = "\(directory)/message.txt"
let renamed = "\(directory)/message-final.txt"
let symlink = "\(directory)/message-link.txt"
try await sftp.makeDirectory(directory)
try await sftp.writeFile(file, data: Array("hello\n".utf8))
try await sftp.setAttributes(
file,
attributes: SSHSFTPFileAttributes(
flags: SSHSFTPFileAttributes.permissionsFlag,
size: nil,
userID: nil,
groupID: nil,
permissions: 0o640,
accessTime: nil,
modificationTime: nil,
extensions: []
)
)
try await sftp.rename(file, to: renamed)
try await sftp.createSymbolicLink(targetPath: renamed, linkPath: symlink)
let linkTarget = try await sftp.readLink(symlink)
print(linkTarget.filename)
try await sftp.removeFile(symlink)
try await sftp.removeFile(renamed)
try await sftp.removeDirectory(directory)Limits
Mutation coverage is already broad enough for real application use:
- one
SFTPClientoverlaps path and handle mutation requests through request-id-based reply routing - bulk helper reads and writes keep a simple whole-file model
- broader extension coverage remains focused on the documented OpenSSH paths
- wider cross-server validation remains release work
Transfers
Read whole files, use bounded concurrent read or write requests, observe transfer progress, and understand the current transfer limits.
Forwarding
Understand the difference between direct channels, local forwarding, dynamic forwarding, remote forwarding, and ProxyJump before you choose a Traversio API.