HomeDocsDaemonSFTP System
Daemon

SFTP System

Native TypeScript SFTP server for container file access over SSH.

bthavanishBy bthavanish

SFTP System

AirLink runs a pure TypeScript SFTP server as part of the daemon process. The previous architecture used an atmoz/sftp Docker sidecar; this has been fully replaced by a native SSH server built on the ssh2 library.

Architecture

AspectDetail
ProtocolSFTP over SSH-2
Libraryssh2 (pure JS, no native deps)
Host keyEd25519, auto-generated
DeploymentDaemon process, no container isolation
ReplacementReplaced atmoz/sftp sidecar

The server handles SSH transport and the SFTP subsystem directly. No external processes, no Docker, no privilege escalation.

Components

sftpServer.ts

Manages the SSH server lifecycle:

  • Listens on a configurable port
  • Generates and loads an Ed25519 host key from storage/sftp_host_ed25519
  • Creates SSH server instances per connection
  • Handles session creation and teardown
  • Emits connection/disconnection events for activity tracking

sftpAuth.ts

Handles credentials and authentication:

  • Generates per-session credentials with format alsftp_<sha256_hex_16> as username
  • Produces a 24-byte random password per session
  • Validates incoming SSH auth against active session credentials
  • Tracks activity events (connect, disconnect, reads, writes, etc.)
  • Enforces one-session-per-server: new credentials revoke any previous active session
  • Runs periodic cleanup every hour to expire stale sessions

sftpSubsystem.ts

Full SFTP protocol handler:

  • Implements the SFTP packet framing layer
  • Maps SSHFXP* request types to filesystem operations
  • Enforces path jail and security checks before every operation
  • Returns proper SFTP status codes on success and failure

Supported Operations

OperationSSH_FXP TypeDescription
OPENSSH_FXP_OPENOpen a file for read, write, or append
READSSH_FXP_READRead bytes from an open file handle
WRITESSH_FXP_WRITEWrite bytes to an open file handle
CLOSESSH_FXP_CLOSEClose an open file or directory handle
OPENDIRSSH_FXP_OPENDIROpen a directory for listing
READDIRSSH_FXP_READDIRRead directory entries from an open dir handle
STATSSH_FXP_STATGet file attributes (follows symlinks)
LSTATSSH_FXP_LSTATGet file attributes (does not follow symlinks)
FSTATSSH_FXP_FSTATGet attributes for an open file handle
REMOVESSH_FXP_REMOVEDelete a file
RMDIRSSH_FXP_RMDIRRemove an empty directory
MKDIRSSH_FXP_MKDIRCreate a new directory
RENAMESSH_FXP_RENAMERename or move a file/directory
REALPATHSSH_FXP_REALPATHResolve a path to its canonical form
SETSTATSSH_FXP_SETSTATSet file attributes on a path
FSETSTATSSH_FXP_FSETSTATSet file attributes on an open handle

Session Management

Credential Format

  • Username: alsftp_<first_16_hex_chars_of_sha256>
  • Password: 24 random bytes, base64url encoded
  • Each credential set is tied to a single server ID

Lifecycle

ParameterValue
Session TTL24 hours
Max active sessions1 per server
Cleanup intervalEvery hour
Host key typeEd25519
Host key pathstorage/sftp_host_ed25519

When new credentials are generated for a server, any existing active session for that server is immediately revoked. The old credentials stop working on the next auth attempt.

The host key is generated once on first startup if it does not exist. It persists across daemon restarts so clients are not prompted about host key changes.

Activity Events

Events are tracked per server and buffered for consumption by the panel.

EventDescription
connectSSH connection established
disconnectSSH connection closed
writeFile write operation
readFile read operation
removeFile or directory deleted
renameFile or directory renamed
mkdirDirectory created
readdirDirectory listing read

Events are buffered up to a maximum of 500 per server. The panel consumes these via polling or subscription. Buffer overflow drops oldest events.

Security

Path Jail

Every filesystem operation goes through jailPath() which resolves the target path and confirms it falls within the session’s designated root directory. Symlink traversal is checked via realpathSync() to prevent escape.

Secure File Open

Kernel VersionMethodDescription
>= 5.6openat2 FFIUses RESOLVE_BENEATH and RESOLVE_NO_MAGICLINKS for atomic path resolution
< 5.6O_NOFOLLOW fallbackOpens file without following symlinks, relies on jailPath pre-check

The openat2 approach prevents TOCTOU (time-of-check-time-of-use) races by resolving and opening the file in a single kernel syscall. The fallback path uses O_NOFOLLOW to block symlink following but does not fully eliminate the race window.

Security Summary

  • Path jail enforced before every operation
  • Symlink escape prevented by realpath check
  • TOCTOU minimized via openat2 on modern kernels
  • One session per server limits blast radius
  • Credentials are non-reusable (tied to session, revoked on replacement)

API Endpoints

POST /sftp/credentials

Generate new SFTP credentials for a server. Revokes any existing active session for that server.

Response:

{
  "username": "alsftp_a1b2c3d4e5f67890",
  "password": "xK9m...",
  "host": "sftp.example.com",
  "port": 2222,
  "rootDir": "/data/servers/abc",
  "expiresAt": "2026-01-16T12:00:00Z"
}

DELETE /sftp/credentials

Revoke the active SFTP credentials for a server. The existing session is terminated and the credentials stop working immediately.

Response:

{
  "revoked": true
}

GET /sftp/status

Return active SFTP session status for all servers or a specific server.

Response:

{
  "sessions": [
    {
      "serverId": "abc",
      "username": "alsftp_a1b2c3d4e5f67890",
      "active": true,
      "connectedAt": "2026-01-15T12:00:00Z",
      "lastActivity": "2026-01-15T14:30:00Z",
      "expiresAt": "2026-01-16T12:00:00Z"
    }
  ]
}

GET /sftp/activity

Return buffered activity events for a server.

Query Parameters:

ParameterDescription
serverIdServer ID to fetch events for
limitMax events to return (default 100, max 500)

Response:

{
  "events": [
    {
      "type": "read",
      "path": "/data/files/log.txt",
      "bytes": 4096,
      "timestamp": "2026-01-15T14:30:00Z"
    }
  ]
}