Filie Protocol

This page contains Technical Documentation for the Filie secure transfer protocol. Filie is a cross-platform file sharing application. This specific page is purely informational; no software is downloaded or installed upon viewing this document.

Filie uses open, documented control messages and standard cryptographic primitives to protect file transfers without depending on the secrecy of the protocol itself.

The specification covers the wire-visible protocol, key agreement, key derivation, authenticated encryption, and security boundaries. Local storage policy, UI behavior, service deployment, transfer retention, and operational tuning are implementation profiles layered on top of the core protocol.

Goals

Filie follows a zero-trust computing model for transfer infrastructure: the network, relay services, and object storage are treated as untrusted. File contents are encrypted on the sending device and decrypted only on the receiving device. The shared encryption secret is derived locally by the participating devices and is never transmitted over the wire or stored by Filie infrastructure. Filie infrastructure does not know, and cannot derive, the contents of encrypted chunks.

The protocol is designed to provide:

The protocol does not attempt to protect plaintext from a device that is already compromised before encryption or after decryption.

Terminology

Cryptographic Primitives

The current protocol profile uses:

All private keys and symmetric keys are generated and used on client devices. Relay services receive public keys, signed envelopes, transfer state, and encrypted chunks, but not private keys, shared secrets, transfer keys, or plaintext.

Device Identity

Each device generates an Ed25519 identity keypair:

ID_priv, ID_pub = Ed25519.Generate()

ID_priv remains local to the device. ID_pub is included in signed device announcements and transfer messages. Identity keys are used for signatures only; they are not used directly as encryption keys or key-agreement keys.

Ephemeral Agreement Keys

Each device maintains a short-term X25519 keypair:

E_priv, E_pub = X25519.Generate()

The public key E_pub may be advertised through LAN discovery or cloud device registration. The private key E_priv remains local.

Ephemeral key rotation and retention are protocol profile parameters. A Filie implementation may retain a bounded history of recent ephemeral private keys so that delayed transfers encrypted for a recently advertised public key can still be received. The exact retention window is not part of the core wire protocol. Deployments should document their chosen retention profile separately.

Signed Device Envelope

Devices advertise identity and agreement material with signed envelopes. The signature binds the device identity to the advertised ephemeral key and a fresh nonce.

WAN/cloud device envelope:

{
  "hdr": {
    "nonce": "BASE64URL(16 random bytes)",
    "sig": "BASE64URL(Ed25519 signature)"
  },
  "body": {
    "device": {
      "id": "device identifier",
      "name": "device display name",
      "platform": "platform identifier",
      "identity": "BASE64URL(Ed25519 public key)"
    },
    "wan": {
      "ephemeral": "BASE64URL(X25519 public key)"
    },
    "discovery": {
      "ttl": 300
    }
  }
}

The WAN signature payload is the following fixed-field JSON string:

{"id":"...","name":"...","platform":"...","identity":"...","wan_ephemeral":"...","nonce":"..."}

LAN discovery uses the same structure with a lan object:

{
  "msg": "device",
  "ver": 1,
  "hdr": {
    "ask": true,
    "nonce": "BASE64URL(16 random bytes)",
    "sig": "BASE64URL(Ed25519 signature)"
  },
  "body": {
    "device": {
      "id": "device identifier",
      "name": "device display name",
      "platform": "platform identifier",
      "identity": "BASE64URL(Ed25519 public key)"
    },
    "lan": {
      "port": 28073,
      "ephemeral": "BASE64URL(X25519 public key)"
    },
    "discovery": {
      "timestamp": 1714000000,
      "ttl": 300
    }
  }
}

The LAN signature payload is the following fixed-field JSON string:

{"id":"...","name":"...","platform":"...","identity":"...","lan_ephemeral":"...","nonce":"..."}

Receivers verify the Ed25519 signature before trusting the advertised identity and ephemeral key. Unknown fields are ignored unless they are required by a negotiated future capability.

Shared Secret Derivation

For a transfer between device A and device B:

secret_A = X25519(A_E_priv, B_E_pub)
secret_B = X25519(B_E_priv, A_E_pub)

Both devices derive the same shared secret without transmitting it.

The shared secret is then bound to the transfer context with HKDF. Current profiles use:

lan_file_key   = HKDF-SHA256(shared_secret, info = "filie-file", length = 32)
cloud_file_key = HKDF-SHA256(shared_secret,
                             info = "filie-cloud-transfer:" || transfer_id,
                             length = 32)

Future profiles may derive separate metadata, file, and chunk keys from an intermediate session key:

session_key = HKDF-SHA256(shared_secret, info = "filie-session", length = 32)
file_key    = HKDF-SHA256(session_key, info = "filie-file", length = 32)
meta_key    = HKDF-SHA256(session_key, info = "filie-meta", length = 32)

Key-derivation labels are part of the protocol profile and must not be reused for unrelated purposes.

Chunk Encryption

Files are split into chunks. Each chunk is encrypted independently with AES-256-GCM:

box_i = AES-256-GCM-Encrypt(file_key, plaintext_chunk_i)

The serialized encrypted box is:

nonce || ciphertext || tag

The nonce is included with each encrypted chunk. Receivers reconstruct the AEAD box, authenticate it, and reject the transfer if authentication fails.

Chunk size and upload/download parallelism are implementation tunables. Changing chunk size does not change the cryptographic model as long as each chunk is authenticated and encrypted independently.

LAN Transfer Profile

LAN discovery uses multicast to advertise signed device envelopes. The current LAN profile uses:

IPv4 multicast: 239.0.19.69:25077
IPv6 multicast: ff02::1969:25077
Default local HTTP transfer port: 28073

The receiver exposes a local HTTP endpoint:

The /accept body includes the sender signed envelope plus transfer summary:

{
  "hdr": {
    "nonce": "...",
    "sig": "..."
  },
  "body": {
    "device": {
      "id": "...",
      "name": "...",
      "platform": "...",
      "identity": "..."
    },
    "lan": {
      "ephemeral": "..."
    },
    "transfer": {
      "description": "10 photos",
      "file_count": 10,
      "total_bytes": 1048576
    }
  }
}

For /upload, each encrypted chunk frame is serialized as:

clear_len_u32_be || box_len_u32_be || aes_gcm_box

The request includes:

X-Filie-Sender-Id: sender device id
X-Filie-Filename: base64url(utf8(original filename))

The receiver accepts uploads only from a sender session that passed signature verification and local approval.

Cloud Transfer Profile

Cloud-assisted transfers use relay services for coordination and encrypted chunk storage. The relay may know transfer identifiers, device identifiers, file counts, chunk counts, chunk sizes, timing, and encrypted bytes. It does not receive plaintext, private keys, shared secrets, or derived transfer keys.

The current encrypted cloud mode identifier is:

x25519-aesgcm

A cloud transfer includes:

For upload, the sender derives:

shared_secret = X25519(sender_E_priv, receiver_E_pub)
file_key = HKDF-SHA256(shared_secret,
                       info = "filie-cloud-transfer:" || transfer_id,
                       length = 32)

For receive, the receiver derives:

shared_secret = X25519(receiver_E_priv, sender_E_pub)
file_key = HKDF-SHA256(shared_secret,
                       info = "filie-cloud-transfer:" || transfer_id,
                       length = 32)

If the receiver no longer has the private key corresponding to the advertised receiver ephemeral public key, the transfer cannot be decrypted.

Multi-Recipient Transfer Profile

A transfer may be addressed to more than one recipient device. The encrypted cloud mode identifier for this shape is:

filie-envelope-v1

In envelope mode, file content is encrypted once under a fresh key, and that key is wrapped separately for each authorized recipient device.

One-to-one transfers, over both LAN and cloud paths, continue to use the pairwise x25519-aesgcm mode described above, where the transfer key is derived from a shared secret rather than transmitted in wrapped form. Envelope mode applies to transfers addressed to a group.

Content Keys

Each transfer uses a fresh random 256-bit content key generated by the sender. Chunks are encrypted with AES-256-GCM under the content key rather than under a key derived from a pairwise shared secret:

content_key = random(32)
box_i       = AES-256-GCM-Encrypt(content_key, plaintext_chunk_i)

Encrypted output is serialized as:

nonce(12) || ciphertext || mac(16)

The content key itself never reaches the relay in any form other than as recipient-wrapped ciphertext.

Signed Encryption Prekeys

Recipients are addressed by signed X25519 encryption prekeys rather than by a currently advertised ephemeral key. A device publishes prekeys signed by its durable Ed25519 identity key:

{
  "id": "pk_...",
  "type": "x25519",
  "publicKey": "BASE64URL(X25519 public key)",
  "createdAt": 123,
  "expiresAt": 456,
  "signature": "BASE64URL(Ed25519 signature)"
}

The signature covers the canonical serialization of id, type, publicKey, createdAt, and expiresAt. A sender verifies the prekey signature against the recipient's trusted Ed25519 identity key, and checks the validity window, before wrapping anything to it. Ed25519 keys are never used for encryption; they authorize the X25519 material that is.

Once a device trusts another device's Ed25519 identity, it accepts signed prekey updates from that device without a further pairing ceremony.

Recipient Envelopes

Each authorized recipient receives one envelope:

{
  "deviceId": "dev_receiver",
  "identityKey": "BASE64URL(Ed25519 public key)",
  "prekeyId": "pk_...",
  "encryptedContentKey": "BASE64URL(nonce || ciphertext || mac)"
}

The key-encryption key is derived from ECDH between the sender's transfer ephemeral key and the recipient's signed prekey, domain-separated from any other key derived from the same secret:

kek = HKDF-SHA256(X25519(sender_E_priv, recipient_prekey_pub),
                  info = "filie-envelope-v1:content-key-wrap",
                  length = 32)

encryptedContentKey = AES-256-GCM-Encrypt(kek, content_key)

The recipient derives the same key-encryption key from its own prekey private key and the sender's ephemeral public key, unwraps the content key, and decrypts chunks. A device that is not an intended recipient derives a different key and fails authentication.

As with single-receiver transfers, a recipient that no longer holds the private key for the referenced prekey cannot decrypt the transfer.

Group Governance

A group is a persistent scope whose membership and sender authority are carried in signed governance checkpoints. The checkpoint, not the server record, is the cryptographic source of truth:

{
  "scopeId": "group-...",
  "type": "equal_group",
  "epoch": 8,
  "createdAt": 123,
  "sendPolicy": "open",
  "members": [
    {
      "deviceId": "dev_...",
      "identityKey": "BASE64URL(Ed25519 public key)",
      "role": "owner"
    }
  ],
  "previousCheckpointHash": "...",
  "signedBy": "dev_...",
  "signature": "BASE64URL(Ed25519 signature)"
}

sendPolicy is signed state: open allows any member to send, broadcast allows only owners to send while members receive. Because it is inside the signed payload, a member cannot grant itself send authority locally.

Members are canonically ordered by deviceId before signing, so the signature and checkpoint hash do not depend on insertion order. Presentation metadata — group name, member display name, and platform — is deliberately excluded from the signed payload; it propagates for display but carries no authority.

A scope may have more than one device holding the owner role.

The checkpoint hash is:

checkpoint_hash = BASE64URL-NOPAD(SHA-256(canonical_payload))

Each epoch carries the hash of the epoch it supersedes, forming an ordered chain.

Checkpoint Authority

A new epoch must be signed by a device holding the owner role in the epoch it supersedes.

One exception exists so that a member can leave without owner assistance. A departing device may sign an epoch that removes only itself. Verifiers accept such a checkpoint only when the signing device is absent from the new roster and every remaining member is unchanged in device id, identity key, and role. A departing member therefore cannot alter anyone else's membership or role on its way out.

Verifying a Group Transfer

A receiving device:

  1. Resolves the scope id and policy type on the transfer.
  2. Verifies the referenced governance checkpoint against its last trusted checkpoint, rejecting any epoch lower than the highest already verified.
  3. Confirms the sending device was a member authorized to send under that checkpoint's sendPolicy.
  4. Unwraps its own recipient envelope and decrypts.

The relay may store, index, and route group state, and it decides fanout, but it cannot forge a checkpoint, promote an unauthorized sender, or read content. Omission by the relay is an availability problem, not a confidentiality break.

Removal Is Prospective

Removing a device advances the group to a new signed epoch that excludes it. From that epoch forward the removed device receives no further content-key envelopes and no further group control messages.

Removal does not revoke access to content the device already holds. A transfer whose envelope the device already received remains decryptable by it, and files already saved on that device cannot be withdrawn. Implementations should say so plainly in any removal interface rather than implying retroactive revocation.

Stranger Transfer Profile

A stranger transfer is a cloud-assisted transfer discovered through a short code. The short code is a rendezvous locator, not an encryption secret.

The relay uses the code to connect sender and receiver long enough to exchange signed device envelopes and public agreement keys. File encryption still uses the same X25519, HKDF, and AES-GCM pipeline. Compromise of the short code alone does not reveal file contents.

Retention Profile

The core protocol requires only that private agreement keys stay local. It does not mandate a specific private-key retention duration.

A deployment may choose a bounded retention profile for recent ephemeral private keys to support delayed delivery, offline devices, retries, and cold starts. This affects the security/usability tradeoff:

For public descriptions, it is accurate to say that Filie uses bounded local key retention when that profile is enabled. Avoid describing bounded retention as full perfect forward secrecy. It limits historical exposure, but retained keys can decrypt the ciphertext they cover if the endpoint is compromised while those keys are still available.

Security Properties

Filie is designed so that:

Non-Goals and Metadata

Filie does not hide all metadata by itself. Depending on the transfer path, a network or relay observer may learn:

Network metadata reduction can be layered below Filie, for example with a VPN or multi-hop transport. That is separate from Filie's payload encryption protocol.

Filie also does not protect against:

Post-Quantum Migration

The current X25519 key agreement and Ed25519 signatures are classical elliptic curve primitives. They are not considered resistant to a future cryptographically relevant quantum computer.

A post-quantum migration profile should use hybrid key establishment:

classical_secret = X25519(...)
pq_secret        = ML-KEM.Decapsulate(...) or ML-KEM shared secret
hybrid_secret    = HKDF-SHA256(classical_secret || pq_secret,
                               info = "filie-hybrid-session",
                               length = 32)

Transfer keys would then be derived from hybrid_secret rather than directly from the X25519 shared secret. Hybrid mode preserves compatibility with current classical security assumptions while adding protection against harvest-now, decrypt-later attacks if the post-quantum KEM remains secure.

A future identity migration may similarly use hybrid signatures, such as Ed25519 plus a standardized post-quantum signature scheme. Payload secrecy should prioritize hybrid key establishment first.

Versioning

Protocol profiles should be identified explicitly when serialized on the wire. Examples:

x25519-aesgcm
filie-envelope-v1
x25519-mlkem-aesgcm

Receivers must fail closed when a transfer requests encryption but the relay or peer does not return the expected encryption profile. Unsupported cryptographic profiles must be rejected rather than silently downgraded.

Open Protocol Statement

This document describes Filie's application-layer end-to-end encryption protocol. Filie uses documented message formats and standard cryptographic primitives rather than secret proprietary cryptography.

The control plane is human-readable JSON. Discovery, identity, transfer setup, and public key material can be inspected directly on the wire. File contents are not carried in plaintext; they are carried as opaque authenticated-encrypted chunks. This keeps the protocol open and auditable while protecting transfer contents and detecting payload tampering.