Generate
.aeroftpconnection profiles from your control panel so customers can import pre-configured connections with a single click.
Version: 1.0 Last Updated: 4 April 2026
AeroFTP uses an encrypted .aeroftp file format for exporting and importing server connection profiles. Hosting providers can generate these files from their control panels, allowing customers to download a ready-to-use profile instead of manually entering FTP/SFTP credentials.
Benefits for hosting providers:
- Customers connect in one click with no manual configuration
- Credentials are never exposed in plaintext emails
- Profiles are encrypted with AES-256-GCM + Argon2id key derivation
- Works with FTP, FTPS, SFTP, and WebDAV connections
An .aeroftp file is a JSON document with the following structure:
{
"version": 1,
"salt": [/* 32 random bytes */],
"nonce": [/* 12 random bytes */],
"encrypted_payload": [/* AES-256-GCM ciphertext */],
"metadata": {
"exportDate": "2026-04-04T20:00:00Z",
"aeroftpVersion": "3.5.0",
"serverCount": 1,
"hasCredentials": true
}
}The metadata field is unencrypted and shown to the user before they enter the decryption password. The encrypted_payload contains the actual connection data.
The encryption password is processed through Argon2id with the following parameters:
| Parameter | Value |
|---|---|
| Algorithm | Argon2id |
| Memory | 128 MiB |
| Iterations | 3 |
| Parallelism | 4 |
| Output length | 32 bytes |
| Salt | 32 random bytes |
| Parameter | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key | 32-byte Argon2id output |
| Nonce | 12 random bytes |
| Plaintext | JSON-serialized payload (UTF-8) |
The salt, nonce, and encrypted_payload fields in the file are JSON arrays of unsigned byte values (0-255).
The decrypted payload is a JSON object containing a servers array:
{
"servers": [
{
"id": "unique-id",
"name": "My FTP Server",
"host": "ftp.example.com",
"port": 21,
"username": "customer123",
"protocol": "ftp",
"initialPath": "/public_html",
"credential": "the-password",
"options": {
"tlsMode": "explicit",
"verifyCert": true
}
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Unique identifier (UUID v4 recommended) |
name |
string | yes | Display name shown to the customer |
host |
string | yes | Server hostname or IP address |
port |
number | yes | Connection port |
username |
string | yes | Login username |
protocol |
string | no | ftp, ftps, sftp, webdav. Default: ftp |
initialPath |
string | no | Remote directory opened after connection |
localInitialPath |
string | no | Local directory paired with connection |
credential |
string | no | Password or passphrase (encrypted in file) |
color |
string | no | Hex color for the server badge (e.g. #3B82F6) |
providerId |
string | no | Provider identifier for branding |
options |
object | no | Protocol-specific options (see below) |
| Option | Type | Values | Description |
|---|---|---|---|
tlsMode |
string | explicit, implicit, explicit_if_available, none |
TLS encryption mode |
verifyCert |
boolean | true / false |
Validate server certificate (default: true) |
explicit(recommended): AUTH TLS on port 21implicit: TLS on port 990explicit_if_available: Try TLS, fall back to plaintextnone: No encryption (not recommended)
| Option | Type | Description |
|---|---|---|
authMethod |
string | password, key, key_and_password |
privateKeyPath |
string | Path to SSH private key file |
key_passphrase |
string | Passphrase for encrypted private key |
No additional options required. Use the full server URL as host (e.g. https://webdav.example.com/remote.php/dav/files/user/).
import json
import os
import uuid
from argon2.low_level import hash_secret_raw, Type
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from datetime import datetime, timezone
def generate_aeroftp_profile(servers, password):
# Key derivation
salt = os.urandom(32)
key = hash_secret_raw(
secret=password.encode('utf-8'),
salt=bytes(salt),
time_cost=3,
memory_cost=131072, # 128 MiB
parallelism=4,
hash_len=32,
type=Type.ID
)
# Encrypt payload
nonce = os.urandom(12)
payload = json.dumps({"servers": servers}).encode('utf-8')
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, payload, None)
return {
"version": 1,
"salt": list(salt),
"nonce": list(nonce),
"encrypted_payload": list(ciphertext),
"metadata": {
"exportDate": datetime.now(timezone.utc).isoformat(),
"aeroftpVersion": "3.5.0",
"serverCount": len(servers),
"hasCredentials": any(s.get("credential") for s in servers)
}
}
# Example usage
servers = [{
"id": str(uuid.uuid4()),
"name": "Customer - example.com",
"host": "ftp.example.com",
"port": 21,
"username": "customer@example.com",
"protocol": "ftp",
"initialPath": "/public_html",
"credential": "customer-password",
"options": {
"tlsMode": "explicit",
"verifyCert": True
}
}]
profile = generate_aeroftp_profile(servers, "secure-transfer-password")
with open("customer.aeroftp", "w") as f:
json.dump(profile, f, indent=2)const crypto = require('crypto');
const argon2 = require('argon2');
const { v4: uuidv4 } = require('uuid');
async function generateAeroftpProfile(servers, password) {
const salt = crypto.randomBytes(32);
// Argon2id key derivation
const key = await argon2.hash(password, {
type: argon2.argon2id,
salt: salt,
memoryCost: 131072, // 128 MiB
timeCost: 3,
parallelism: 4,
hashLength: 32,
raw: true
});
// AES-256-GCM encryption
const nonce = crypto.randomBytes(12);
const payload = JSON.stringify({ servers });
const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);
const encrypted = Buffer.concat([
cipher.update(payload, 'utf8'),
cipher.final(),
cipher.getAuthTag() // 16-byte tag appended to ciphertext
]);
return {
version: 1,
salt: [...salt],
nonce: [...nonce],
encrypted_payload: [...encrypted],
metadata: {
exportDate: new Date().toISOString(),
aeroftpVersion: '3.5.0',
serverCount: servers.length,
hasCredentials: servers.some(s => s.credential)
}
};
}When a customer opens an .aeroftp file in AeroFTP:
- AeroFTP reads the
metadataand shows a summary (server count, export date, whether credentials are included) - The customer enters the decryption password
- AeroFTP derives the key with Argon2id using the stored
salt - The
encrypted_payloadis decrypted with AES-256-GCM - Connections appear in the "My Servers" list, ready to use
- Always use FTPS or SFTP - set
tlsModetoexplicitorimplicitfor FTP, or useprotocol: "sftp" - Use a strong transfer password - this protects the credentials in transit. Communicate it to the customer through a separate channel (SMS, phone, different email)
- Set
verifyCert: true- ensure your server has a valid TLS certificate (Let's Encrypt works) - Pre-fill
initialPath- save customers from navigating to their web root (/public_html,/httpdocs,/www, etc.) - Use meaningful
name- include the domain name so customers can identify the connection (e.g. "example.com - FTP")
For questions about the .aeroftp format or integration help, contact dev@aeroftp.app.
AeroFTP is free and open source (GPL-3.0). Hosting providers are welcome to integrate without any licensing requirements.