Theme
If you know Syncplify Server Preview
The engine is the same, the language is the same, and the entire standard library is the same. What differs is everything that depends on there being a client session, because on the Connector there is not one: the session lives on the Head, in the cloud, and the Connector only ever sees file operations arriving down a tunnel.
Read this page before porting anything. Most of the differences fail loudly. One of them fails silently, and that is the one that costs you an afternoon.
The one that fails silently
This does not throw. It returns a wrong answer and lets the script carry on
| What you wrote | What happens here |
|---|---|
EventCtx().RelPath | Returns undefined. The fields have different names here |
Search every ported script for EventCtx(). before you enable it.
GetSecret and SendMail used to be on that list. They are not any more
Both work here now. GetSecret reads from a secret store you fill in under Automation, then Secrets; SendMail sends through the relay you configure under System, then Settings, on the SMTP tab. NotifyViaTelegramBot works too, configured on the Telegram tab.
None of the three is configured on a new Connector, so a ported script that uses one gets "" or false until you set it up. All three write the reason to the log, so Activity tells you which it was.
Events
There is no overlap in the event names. Syncplify Server has roughly seventy events covering connections, authentication, shares and protocol commands; the Connector has sixteen, all of them file system operations.
| Syncplify Server | Connector |
|---|---|
BeforeFileUpload, AfterFileUpload | before.upload, after.upload |
BeforeFileDownload, AfterFileDownload | before.download, after.download |
BeforeDeleteFile, AfterDeleteFile | before.delete, after.delete |
BeforeDeleteDir, AfterDeleteDir | before.deleteDir, after.deleteDir |
BeforeRenameFile, BeforeRenameDir, and their after twins | before.rename, after.rename |
BeforeMakeDir, AfterMakeDir | before.makeDir, after.makeDir |
BeforeListDir, AfterListDir | before.listDir, after.listDir |
BeforeSendDirListToClient | dirList.filter |
OnConnectionClose | session.disconnect, with no context at all |
Everything about connections and authentication: OnNewConnection, OnAuthPassword, OnAuthPKI, OnAuthSuccess, OnAuthFail, OnProtectorStrike, OnBlocklistHit | No equivalent. Authentication happens on the Head |
Everything about shares: BeforeShrCreate, BeforeShrFileUpload, and the rest | No equivalent. Shares are a Portal and Head concern |
Everything protocol specific: OnCustomSITECommand, BeforeOpenShell, BeforeSendSoftwareID, OnFileHashRequest, OnAvailDiskRequest | No equivalent. The Connector does not speak SFTP or FTP |
OnQuotaExceeded, BeforeFileCombineRequest, BeforeAttrChange, BeforeTimeChange, BeforeSymlink, OnCheckFileExist, OnFileSizeRequest | No equivalent today |
The full catalog is on Events and when they fire.
EventCtx has different field names
| Syncplify Server | Connector |
|---|---|
RelPath | Src |
RelTargetPath | Dst |
VFSName | VfsName |
Username | User |
VirtualSite | absent |
Event, Sess and Priority are additions |
Use the Ctx functions and this stops mattering
CtxRelPath(), CtxRelTargetPath(), CtxVFSName(), CtxUsername(), EventHandler() and HandlerPriority() are spelled the same and behave the same in both products. Code written against them ports with no edits. Code written against the EventCtx() object does not.
The Session object
Session exists and is never null on the Connector, which is one thing that gets simpler. What it can tell you is narrower.
Works, same as Syncplify Server
GetID, GetUserID, GetLoggedInUserID, GetRelPath, GetRelTargetPath, GetCurrentVFSName, CurrentVFSID, RemoveFromDirList, RemoveFromListDir.
Works, but means something different
| Method | Here |
|---|---|
GetAbsPath, GetAbsTargetPath | Return the VFS relative path, identical to the Rel versions. There is no on disk path to give you, because a virtual file system may be a bucket |
GetProtocol | Always R2FS. The Connector does not know whether the user came in over SFTP, FTPS or the web client |
Throws a catchable error
GetRemoteAddress, GetVirtualSite, GetSrvConfig, Terminate, BlockOperation, AddQuestion.
These fail loudly on purpose, so a ported script stops at the exact line rather than quietly doing nothing.
Not defined at all
Calling one of these is a TypeError that stops the script:
GetUser, GetClientVersion, GetStartTime, GetLastActivity, GetLastCommand, GetLastCommandTime, GetLastError, GetLastErrorTime, AddCustomData, GetCustomData, GetAllCustomData, AddQuestionPassword, AddQuestionTOTP, AuthenticateUserFromScript.
There is no User object
Session.GetUser() does not exist, and neither does the User class. CtxUsername() gives you the username, and that is all the Connector holds about the person: home directory, quota, permissions and custom data live in the Portal and on the Head, not here. A script that branched on a user's properties needs rewriting around the username alone, or around a lookup of your own.
Globals that are absent
| Missing | Why |
|---|---|
MakeTOTP, ValidateTOTP | They use Syncplify Server's own global TOTP secret. There is no such secret here |
CtxVirtualSite | No virtual sites |
Terminate as a global | Ending a session is the Head's job |
User | See above |
Globals that need configuring before they work
| Global | Configure it at |
|---|---|
GetSecret(name) | Automation, then Secrets. Returns "" for a name that is not stored |
SendMail(...) | System, Settings, SMTP. Returns false until a relay is saved |
NotifyViaTelegramBot(...) | System, Settings, Telegram. Returns false until a bot is saved |
Globals that exist but cannot work
| Global | Behavior here |
|---|---|
new VirtualFSByName(name) | Always fails, and the failure cannot be caught |
VfsTypeR2FS | Defined, but a virtual file system of that type cannot be constructed |
The virtual file system behaves differently
GetCurrentVFS() is not scoped to the user here
On the Connector, the object returned by GetCurrentVFS() has full access to the whole virtual file system and does not apply the triggering user's permissions. Permissions were already checked, before your script ran, for the operation that triggered it; anything else the script does is unchecked. A Syncplify Server script that relied on the VFS object refusing what the user could not do will behave differently here. See The virtual file system object.
Operations through GetCurrentVFS() also carry no deadline of their own, so a stalled backend can outlast the handler timeout. A virtual file system you build with new VirtualFS(...) does honor the deadline.
Configuration and modules
| Syncplify Server | Connector | |
|---|---|---|
| Secret store | Yes, per virtual site | Yes, one per Connector. Names are not case sensitive here |
| SMTP settings | Per virtual site | One per Connector, under System, Settings, SMTP |
| Telegram bot | SuperAdmin, and it accepts commands | Under System, Settings, Telegram. Sends only, never accepts commands |
| Modules folder | C:\ProgramData\Syncplify\Server\modules, /etc/Syncplify/Server/data/modules | C:\ProgramData\Syncplify\sc-conn\modules, /opt/Syncplify/sc-conn/modules, /data/modules in Docker |
| Underscore preinstalled | Yes | No. Place the file yourself |
| Default script timeout | Set per handler | Set per handler, 5000 ms when left at zero |
Engine fixes you may not have on Syncplify Server yet
Four engine bugs were found and fixed while this book was being written. They live in the shared SyncJS engine, so they affected Syncplify Server and AFT too, and a Syncplify Server installation that has not picked up the fix still has them.
| Call | The old behavior |
|---|---|
ToDate(item.TimeStamp) | Stopped the script, uncatchably |
FormatDateTime(fmt, item.TimeStamp) | Silently returned the current time, not the file's |
hc.ReqBody(...) | Anything but a flat object of string values was sent as application/octet-stream with a binary prefix; flat objects had their keys re-ordered; an explicit Content-Type was overwritten |
hc.Head() | Discarded every header, bearer token and credential |
All four behave correctly on a current Connector. If a script that works here misbehaves after you move it to Syncplify Server, check which engine build that installation is running.
A porting checklist
- Rewrite the event binding. The event names are entirely different, and there is no automatic mapping.
- Search for
GetSecret. The calls port unchanged, but the secrets do not: create each one under Automation, then Secrets. Names are matched without regard to capitals here. - Search for
SendMailandNotifyViaTelegramBot. Both work, once you fill in the SMTP and Telegram tabs under System, then Settings.SendMailhere also accepts an empty sender and falls back to the configured one. - Search for
Session.GetUser,GetRemoteAddress,GetVirtualSite,Terminate,BlockOperation, and the custom data methods. Each one either throws or stops the script. - Search for
EventCtx().and switch to theCtx*functions. - Search for
MakeTOTP,ValidateTOTP,CtxVirtualSite,VirtualFSByName. - Bind it fail open, to one virtual file system, and watch it work before you tighten anything. See Writing and testing a script.
What is identical
Worth saying plainly, because the list above is long: the entire standard library carries over unchanged. Logging, local file operations, the virtual file system method names and response objects, HTTP, SQL, all five remote transfer clients and their options, AMQP, PGP, images, compression, encoding, hashing, HMAC, CSV, XML, process execution, Exit(), Sleep(), the identifier generators and require() all behave the same way in both products.
So do GetSecret, SendMail and NotifyViaTelegramBot, once configured. They are spelled the same, take the same arguments and return the same things; only the place you configure them has moved, from a virtual site to the Connector itself.
If a script only reads CtxRelPath() and CtxUsername(), does something with the virtual file system, and logs the result, it will very likely run here unmodified.