‹ All Air Kalon docs

Air Kalon

Security and privacy

What Air Kalon protects, what it does not, what an onlooker can capture, and how the receiver defends itself against hostile frames.

The short version

A transfer has no network attack surface, because it uses no network. The only connection in Air Kalon is the Mac app's update check, which installs nothing that isn't signed by Kalon's developer and notarized by Apple. Air Kalon does not encrypt the stream. Anyone who can see the sender's screen clearly enough can capture the whole file. Treat a transfer the way you would treat holding a printed page up to someone across a room.

What never happens

  • Transfers involve no network. Nothing is uploaded, synced or reported, and there is no server, account or analytics. The Mac app's one connection is its update check, described below.
  • No radio is used for the transfer. Wi-Fi, Bluetooth and cellular can all be off.
  • The only permission the apps ask for is the camera: on the receiver while receiving, and on the sender only while a stream is on air with auto-stop switched on.

Updates on the Mac

Air Kalon for Mac keeps itself up to date from its GitHub releases. It reads a small file describing the newest release twice a day and when you choose Check for Updates…, sending nothing but the version you have, over HTTPS only. With Update automatically turned off, in the Software Update window, Air Kalon makes no connection at all until you choose Check for Updates….

An update is installed only if every check passes:

  • The download matches the size and SHA-256 published with the release.
  • Every name in the archive sits inside Air Kalon's own app, with no absolute paths, no .. and no links, checked before anything is unpacked.
  • The app inside is Air Kalon, newer than the copy installed, signed with Kalon's Developer ID and notarized by Apple, under a strict check of all of its code.
  • The same checks run again just before the swap, which happens as Air Kalon quits, in one step, so there is never a moment without a working app.

A tampered download site can at worst hold an update back; it cannot get anything else installed. The iPhone and iPad app does not check for updates.

What an onlooker can capture

The frames are plaintext. A camera that can read the sender's screen, from a phone, a security camera or a reflection, can run the same decoder and recover:

ItemWhere it travels
File contentsEvery frame
File name, media type and kind (file, snippet or bundle)Inside the KCF1 container, in the clear
Original and transmitted sizeContainer header and every frame header
SHA-256 of the fileContainer header
Session, progress and completionFrame headers, and the receiver's acknowledgement code

Every frame header reserves a cipher suite byte for future encryption, but no encryption is implemented. A frame with a nonzero suite is recognised and reported as unsupported. The specification already fixes that any future suite must authenticate the whole 24-byte header. The plan document (docs/PLAN.md) describes a planned ChaCha20-Poly1305 suite; it is not available today.

Integrity

Every transfer, of any size, passes two checks before a file is offered:

CheckAlgorithmCoversPurpose
Stream hashFNV-1a, 32-bitThe whole KCF1 containerDetects accidental corruption in decoding
File digestSHA-256The original file bytes, after decompressionMust match or the transfer fails and nothing is offered

The SHA-256 covers the file bytes only. The name, media type and flags are covered by the stream hash, which is a transport check rather than an integrity guarantee, and the receiver re-sanitises the name whatever it says.

Neither check authenticates the sender. Anyone who controls what the sender's screen shows, or who puts their own screen in front of your camera, can send any file with a matching digest. Air Kalon proves that the bytes arrived as they were sent, not who sent them.

Defences in the receiver

The receiver treats every decoded code as untrusted input.

  • Strict parsing. Every frame format has a fixed validation order, and anything that fails is ignored with no change of state.
  • Bounded allocation. A decoder never locks on to a first frame claiming more than 256 MiB, or one whose block count disagrees with its lengths. The container never declares more than 256 MiB for the file or the body.
  • Quorum before a switch. A frame from a different stream is buffered until three distinct frames of that stream arrive within two seconds. One hostile code cannot reset a nearly complete transfer. Pending candidates are capped at eight streams.
  • Bounded restarts. More than three same-session restarts in sixty seconds stops the receiver switching and reports a conflict.
  • Bounded decompression. Inflation never produces more than the declared original length plus one byte, output grows in 1 MiB steps, and trailing bytes after the DEFLATE stream are rejected.
  • Refuse, do not guess. A payload that is not a verifiable KCF1 container is refused with its reason. There is no raw fallback, so nothing unverified is ever offered.
  • Safe file names. The name is reduced to its last path component, control characters and the characters /, \ and : are removed, leading dots and spaces are trimmed, and an empty name becomes file. The receiver applies this rule again when it reads the container, and the apps take only the last path component once more when writing.
  • No automatic actions. A received link opens only when you tap Open. Only http and https links qualify, links with a user name or password are refused, and the button names the host first.

The acknowledgement channel

The receiver's acknowledgement code is also plaintext. It shows the session, the progress and the payload hash to anyone who can see the receiver's screen.

A sender honours an acknowledgement only when its mode, session and payload hash all match the live stream, and stops at most once per stream. Another device's acknowledgement in view cannot stop your transfer. Someone who can read your stream could, in principle, show a forged done acknowledgement to your sender's camera and stop it early; the effect is that the sender stops streaming, and the receiver simply does not finish.

What is stored

WhereWhat
MacReceived files are written only where you choose in the save panel
iPhone and iPadA received file is written to the app's temporary directory and handed to the share sheet
BothStream settings and receiver experiment switches, in the app's preferences

There is no transfer history. Sent files are read, not copied, except that the iPhone and iPad app keeps a temporary copy of an item dragged onto the Send screen until the next drag.

Physical considerations

  • Shield the sender's screen from other cameras when the content is sensitive.
  • Close the transfer by pressing Stop, or with auto-stop, so the stream is not left playing.
  • Check that the name and size on the completion screen are what you expected before you open a file.