TAGII · UploadMesh early access

Upload Mesh

Your devices should feel like they belong to you. UploadMesh links the devices you choose into one private mesh, without asking you to create an account. Start with your Android phone and Linux computer, pair them once, and decide what you want them to do together.

Start with two devices

UploadMesh is one product with a package for each operating system. The current early-access release supports Android and 64-bit Linux. Windows, macOS and iPhone are not released yet.

Android

Install UploadMesh on your phone

Use this package on a 64-bit phone running Android 8 or newer. Android will ask you to approve installing an app downloaded from this site.

Download for Android

Early access. The package is signed; a complete physical-phone proof is still open.

Linux

Install UploadMesh on your computer

Use this archive on a 64-bit Linux laptop, desktop or server. It includes the product, its licences, its parts list and the installer.

Download for Linux

Early access. The installer path has been run and read back on Linux.

Install it once, then choose what your mesh is for

  1. Install the Android package. Open the downloaded file, read Android's install screen, choose Install, and then open UploadMesh.
  2. Install the Linux package. Extract the archive, open a terminal in its folder, and run ./Install-UploadMesh.sh. The installer starts the node and opens pairing.
  3. Link the two devices. On the phone, open Send and choose the computer shown under Found on this Wi-Fi. Once you approve the pairing, the devices remember each other.
  4. Begin with one real job. Send a file between the devices. You decide what belongs in your mesh and which features each device may use.

Your phone

The phone is the part you carry. It can send a file, receive an offer, and let you accept or decline before anything is downloaded.

Your computer

The computer runs the same UploadMesh product for its operating system. It can find your phone on the same Wi-Fi and keep the private link available.

Your server, if you have one

A server is another device in your mesh, usually with fewer permissions. It is not a central account and it does not become the owner of your files.

Your personal SmartaiPai, if you choose one

Your personal SmartaiPai is not a second UploadMesh product or a second identity. The planned connection lets it use only the actions you allow. That connection is not finished in this public build, and the page does not pretend it is.

What has actually been proven

The simple start is above. The rest of this page shows the evidence and the unfinished work, because a claim with no execution behind it is not a result.

Every line below carries a proof reference: the build item and the test name or record line that proves it. The tests are in the repository under the names shown, and the register beside this page (CLAIMS.md) resolves each one to the file that holds it. The repository's own check refuses a citation that does not exist.

The one thing to know first: files both ways and a clipboard round trip were proven between two real machines once, on the build of 2026-08-29, with the result read back on the receiving machine. The build since then has been proven on one machine, or between a real desktop and a phone emulator. Nothing else is described here as working between your devices.

One product across the devices you choose

UploadMesh does not decide what your devices are for. It gives them one private way to recognize each other, and you choose the connections and capabilities that matter to you.

What is proven between two real machines

4 claims. Each was run between two real machines over the private network between them, on the build of 2026-08-29, with the result read back on the receiving machine and the record kept, by line, in the repository. The current build has not been re-run between two machines, and the next section says so.

Built and tested, on one machine or an emulator

32 claims. Each is in the repository with a named test that proves it with a paired negative, and several were driven on an emulator against a real desktop and read back there. The other side was a fixture, a loopback, a second process on this machine, or that emulator. Read them as engineering, not as a product promise.

What is not finished yet

This is the honest list. Nothing here is a capability of Upload Mesh today, and none of it should be read as nearly done unless it says so.

What it cannot do

Refusals and walls, not to do items. The refusals are ours and will not change. The walls are decisions of the operating systems and the networks in between, and no amount of our work removes them.

Three things it will not do, ever

  • It will not put a web page in any flow. Not for setup, not for settings, not for administration. The one exception is the very first download onto a phone that has nothing installed, where the operating system leaves no other route.
  • It will not carry your files through a server of ours. There is no upload. If your devices are all off, nothing is reachable, and that is the trade being made on purpose.
  • It will not claim something worked because a packet was sent. A wake counts on an answer. A transfer counts on the receiving side's hash. A claim on this page counts on a test or a record you can open.

Three things that are off unless you turn them on

  • Two-way folder sync. Built by driving a maintained sync program, not re-invented; ships off; two instances exchanging a file has not been run.
  • Notification relay from a phone. One decision per app, off for every app until you turn one on by name.
  • Control of a desktop from a phone. Off, gated, refused while the session is locked, and it expires on its own.

The platform walls

  • An iPhone in your pocket cannot hold the connection open. The system suspends a background app and closes its sockets. The only sanctioned way around that brings back a system consent dialog and a special entitlement from Apple.
  • An Android phone that keeps the connection alive shows a permanent notification. That is the cost of a foreground service and we are not going to hide it.
  • No app can read an Android phone's clipboard in the background. Sending a copy from a phone is one deliberate tap, and always will be. We will not reach it through the accessibility system, which would work and is an abuse of an interface built for disabled people.
  • The first install on a blank phone cannot be zero taps. A new install by any non privileged installer ends at the system install dialog. On a phone that has never allowed installs from that source, the honest count is three taps with something to read.
  • On Linux, one application cannot type into another one's window. Anything that injects input sits below the desktop and types where the focus is, which is why the control feature is built with that hazard on the screen rather than in a footnote.
  • Some networks defeat a direct connection. Address translation at both ends or a corporate network means the traffic has to relay. That is slower, and the interface is meant to say so.
  • A phone that is switched off cannot be woken. By anything.

Sources for this section: the manual's chapter 20, What this never does, and chapter 21, the threat model, each read from the platform's own documentation rather than from memory.

What we can see, and what we cannot

What the operator can see

  • That your devices exist and are linked. The coordination server holds a name you chose for each device, its public key, roughly where it is on the internet, and when it last checked in.
  • That a session happened. An audit row on your own device: what, which of your devices, when it started, when it ended, how it ended. Never the content. Chained, so an entry cannot be quietly removed, and you can verify it yourself with tagii-mesh audit verify.
  • Refusals, by reason. A refusal is logged by its class so a failure is diagnosable. Codes, keys and tokens are recorded as fingerprints or not at all.
chapter 19, What we can see · TestSettingsAPIOwnerGates · TestDiagBundleCarriesNoSecrets

What the operator cannot see

  • Your screen. Frames are held in memory and served to a paired device. No frame is ever written to disk, and the audit row carries no size and no dimensions.
  • Your files. They move device to device. Nothing central holds a copy.
  • Your clipboard. It moves device to device on the same connection. Text that looks like a secret is withheld by default and the withholding is logged so it is never a silent drop.
  • Your keys. The credential a paired device holds is never on disk in clear, and a test asserts that it never reaches a log.
chapter 19 · TestViewPauseStopsFrames · TestClipWatcherSecretFilter · TestPairedTokenNeverWrittenAsCleartext

Two honest details rather than a clean sentence. A clipboard item that has arrived sits in a file on the receiving device, unencrypted, until it is delivered; anything running as you on that machine can read it. That is a real gap and it is being worked on.

And the secret filter is a list of patterns. It will miss a bare key with no label around it, and it will withhold an ordinary sentence that happens to contain the word token. It is a seatbelt, not a guarantee.

chapter 19, Where this is weaker than it sounds

How to check any of this yourself

The point of a page like this is that you do not have to take it on trust. Here is what exists to check against, and what does not exist yet.

Proof index

The same table is kept beside this page as CLAIMS.md, with the workbook item, the record and every test resolved to its file. Counts on 2026-09-09: 4 live verified, 32 built, 14 not yet, and 1 governance decision (the licence) that is not a proof state at all. 51 rows.

Every claim on this page and the state it is in.
ClaimItemIn the repositoryState
A file sent to a second real machine landed with its hash checked on the receiving side, and a transfer that stalled resumed from its exact offsetUM.75 (part)\1, \1 (go/cmd/tagii-mesh/transfer_test.go), \1 (go/internal/meshcore/filesrw_test.go)Live
A file was pulled from a second real machine, hash exact, no partial left behindUM.75 (part)\1, \1 (go/internal/meshcore/filesrw_test.go)Live
A clipboard round trip between two real machines came back byte-identical, in both directionsUM.14, UM.75 (part)\1, \1 (go/cmd/tagii-mesh/relay_test.go)Live
The program on a running device was replaced and the device kept its identity, checked from the other machine over the wireUM.15\1 (go/internal/meshcore/install_test.go), \1 (go/internal/meshcore/install_test.go)Live
Two nodes relay the clipboard both ways, with no echo, secrets withheld and logged, and delivery after the far side returnsUM.14\1 (go/cmd/tagii-mesh/relay_test.go), \1 (go/internal/meshcore/queue_test.go), \1 (go/internal/meshcore/queue_test.go), \1 (go/internal/meshcore/queue_test.go), \1 (go/internal/meshcore/queue_test.go), \1 (go/internal/meshcore/queue_test.go), \1 (go/internal/meshcore/watch_test.go), \1 (go/internal/meshcore/watch_test.go)Built
The node binds the loopback and the private network address only, never a wildcardUM.10\1 (go/internal/meshcore/bind_test.go), \1 (go/internal/meshcore/bind_test.go)Built
A peer whose certificate changed is refused on the transfer pathUM.10\1 (go/internal/meshcore/tailnet_test.go)Built
Certificate creation under concurrent first runs produces one identity, never a mismatched pair2026-08-29 defect 3\1 (go/cmd/tagii-mesh/certrace_test.go), \1Built
An update that is wrong in any way is refused whole, with the program on disk unchangedUM.41, UM.55\1 (go/internal/meshcore/update_test.go), \1 (go/internal/meshrelease/meshrelease_test.go), \1 (go/internal/meshrelease/meshrelease_test.go)Built
Waking a device: ask first, then send the signal, directly or through another of your devicesUM.23\1 (go/cmd/tagii-mesh/wake_test.go), \1 (go/cmd/tagii-mesh/wake_test.go)Built
A screen session refuses a device outside your set and its audit row never carries contentUM.20, UM.24\1 (go/internal/meshcore/stream_test.go), \1 (go/internal/meshcore/stream_test.go), \1 (go/internal/streamhost/streamhost_test.go)Built
A view of the screen goes only to a paired device; paused means no picture leaves; a stream carries distinct framesR7\1 (go/internal/meshcore/view_test.go), \1 (go/internal/meshcore/view_test.go), \1 (go/internal/meshcore/view_test.go), \1 (go/internal/meshcore/night_test.go)Built
A file you fetch carries a hash you can check, and nothing outside what you shared is reachableR7\1 (go/internal/meshcore/files_test.go), \1 (go/internal/meshcore/files_test.go), \1 (go/internal/meshcore/files_test.go)Built
Every file in the product explains itself, and the tools that check and write the header ship in the binaryL.1, L.2, UM.62\1 (go/internal/headers/stamp_test.go), \1 (go/internal/headers/stamp_test.go), \1 (go/internal/headers/stamp_test.go), \1 (go/internal/headers/stamp_test.go), \1 (go/internal/headers/embed_test.go), \1 (go/internal/headers/diff_test.go), \1 (go/internal/meshcore/codediff_test.go), \1Built
Deploy sends the files that changed to the device and path each file's own header names, and nowhere elseL.4\1 (go/internal/meshcore/deploy_test.go), \1 (go/internal/headers/deploy_test.go), \1Built
A watched folder lands every save on another device, one way, and nothing on the far side is deletedL.3\1 (go/internal/meshcore/folderwatch_test.go), \1 (go/internal/meshcore/folderwatch_test.go), \1 (go/internal/meshcore/folderwatch_test.go), \1Built
The phone receives files only while its own switch is on, off by default, and the switch turns itself off when the socket dies underneath itUM.104\1 (go/internal/meshphone/lanreceive_test.go), \1 (go/internal/meshphone/lanreceive_test.go), \1 (go/internal/meshphone/lanreceive_test.go), \1Built
An incoming file is a question on the phone: nothing is taken until Accept, Decline takes nothing and the sender knows, silence expires into a decline, and quick save is per deviceUM.44, UM.52\1 (go/internal/meshphone/offers_test.go), \1 (go/internal/meshphone/offers_test.go), \1 (go/internal/meshphone/offers_test.go), \1 (go/internal/meshphone/offers_test.go), \1 (go/internal/meshcore/payloadbind_test.go), \1, \1Built
Progress is shown both ways and the bytes arrive; received files have a pageUM.106, UM.107\1 (go/internal/meshphone/progress_test.go), \1Built
No screen reachable from the Upload Mesh entry point is a registration surface, and the guard has its own positive controlUM.90, UM.92\1Built
The phone's identity opens only after its owner is asked, and seals when the app leaves the frontUM.93, UM.96\1, \1Built
Who is at the other end is visible wherever an action is taken, by the device's pinned fingerprintUM.97\1, \1Built
A risky act asks the approval phone first: bound to the requester, expiring, single-use, and no answer is a no; the ordinary acts never askUM.56, UM.95\1 (go/internal/meshcore/approval_test.go), \1 (go/internal/meshcore/approval_test.go), \1 (go/internal/meshcore/approval_test.go), \1 (go/internal/meshcore/approval_test.go), \1 (go/internal/meshcore/approvalstep_test.go), \1 (go/internal/meshcore/approvalstep_test.go), \1 (go/internal/meshphone/approve_test.go), \1Built
Ringing a device never touches its mixer, carries a Stop on the device itself, and has a hard time limitUM.46\1 (go/internal/meshcore/ringsafety_test.go), \1 (go/internal/meshcore/ringsafety_test.go), \1 (go/internal/meshcore/ringsafety_test.go), \1 (go/internal/meshcore/ringsafety_test.go)Built
Revoking a device reaches it on the wire and is named as a revocation; peer credentials are never cleartext at restF13, key lifecycle\1 (go/internal/meshcore/revoke_test.go), \1 (go/internal/meshcore/revoke_test.go), \1 (go/internal/meshcore/tokenseal_test.go), \1 (go/internal/meshcore/tokenseal_test.go)Built
The pinned link between devices negotiates a hybrid post-quantum key exchange by default, and the guard can tell classical from hybridUM.38\1 (go/internal/meshcore/tlspq_test.go), \1 (go/internal/meshcore/tlspq_test.go), \1 (go/internal/meshcore/tlspq_test.go)Built
Settings are served on an owner-only socket; every switch has a page; the diagnostics bundle carries no secretsUM.70, L.6\1 (go/internal/meshcore/settingsapi_test.go), \1 (go/internal/meshcore/settingsapi_test.go), \1 (go/internal/meshcore/settingsapi_test.go), \1, \1Built
A phone's own notifications cross to a paired desktop only for the apps a person turned on, one by one, off for every app by defaultUM.57\1, \1, \1Built
Remote input into a desktop is off unless turned on, refuses while the session is locked, expires on its own, and admits only the peers on its listUM.19\1 (go/internal/meshcore/input_test.go), \1 (go/internal/meshcore/input_test.go), \1 (go/internal/meshcore/input_test.go), \1 (go/internal/meshcore/input_test.go), \1 (go/internal/meshcore/input_test.go)Built
A stock KDE Connect device on the same network becomes visible and never trusted; the listener is off by defaultUM.14b\1 (go/internal/meshcore/kdeconnect_discovery_test.go), \1 (go/internal/meshcore/kdeconnect_discovery_test.go), \1 (go/internal/meshcore/kdeconnect_discovery_test.go)Built
A one-shot door serves exactly one file over exactly one connection and is then gone; no command or screen opens it yetUM.53, UM.54\1 (go/internal/meshguest/door_test.go), \1 (go/internal/meshguest/door_test.go), \1 (go/internal/meshguest/door_test.go), \1 (go/internal/meshguest/door_test.go)Built
A device can join by a code that never crosses the wire; a wrong code is refused at the first stepD13 (not on any screen)\1 (go/internal/meshpake/pake_test.go), \1 (go/internal/meshpake/pake_test.go), \1 (go/internal/meshphone/pake_join_seam_test.go), \1 (go/internal/meshphone/pake_join_seam_test.go)Built
A folder can be kept in sync by a bundled Syncthing driven over its own interface, and the driver never touches a folder a person configured by handUM.48, D5\1 (go/internal/syncfolder/syncthing_test.go), \1 (go/internal/syncfolder/syncthing_test.go), \1 (go/internal/syncfolder/syncthing_test.go), \1Built
A generated project passes the header audit and one with an unfinished field fails itUM.21\1 (go/cmd/tagii-devkit/devkit_test.go), \1 (go/cmd/tagii-devkit/devkit_test.go), \1 (go/cmd/tagii-devkit/devkit_test.go)Built
A signed Android early-access package and its published SHA-256 digest are present in the release directoryUploadMesh Android early access\1, \1Built
A 64-bit Linux package contains the product, its licences, its parts list and a checked installer that opens pairingUploadMesh Linux early access\1, \1, \1Built
The current build between two real machines: view, alert, revoke, and a re-run of files and clipboardUM.75\1Not yet
A person using the app on a phoneUM.11, UM.12, UM.104\1Not yet
Two real phones, phone to phoneUM.105\1Not yet
The install door (a phone scanning a code, downloading and installing the app)UM.22\1Not yet
Across the internet, away from one networkUM.09\1Not yet
Our own relay applied and in the pathUM.08\1Not yet
An actual video stream on a viewerUM.20\1Not yet
A real machine wokenUM.23\1Not yet
A release reproduced byte for byte on two independent machines, with a transparency log checked before updatingUM.73, UG14\1, \1, \1Not yet
Everything returns after a restartUM.16\1Not yet
Apple platformsworkbook section 7 item 1\1Not yet
Windows beyond a cross compileUM.15\1 (go/internal/meshcore/platform_test.go)Not yet
Clipboard from a phone, in the backgroundworkbook section 7 item 5, D9\1Not yet
Public repository, on a self hosted forgeworkbook section 5\1Not yet
Licence: AGPL-3.0-or-laterD1\1Decided, not a proof state