Theme
Encrypting files with PGP before upload
When you deliver files to a third party SFTP server you cannot always trust that the storage on the other end is encrypted. PGP encryption solves that at the application layer: only the holder of the matching private key can ever read the file, regardless of how the server stores it. This recipe combines FsWatcher with PGPEncryptFile to encrypt every file that lands in a local outbound folder and upload the encrypted copy to an SFTP server. The script runs persistently until halted.
How it works
FsWatcher watches a local directory for new files. When a file appears, the callback calls PGPEncryptFile to produce an encrypted copy, uploads that copy through a named SFTP VFS, and deletes the local temporary file. The original, unencrypted file is left untouched; you manage its lifecycle separately.
The path of the recipient's PGP public key is read from the AFT! secrets store at startup, so neither the key material nor its location appears in the script source.
javascript
// PGP encrypt before upload
// Watches a local folder for newly created files, encrypts each one with a
// recipient's PGP public key, uploads the encrypted copy to a remote SFTP server
// through a named VFS, then removes the local encrypted temporary file.
// Runs persistently until halted.
var localWatchDir = "/local/outbound"; // local folder to watch for new files
var remoteDestDir = "/remote/encrypted-inbox"; // remote destination for encrypted files
var sftpVfsName = "your-sftp-vfs-name"; // named SFTP VFS in the VFS library
var recipientKeyFile = GetSecret("pgp-pubkey-path"); // path to the recipient's PGP public key file
var watcher = new FsWatcher();
watcher.NotifyCreate = true;
watcher.NotifyWrite = false;
watcher.NotifyRemove = false;
watcher.NotifyRename = false;
watcher.NotifyChmod = false;
// Wait a moment before firing, so that newly written files are fully closed.
watcher.DelayBySeconds = 2;
var sftp = new VirtualFSByName(sftpVfsName);
try {
// Watch only the top level directory; subdirectories are not processed.
watcher.WatchDir(localWatchDir, false);
watcher.Start(function(evt) {
if (evt.Event !== EVT_CREATE) {
return;
}
var sourceFile = evt.Object;
var encryptedFile = GetTempFileName() + ".pgp";
Log.Info("encrypting: " + sourceFile);
if (!PGPEncryptFile(sourceFile, encryptedFile, recipientKeyFile)) {
Log.Error("PGP encryption failed for: " + sourceFile);
return;
}
// Append .pgp to the original file name, so that the recipient can
// recognize the format and strip the suffix after decryption.
var remoteName = ExtractName(sourceFile) + ExtractExt(sourceFile) + ".pgp";
var remotePath = remoteDestDir + "/" + remoteName;
Log.Info("uploading encrypted file to: " + remotePath);
var r = sftp.ImportFile(encryptedFile, remotePath);
if (!r.Ok()) {
Log.Error("upload failed: " + r.ErrorMsg());
} else {
Log.Info("uploaded: " + remotePath);
}
// Remove the local encrypted temporary file whatever the outcome of the upload.
// The original unencrypted file stays in place; manage its lifecycle
// separately, according to your retention policy.
DelFile(encryptedFile);
});
// Block here until an operator sends a halt signal from the AFT! web UI or the API.
WaitForHaltSignal();
Log.Info("halt signal received, shutting down");
} finally {
watcher.Stop();
}watcher.DelayBySeconds = 2: this small delay keeps the callback from firing while the producer process is still writing the file. Without it, PGPEncryptFile might read a file that is only half written. Two seconds are enough for almost all local writes; increase the value if your producer writes large files slowly.
GetTempFileName() + ".pgp": GetTempFileName() returns a unique path in the system's temporary directory. Appending .pgp is not needed for the encryption to work, but it makes the temporary file recognizable if you ever inspect that directory by hand.
DelFile(encryptedFile): the encrypted temporary file is deleted after every event, whether the upload succeeded or not. Keeping it around would accumulate encrypted copies in the temporary directory with no benefit.
Storing the key path as a secret
Rather than hard coding the path of the recipient's public key in the script, store it in the AFT! secrets store. In the web UI go to Secrets, create a new secret named pgp-pubkey-path, and set its value to the absolute path of the public key file on the machine running AFT!. GetSecret retrieves it at run time without ever touching the script source.
Encrypting for several recipients
PGPEncryptFile encrypts for one public key: the first key found in the file you pass. To deliver the same file to several recipients, encrypt one copy per recipient, each with that recipient's key, and upload each copy to its own destination. A small table drives the loop:
javascript
var recipients = [
{ keyFile: GetSecret("pgp-pubkey-path-acme"), destDir: "/remote/acme/inbox" },
{ keyFile: GetSecret("pgp-pubkey-path-globex"), destDir: "/remote/globex/inbox" }
];Inside the callback, run the encrypt, upload and delete steps once for each entry of the table.