1. The short answer
When your phone is showing your PC's screen, that picture is going straight from one device to the other. It is encrypted at the PC and decrypted at the phone, and the same is true of the PC's sound, of every tap and keystroke you send back, and of any file that moves between them. Our server's job is to introduce the two devices to each other. Once they are talking, it is not in the middle of anything, and it holds none of the keys that would let it read what passes.
The introduction is where the interesting work happens. Your PC shows an eight-character code and you type it into your phone. That code is never transmitted. Not to us, not even to your phone. Instead both devices use it as a secret ingredient in a standard cryptographic handshake, and what crosses the network is only the output of that maths. An eavesdropper who records the entire exchange learns nothing about the code, and cannot work backwards to it later.
The part we can see is the fact of the connection: that a device at one address and a device at another address met in a particular room at a particular time. That is real. Section 4 says so plainly rather than rounding it down to "we see nothing".
What follows is the detail. Section 6 is the one to read if you only read one. A security page that lists its strengths and not its gaps is an advertisement.
2. How a pairing works
The code, and why it is cut in two
The code on your PC is eight characters long, drawn from a 32-symbol alphabet: the digits 2 to 9 and the letters A to Z, with the look-alikes 0, O, 1 and I removed so that nothing is ambiguous when you are copying it off a screen.
It is deliberately split. The first three characters are public. They are put through a one-way function to produce the name of a meeting room on our server, which is how your two devices find each other. The last five characters are the secret, and they are the actual password. They are never given to our server in any form, so the relay cannot learn the password even in principle. The room name is not a hash of the password either. Nobody who learns it can take it away and grind through guesses offline.
Five characters from 32 symbols is 33,554,432 combinations, or about 25 bits.
The handshake
Pairing uses SPAKE2, a published, standards-track password handshake specified in RFC 9382. It is not a scheme we invented. Neither side hand-rolls the maths: each app takes its elliptic-curve work from an established open-source library, pinned to an exact version, and its hashing from a separate established one. The two apps are written in different languages, so they use different libraries. Section 7 names all of them, including the one place the phone's library does not meet what the RFC asks for. The two fixed points the protocol requires are the ones written into the RFC itself, embedded verbatim so that nobody, us included, could have chosen them for a hidden reason.
The property is this: both devices end up holding the same secret key only if they both knew the code, and someone recording the exchange learns nothing at all. The handshake also gives no partial credit. There is no "warmer" signal, no prefix that matches, nothing to narrow the search with. A guess either completes or it does not.
Guessing is throttled, twice
Two independent limits run before any cryptography happens, so a locked-out attempt costs an attacker a refusal rather than a guess:
- Five wrong tries from one address freezes that address for 30 seconds.
- Ten wrong tries from anywhere at all freezes everybody for 45 seconds.
The second one exists because the first is easy to defeat: on modern home internet a single subscriber is routinely handed billions of addresses, so a per-address limit alone is a speed bump. Working at the fastest rate those limits permit, getting through half of the 33.5 million possibilities takes roughly two and a half years. That number is arithmetic from the two limits above, not the result of an attack anyone has run.
A wrong guess does not burn the code
Typing the code wrong does not force a new one. This is a deliberate choice and it was made the other way round first. If a wrong guess regenerated the code, then any stranger on the internet making one bad guess would strand you halfway through your own pairing, and one mistyped character would mean walking back to the PC. Since guessing is bounded by time rather than by attempts, rotating on failure bought no security and cost real usability.
The code does expire on its own: after ten minutes in which the PC saw no pairing activity at all. It will not rotate out from under you while you are mid-way through typing it, because a rotation requires that nobody is in the middle of a handshake, that no connection is waiting, and that the full ten minutes has passed. It is also replaced after a successful pairing and whenever the app restarts.
Nothing leaves the PC before the phone has proved itself
An unverified phone is negotiated with on a separate, isolated connection whose video and audio slots are deliberately left empty. The connection is built with no camera plugged into it. The real screen is attached only at the moment the PC has verified the phone's proof. There is no window during pairing in which a frame could escape to someone who guessed wrong.
The PC's own Tether window is hidden from screen recording and screenshots at the operating-system level, always, with no setting to get wrong. The pairing code cannot be captured by the very screen-sharing the app provides.
The phone is deliberately not the same, and you should know which way round it is. Android can blank the app in exactly the same way, and Tether offers it as a switch called "Block screenshots & recording". But it ships off, because a great many people quite reasonably want to record or cast their own session. With it off, anything recording your phone's screen can see the code while you type it. We chose the side of not surprising you; if you would rather have the protection, the switch is in Settings and it covers the whole app.
What the phone keeps afterwards
After a successful pairing the phone stores a key that was never transmitted: both devices calculated it independently from the handshake, and it exists on both sides only because both sides genuinely completed it. That key also moves forward every time you reconnect. Each reconnection derives the next key from the last, so a stolen key goes stale on its own rather than lasting forever.
Stale is not the same as instantly dead. The PC keeps the current key and the one before it, so a stolen copy still works for one more of your reconnections and dies on the second. That one-step grace is deliberate: if the PC advanced the key and the confirmation never made it back to your phone, a system without the grace would lock you out of your own PC and make you pair again. The first time a key gets used out of step, the PC notices and records it as a possible copy.
The phone also pins the PC's certificate. If it ever sees a different one, it stops and says so: "This PC doesn't look like the one you paired with…" It does not silently connect anyway. The only ways back in are deliberate: scan the PC's QR code again, or explicitly forget the PC and start over. Neither happens on its own.
That pin is only ever accepted from a route that is already trustworthy: either out-of-band through the QR code, where it travels from the PC's screen to the phone's camera and never touches the network at all, or inside the handshake carried by an authentication tag that only a device holding the handshake secret could produce. A stripped or tampered tag aborts the pairing. A pin learned from the QR code always wins and is never overwritten.
3. What protects a live session
Once you are connected, everything rides on a single standard WebRTC connection, encrypted with DTLS-SRTP. That is the same mechanism that protects browser video calls. On that one connection: a video stream carrying the screen, an audio stream carrying the PC's sound, and three separate data channels. One is for control, and is fully reliable: a keystroke or a clipboard paste is never allowed to go missing. One carries file bytes, also fully reliable. The third carries cursor movement and is deliberately allowed to drop packets rather than retransmit them, because a mouse position that arrives late is worse than one that never arrives.
An honest note on ciphers: Tether does not choose the encryption algorithm. It is negotiated between the two WebRTC implementations at runtime, and nothing in our code pins a particular cipher suite. We will tell you the mechanism is DTLS-SRTP, which is verifiable in our source. We will not print a specific algorithm name that our own code does not actually guarantee.
Why the relay cannot quietly sit in the middle
This is the question a technical reader should ask first, because "the server just passes messages along" is not by itself a guarantee. A server that passes messages along could equally well substitute its own and terminate both halves itself.
The answer is that both devices fold the connection's cryptographic fingerprints into the proof they exchange during pairing. If anything had re-terminated the connection in the middle, at least one of those fingerprints would be different, the two sides would compute different keys, and the proofs simply would not match. Pairing aborts. The relay never holds the keys that encrypt your session, and it cannot substitute its own without the mismatch being detected. That is the actual proof. Not a promise about our conduct. A property of the maths.
The same binding protects every later reconnection, not just the first pairing. Each reconnect proof includes both devices' fingerprints, a fresh random value from each side, and a label saying which device is speaking, so a recorded proof cannot be replayed and cannot be reflected back at the sender. The reconnect key itself is never transmitted.
One detail, because it is the sort of thing that goes wrong quietly: the code that reads those fingerprints out of a setup message refuses to guess. If a message contains more than one fingerprint and they disagree, pairing is aborted rather than interpreted. That check exists because an earlier, sloppier version could have been fed a decoy fingerprint alongside a rewritten real one. Both sides would have bound the decoy, every proof would have verified, and a relay would have been holding both halves of the call with nothing to show for it.
The PC does not listen to the whole network
The PC's listener is never opened to every network interface. It binds to the local machine always, to your private encrypted network when one exists, and to your home network's specific addresses in three cases only: when home-network access is switched on, when a phone is already paired and that allowance is on, and while the pairing card is actually open on screen. That last one exists so a first pairing can still complete over Wi-Fi when the PC's internet is down, and it closes again with the card. It never listens on everything. Separately, any connection arriving with a web-page origin attached is refused outright, which closes the trick where a malicious website in your browser tries to talk to software running on your own machine.
4. What our servers can and cannot see
Tether uses one server of ours, which we call the switchboard. Its only job is to let your two devices find each other and swap the setup messages that get a direct connection started.
What it cannot see
- Your screen, the PC's sound, your keystrokes, your clicks, your files. None of it passes through the switchboard, and it holds no key that could decrypt any of it.
- The secret half of your pairing code. Only the public three-character slice ever reaches the server, and the room name derived from it gives no way to test guesses at the password.
- Your licence code, in readable form. Before it touches any storage key or log line it is converted to an unreadable one-way fingerprint using a secret only the server holds. Anyone who obtained a copy of our database still could not confirm whether a guessed code was real.
- Your PC's name. Windows machine names commonly contain the owner's name, so it is never sent. The default name your phone and our server see is the literal word "PC". Your exact screen resolution is rounded off before it is sent, for the same reason: an unusual resolution is a fingerprint.
What it does see
- Both devices' IP addresses, because it cannot answer a request without one. On the metered paths it is fingerprinted rather than stored raw, but the request itself necessarily handles it.
- Which room, and when. That two devices met, at what time, for how long, and whether one of them is currently online.
- The setup messages themselves, in readable form. They are relayed byte-for-byte, with no parsing, rewriting or inspection anywhere on that path. But "relayed unchanged" means the server could read them, and honesty requires saying so. Those messages contain the handshake values, the network candidate addresses your devices are offering each other, the certificate fingerprints, and the PC's display name if you set one. They never contain media.
Everything the app sends us, in full
There are five outbound requests, and this is all of them:
- The rendezvous connection. Its web address carries the room name and whether this device is the PC or the phone.
- A room claim. Sends the room name and your licence code, and gets back a signed token proving your PC owns that room, so a stranger cannot displace it.
- A licence check. Sends the licence code and a single word for the device type ("pc" or "phone") and nothing else. No machine identifier, no operating system, no version.
- A request for relay servers, used only when your two devices cannot reach each other directly. Sends the licence code in a header, and only ever to our own switchboard.
- An update check, when the app starts and once a day afterwards. It identifies itself as the bare word "PCTether". Deliberately no version, no operating system, no machine detail: a richer string would be a fingerprint we would then be storing about our own users in our own logs.
Tether is free while it is in early access. The licence code is what switches remote access on; nothing is charged for it right now.
No analytics, and we checked rather than assumed
Neither app contains an analytics, telemetry or crash-reporting product. We searched both codebases for every common vendor by name and found none. A search that finds nothing proves nothing about the search, so we ran the same search for a third-party service we do have code for. It found it. The instrument works.
This website is held to the same rule by its build: if any page referenced a resource from another company's servers, the site would fail to build and could not be published. The only outside addresses anywhere on the site are ordinary links you can choose to click: Stripe's and Cloudflare's own policies, which naming your suppliers requires. The typefaces on this page are served from pctether.com for exactly this reason.
5. What is stored, and where
On your PC
- The reconnect key for each paired phone. Encrypted. It is sealed with your Windows account login, so a copy of the file taken to another machine or another account decrypts to nothing. If that sealing is ever unavailable, the app refuses to write the key at all rather than quietly saving it in readable form; the cost to you is re-pairing on the next launch, which takes about ten seconds. Your phone's raw device identifier is never written down either. Only a one-way hash of it.
- Your licence code, in plain readable text. It sits in the app's settings file as ordinary text. Anyone who can read your Windows user profile can read it. We are stating this rather than leaving you to find it: it is protected by only ever being sent back to the switchboard that issued it, not by encryption on your disk.
- Connection statistics that contain no addresses. The app records what kind of route each connection used and whether it was IPv4 or IPv6, and deliberately not where the other end was. A stored remote address would be a record of where you physically were.
- A relay-usage ledger of monthly totals only. Numbers and a year-and-month label, capped at twelve months, never transmitted anywhere. Knowing that four gigabytes moved in March cannot place anyone anywhere; a dated list of sessions would be a diary of when you were away from home, so there isn't one.
- An error log with the identifying parts removed. IP addresses, MAC addresses, pairing codes and your Windows username are stripped out before anything is written.
On your phone
The reconnect key is held in Android's Keystore-backed secure storage. That is the same facility the operating system offers any app for credentials. On phones with a secure chip for the purpose, that storage is backed by the hardware; the app asks for the standard protection rather than demanding the hardware kind, so on a phone without it you get the software-protected version instead. We do not check which one your phone gave us, so we are not going to claim the stronger one.
On our servers
- Your licence code as an unreadable keyed fingerprint, never in readable form on any connection path.
- One timestamp of when a code first connected, plus one word for the kind of device. That is the whole per-connection record, and it exists so a code being passed around can be noticed.
- Setup messages, briefly, and only when one device arrives before the other. Capped at 32 messages, deleted the moment the second device connects or after five minutes of an empty room. This is the one place connection data is written to storage rather than passed straight through.
- If you joined the waiting list, your email address. It is deleted outright the first time your invite is used. Not blanked and kept as a hash: a hash of an email address is still an identifier for that person, because email addresses are short and guessable.
One honest exception to "codes never appear in logs": on the billing path, when a purchase is refunded or reinstated, the first seven characters of the licence code are written to a log line. The codes we issue are fourteen characters, so that is half of one, and it happens only on those two payment events. Never on a connection. (A code typed by hand for someone can be as short as ten, in which case the same seven characters are more of it.) It is small, but "never" would be the wrong word and we are not going to use it.
On this website
The homepage keeps nothing in your browser at all: no cookies, no stored settings. One page is an exception. After a purchase, the thank-you page stashes your licence link for that browser tab only, so that pressing refresh does not destroy a code you have just been given. It is discarded when the tab closes and it is never synced anywhere.
The same ground, stated as a legal commitment rather than an engineering description, is in the privacy policy.
6. What this does not protect you from
Anyone who can see the code can pair
Someone glancing over your shoulder, a photo of your monitor, a camera behind you. Any of those is a complete break of first-time pairing. The maths protects your code from everyone on the network; it does nothing about a person in the room. This is inherent to any system where a shared password is the only thing establishing trust, and it is why the code expires when idle and why the app's own window is hidden from screen recording. Neither of those helps against a pair of eyes.
There is no "add a device" mode
While the PC app is running, it will accept a brand-new pairing attempt at any moment. You are never asked to open a pairing window first. Worse, home-network access is on by default once you have paired one phone, which means a paired PC keeps a pairing listener available on your home Wi-Fi indefinitely. Guessing the code still takes years, so this is exposure rather than a break. But a stranger on your network gets unlimited attempts at the throttled rate, and you are never told. There should be a switch that closes the door. There is not one yet.
Anyone who controls your PC has already won
Malicious software running under your Windows account can read the stored reconnect key and can tamper with a live session, because the key is deliberately sealed to that account so that you can use it. This is the normal boundary for software of this kind and we are not going to pretend otherwise: if your PC is compromised, Tether's encryption is not the thing standing between you and the attacker.
An unlocked, stolen phone is full control of your PC
The reconnect key lives in secure storage and the app can be locked behind your fingerprint or PIN, but neither of those helps if someone is handed your phone already unlocked. Until the app lock engages, a stolen unlocked phone that has been paired is your PC.
And there is no way to cut it off from somewhere else. This is the first thing anyone in that situation asks, so: the only way to remove a phone's access is to go to the PC in person and forget it there. We cannot do it for you and neither can you from another device. There is no account to sign into. That is a deliberate privacy choice, and this is the cost attached to it. A key that simply goes unused does expire on its own, but the window is 180 days, chosen so that a real owner coming back from a long trip is not silently locked out. A stolen phone left switched off is therefore still paired six months later. If your phone is stolen and you cannot get to your PC, the honest advice today is to keep the PC turned off until you can.
We can see who connected to whom, and when
Both IP addresses, which room, the timing, whether you are online. We cannot see your screen, your keystrokes or your files. But "cannot see your screen" is not the same claim as "cannot see that you connected", and the second one is not true. If your threat model includes hiding the fact of the connection itself, Tether does not do that today. Encrypting the introduction as well as the conversation is a change we have not made yet.
Your internet provider can see you use Tether
The app opens a connection to our switchboard when it starts and checks for updates once a day. The contents are encrypted; the destination and the timing are visible to anyone watching your network. Nothing in any software can change this. It is a property of connecting to a named server. The daily update check is, in effect, a once-a-day signal that this PC is running Tether.
We do not choose the session's encryption algorithm
DTLS-SRTP is the mechanism, and that is verifiable. The specific cipher is negotiated between the two WebRTC implementations at run time and nothing in our code pins one. Any page that told you Tether uses a named algorithm would be claiming something its own source does not support.
The Windows installer is not signed
Every Windows user currently clicks through an "unknown publisher" warning to install software that takes their screen, keyboard and mouse. That is exactly the warning a person should pay attention to, and we are asking them to ignore it. It is the single worst thing about how Tether reaches you today, it is worse than anything in the encryption, and a code-signing certificate is the fix. Measured by our own audit on 2 August 2026.
Licence checking is not a security control
Whether your code is valid is checked by the apps themselves, with a fourteen-day offline grace period so a network blip does not lock you out mid-session. A modified copy of the app is not stopped by that layer. It is an access control sitting on top of the encryption; it never touches the encryption, and it should never be described as protecting anything.
A stranger can block a first-time pairing over the internet
Meeting rooms are named from only the public three characters of the code, which is a deliberately small space. Someone sweeping through it could occupy the room your PC is waiting in and stop a new pairing from completing. Roughly an hour of effort covers the whole space. Our server pushes back on clients that dial many different rooms, and a PC already holding a room cannot be evicted from it, so this is a nuisance rather than a break: occupying a room never reveals the password, because the password is in the other half of the code. Phones that are already paired are unaffected.
Certificate pinning does not work the same way on both paths
On your home network the phone checks the PC's certificate directly, against an exact copy it remembers. Over the internet it does not: it trusts the ordinary web certificate of our switchboard for that connection, and establishes the PC's identity separately inside the messages, bound to the same fingerprints described in section 3. The guarantee holds. But it holds by a different mechanism, and a reader who assumed "certificate pinning everywhere" would be mistaken.
One asymmetry we have found and not yet fixed
The phone will play incoming sound from the far end before that far end has finished proving who it is. Video and remote control are correctly held back until the proof completes; audio is not. In practice the reach is narrow, because a legitimate PC attaches no audio at all until after it has verified the phone. It only matters against a far end that is already hostile. It is still a genuine inconsistency with the rest of the design, we found it in our own audit, and it is listed here rather than waiting for the release that closes it.
The phone does not meet one requirement the pairing standard sets
RFC 9382 says an implementation must do its curve arithmetic in constant time: the same number of steps no matter what the secret is, so that measuring how long it takes tells you nothing. The library the phone app uses does not guarantee that. The exposure is narrow: an attacker would need to time the operation precisely, the secret exists for a few seconds, and guessing is throttled regardless. We accepted it knowingly rather than by oversight. It is still a "must" in the specification that we do not currently meet, and the only place that was previously written down was a comment in our own source.
A dormant Google push component ships inside the PC app
The installed app contains a file that would send notifications through Google's push service. Nothing runs it. It is not connected to any part of the app, it has never been switched on, and no data goes to Google today. It is there because closed-app notifications are a feature we started and have not finished. We list it for the same reason as the disabled analytics tag below: a page that tells you which servers the app talks to should also tell you which ones are sitting in the binary switched off.
A disabled analytics tag exists in our source, but not in the page you receive
Our own copy of the homepage keeps a Cloudflare analytics snippet commented out, with a placeholder where an account token would go. It has never run. As of 3 August 2026 it no longer even reaches you: every comment is now removed from the files the website serves, so the snippet is not in the page your browser downloads. You can confirm that yourself with View Source. We are still listing it here, because it is one line away from being switched on at our end, and a page claiming no trackers should say where the code is rather than let you assume it does not exist.
That comment-stripping was not done for tidiness. Comments are invisible on the page and fully visible in the source, and that combination is what stops anyone noticing them. Ours had accumulated developer notes, internal document names and an unfinished legal task. None of it was ever meant for you. Rather than police the wording, we stopped shipping comments at all.
Cloudflare and Stripe are unavoidable third parties
Our switchboard runs on Cloudflare and payments run through Stripe. Everything described on this page stops at the edge of our own code. What Cloudflare records in its own logs and what Stripe keeps for payments are governed by their policies, not by ours. Something specific, then: the room name travels as part of the web address of the rendezvous connection, and web addresses are the part of a request that edge logs typically capture. Nothing in our code writes it down. We do not control what Cloudflare's does.
Nothing here protects you from someone at your unlocked machine
Your licence code is readable text on disk, the settings file is ordinary text, and the list of files queued to send is a plain list of file paths. The reconnect keys resist being copied to another computer, but not somebody sitting at yours while you are logged in.
7. For the technically minded
The primitives, named exactly. Anything not on this list is not something we claim.
- Pairing handshake
- RFC 9382 SPAKE2, ciphersuite P256-SHA256-HKDF-HMAC, protocol
profile string tether-pin-pake-v1. The two ends run
different stacks, both version-pinned exactly. PC: group operations
from @noble/curves 2.2.0, HKDF and HMAC from Node's own
crypto. Phone: group operations from
pointycastle 4.0.0 (secp256r1), HKDF and HMAC from
cryptography_plus. The RFC's M and N seed points are
embedded uncompressed as nothing-up-my-sleeve constants.
One known shortfall, on the phone. RFC 9382 §7 says the scalar multiplication MUST be constant-time; pointycastle's is not. A library that takes a measurably different amount of time depending on the secret can, in theory, leak that secret to something able to measure it precisely. We judged the practical risk small (the secrets exist for seconds and guessing is throttled), but it is a stated MUST that we do not currently meet. Section 6 says so rather than leaving it in a source comment where only a developer would find it. - Password derivation
- The code is turned into a scalar and never sent: w = OS2IP(HKDF-SHA256(ikm = utf8(code), salt = empty, info = "tether-pin-pake-v1|w", L = 40)) mod n Forty bytes of output gives 64 bits of headroom over the curve order, which removes modulo bias. Only 65-byte curve points and 32-byte HMACs cross the wire.
- Code layout
- 8 characters over a 32-symbol base32 alphabet (2-9, A-Z, less 0/O/1/I). Characters 1-3 are public; characters 4-8 are the SPAKE2 password. 325 = 33,554,432, about 25 bits.
- Rendezvous room
- Derived from the PUBLIC slice only, so it is not an offline oracle for the password: roomId = base32(HKDF-SHA256(ikm = utf8(Cpub), salt = empty, info = "tether-room-v1", L = 5)) The result is 8 base32 characters.
- MITM binding
- Both DTLS certificate fingerprints are bound into the
confirmation keys, in a fixed PC-then-phone order:
AAD = utf8(fpPC) ‖ 0x00 ‖ utf8(fpPhone)
KcA ‖ KcB = HKDF-SHA256(ikm = Ka, salt = empty, info = "ConfirmationKeys" ‖ AAD, 32)
cA = HMAC-SHA256(KcA, TT)
cB = HMAC-SHA256(KcB, TT) A relay presenting its own certificate changes a fingerprint, so both MACs fail. - Fingerprint parsing
- Line-anchored extraction of every a=fingerprint line, canonicalised; a value is returned only if the set collapses to exactly one. More than one distinct fingerprint aborts the pairing rather than picking a winner.
- Pairing key
- Derived on both sides and never transmitted in any form: pairingKey = HKDF-SHA256(ikm = Ke, salt = empty, info = "tether-pin-pake-v1|pairing-key", L = 32) Here Ke is the first 16 bytes of SHA-256(transcript).
- Reconnect proof
- Sent on every reconnection in place of the code: HMAC-SHA256(pairingKey, utf8(role) ‖ 0x00 ‖ nonceH ‖ nonceP ‖ 0x00 ‖ utf8(fpPC) ‖ 0x00 ‖ utf8(fpPhone)) The role is one of the anti-reflection literals "phone" or "PC", and both nonces are fixed at 16 bytes. Fresh nonces make a captured proof replay-dead; the fingerprints make it die if the media path was re-terminated.
- Key ratchet
- Applied on every reconnect. A proof is accepted under the previous, current or next generation: a two-step grace plus a bounded one-step forward catch-up, so a crash between advancing and saving never strands the owner. A captured key therefore survives one further legitimate reconnect and dies on the second, and the out-of-step use raises a possible-copy signal: key(n+1) = HKDF-SHA256(ikm = key(n), salt = empty, info = "tether-reconnect-v1|ratchet|" ‖ uint32be(n+1), 32)
- Certificate pin
- Checked as an exact SHA-256 of the
certificate's DER bytes (no CA path, no hostname check) on the direct/LAN leg only.
A mismatch raises a fingerprint-mismatch error and there is no plaintext leg on that
path to fall back to, so a downgrade is structurally impossible rather than forbidden
by policy. The pin is accepted only out-of-band via the QR code's fingerprint field, or
in-band carried by an authentication tag:
fpMac = HMAC-SHA256(master, utf8("tls-fp") ‖ 0x00 ‖ utf8(fpHex))
master = HKDF-SHA256(ikm = Ke, salt = empty, info = "tether-pin-pake-v1|master", 32) - Session transport
- One RTCPeerConnection: a sendonly video transceiver, a sendonly audio transceiver, and three SCTP data channels: control (ordered, reliable), file (ordered, reliable) and input (ordered, maxRetransmits: 0). Data channels ride SCTP inside the same DTLS association as the media. DTLS-SRTP; no cipher suite is pinned anywhere in our source.
- Pre-auth isolation
- An unverified peer negotiates on a second, isolated peer connection with trackless sendonly transceivers. DTLS still completes, so the fingerprints exist to bind into the confirmation, but no media exists to leak. replaceTrack() fills the senders only after the host has verified the MAC.
- Throttles
- Per-IP: 5 failures, 30-second cooldown. Global sliding window: 10 failures per 60 seconds, 45-second cooldown. Both evaluated before any curve arithmetic runs.
- Network perimeter
- Binds 127.0.0.1 always, the CGNAT range 100.64.0.0/10 while that interface exists, and concrete LAN IPv4 addresses only when LAN exposure is on. Never 0.0.0.0. Any WebSocket upgrade carrying an Origin header is refused with 403 before a socket exists, which closes browser-driven DNS rebinding.
- At rest — PC
- Electron safeStorage, which is Windows DPAPI, bound to the logged-in account. Fail-closed: if sealing is unavailable the store writes nothing and keeps trust in memory for that session only. The device identifier is persisted as SHA-256 only.
- At rest — phone
- Android Keystore-backed secure storage.
- At rest — server
- Licence codes are stored and logged only as a domain-separated keyed HMAC, truncated, under a server secret. That was chosen over a plain hash specifically so that an attacker holding a database dump plus a candidate code could not recompute the value and confirm the code was live. Client IPs on the metered path are fingerprinted the same way.
If you find something on this page that is wrong, or something we have left out, write to [email protected]. A correction to this page is worth more to us than a compliment about it.