Releases: cubepy/CubePilot
Release list
v0.3.0
Windows is back, and the home-screen widget is gone.
Added
A Windows build again, published beside the Android ones for the first
time since v0.1.16. Unzip CubePilot_v0.3.0_windows_x64.zip anywhere and run
cubepilot.exe — everything the app needs sits in the folder beside it, and
nothing is installed.
It is not code-signed, so the first time you run it Windows SmartScreen will
put up a blue box saying it does not recognise the publisher. More info →
Run anyway. That will keep happening until the build earns enough downloads
for SmartScreen to stop asking, or until there is a signing certificate to
sign it with. The SHA-256 below is there so you can check the file is the one
that was built rather than taking the warning's word for it.
Removed
The home-screen widget, which shipped in v0.2.1. It showed a list of
servers — a list you already have one tap away, in an app that is already on
the same home screen — and getting it to show that list correctly took three
rounds of fixes and was still wrong at the end of them. Once it opened the
app on an empty server list convincingly enough that the data looked deleted.
It was not; it never was. But a feature has to be worth what it costs to
keep, and this one was not close.
It was also the only thing CubePilot ever wrote outside the encrypted vault.
There is nothing outside it again.
The Quick Settings tile and the floating terminal stay. Those two do
something the app cannot do from inside itself.
Fixed
Launching from a shortcut could skip the app lock. The stored settings —
theme, language, and the lock — were applied by the splash screen, and only
while the splash was still on screen. A link arriving from the tile could
route away before that happened, and the lock went with everything else. A
link now waits until the splash has finished, which is the same moment the
app knows whether it is locked.
The app called itself cubepilot. Lowercase, under its own icon, in the
app switcher, and in every list Android sorts by name — since v0.1.0. The
project's package name was being used as the app's label.
A server could be reported as deleted while the vault was still opening.
An unread vault has no servers in it, and "no servers in it" and "that one is
gone" are the same sentence to code that cannot tell the difference.
Which file do I want?
Android: almost certainly arm64-v8a — every phone sold in the last
several years. armeabi-v7a is for older 32-bit devices, x86_64 for
emulators.
Windows: windows_x64, unless you are on something unusual.
Upgrading
Installs straight over v0.2.1. Same signing key, so your servers, keys and
timeline are untouched. If you added the home-screen widget, it will
disappear when you update — there is nothing behind it any more.
Checksums
fc2ebf61476f93c2549abc6a85bb099f4ade989393450dbb9946845665c623db CubePilot_v0.3.0_arm64_v8a.apk
4db48573fa141b4084cd21a9616abca0dbc81785527647370e782d21d23224c1 CubePilot_v0.3.0_armeabi_v7a.apk
ae1005c84f0159d04160ed1a99877cb3932763997d6684c56c142939804af193 CubePilot_v0.3.0_windows_x64.zip
8f21b625108a0041a4d2689af0ff8625c985001c38f067ecd8d614f1e7b37167 CubePilot_v0.3.0_x86_64.apk
v0.2.1
Your servers on the home screen, a terminal that floats over other apps, and the version the app shows is now the version it is.
Added
A home-screen widget. The servers you actually use, one tap from the launcher: pinned first, then favourites, then whatever you connected to most recently. Resize it and it holds more.
It knows less than you might expect, on purpose. A widget is drawn by the launcher, in the launcher's process, long before CubePilot is running — so it cannot read the vault, whose key only this app can use. What it reads instead holds a name and an internal id and nothing else: no host, no username, no port, no key, no password. Nothing in it can reach a machine. And with the app lock on, the widget lists nothing at all: a home screen is readable by whoever is holding the phone, which is the situation the lock is there for.
A Quick Settings tile. CubePilot two swipes from anywhere, landing on the dashboard with the quick-connect field already focused. The tile deliberately connects to nothing on its own — it can be tapped over a lock screen, and a tile that opened a session to the last server you used would be a one-tap root shell on a phone lying on a table.
A floating terminal. Shrink a session into a small window that stays on top while you use other apps — the button is in the tab bar, beside the one that opens a new session. It shows the output and takes no input: a floating window has no keyboard, and a stray tap that reached a root shell would be a bad way to find that out. Tap it to come back full size.
Connect outside the VPN — for real this time. The setting has existed since v0.2 and has been answering "not supported" on every device, because the Android side of it was never written. It is written now: connections for a server with that switch on go out over the network underneath the VPN rather than through it.
The version, faintly, beside the Dashboard title. "Which build are you on?" is the first question of every bug report, and the answer was four taps deep in Settings. It sits next to the heading now, dim enough to disappear until it is the thing you need.
Fixed
Shell prompts are coloured. All of them. v0.2 coloured the first prompt of a session and no others, and the reason is that a prompt is only ever alone once. As soon as you type something and press Enter, the shell echoes what you typed onto the same line and ends it — so from the second prompt on, the prompt arrives inside a complete line, behind the control bash sends between commands, with your command after it. Whole lines were being refused for carrying an escape, and the prompt pattern was anchored to the end of the line, which by then is your command rather than the $.
Both rules now match what a shell actually sends, captured from bash on a pty rather than guessed at. Your command keeps its own colours after the prompt, and Note: see foo#bar is still not a prompt.
Every filled button was under the contrast standard. White text on the brand blue came to 4.42:1 and on the brand purple to 4.23:1, against WCAG AA's minimum of 4.5:1 — the line below which text stops being readable for a good number of people rather than merely looking soft. Both colours are one step deeper now. Side by side you will not see the difference; that was the point of measuring instead of looking.
Tapping a row in Settings looked like nothing happened. The ripple was being painted behind the card it was supposed to appear on, so every tappable row in a card responded invisibly.
About said 0.2.0 in every build since 0.1.0. The line under About was written out by hand, so the one screen whose entire job is to answer "which build is this?" answered it wrongly for eighteen releases, and called itself a pre-release regardless of how the release was actually published. Both now come from one constant, and a test reads pubspec.yaml and fails if the two ever disagree.
Under the surface
Every screen in the app is now run through Flutter's own accessibility guidelines on every build — tap-target size, labelled controls, text contrast in both themes, right-to-left layout, and the largest system font. The two fixes above are what the first run found, and both had been shipping for months.
Which file do I want?
Almost certainly arm64-v8a — every Android phone sold in the last several years. armeabi-v7a is for older 32-bit devices, x86_64 for emulators.
Upgrading
Installs straight over v0.2. Same signing key, so your servers, keys and timeline are untouched.
The widget and the tile have to be added by you, once: long-press the home screen → Widgets → CubePilot, and pull down the Quick Settings panel → edit → drag the CubePilot tile in.
Checksums
52133ca9be344461503ceadda4e1d55c15966570c8cb831b4f9dfc16f131b0d3 CubePilot_v0.2.1_arm64_v8a.apk
f08143785ce43e739e5dcc5914e29a19700a9f4b4ff1a0aec9aa0c7365dc0bf2 CubePilot_v0.2.1_armeabi_v7a.apk
a7f8323a3c2576f918ad67b87b94ab895710f406b19fbfca8e5b14ae5071b702 CubePilot_v0.2.1_x86_64.apk
v0.2
Two things found by using v0.1.17 rather than by reading it.
Fixed
The last server in the list was behind the Add button. A floating button is painted over the body rather than beside it, so with few enough servers nothing overlapped and there was nothing to see; with enough of them to fill the screen, the last row could not be tapped at all. Servers, tunnels, keys, commands and the timeline all leave room for the button now.
Shell prompts are coloured — this time actually. The colouring shipped in v0.1.17 and never once fired on a real server. A shell does not send a prompt on its own: bash turns on bracketed-paste mode first (ESC[?2004h, the default since 4.4, which is every Debian and Ubuntu in service), and the stock PS1 sets the window title as well. The prompt therefore always arrives with control sequences glued to its front — and anything carrying an escape was left alone, on the assumption that it was output a program had already coloured. The control keeps its place now, and the text after it is what gets painted.
Cyan rather than the violet v0.1.17 intended, because it is a landmark for finding where one command ended and the next began, not branding.
Also
A property test now feeds a real bash transcript through the highlighter at every chunk size from one byte to forty, and requires the visible characters to come back identical. Output arrives split wherever the network felt like splitting it, and that is the part worth proving rather than assuming.
Upgrading
Installs straight over v0.1.17. Same signing key, so your servers, keys and timeline are untouched.
نسخهٔ ویندوز بهزودی به همین ریلیز اضافه میشود.
v0.1.17
No new features. Eighteen rounds of one question asked over and over: does this screen tell the truth, and does this button do what it says?
Some of what it found could lose your data.
Fixed — things that could lose something
Two saves at once could destroy the whole vault. Every save wrote the store to one temporary file and renamed it into place, with nothing keeping two of them apart. The timeline records an entry — without waiting for it — on every command, connection and file transfer, so a setting change landing in the same moment was an ordinary second in this app. When they overlapped, what ended up as the vault was half a JSON document, which the next launch could only read as corrupt: set aside, start empty. Every server, key and tunnel, gone. Saves now run one at a time.
Restoring a backup asked nothing and replaced everything. Pick a file, type the passphrase, done — every server, key, tunnel, saved command, group and the entire timeline gone, with no confirmation anywhere. Private keys that exist only on your device are the one thing in this app that cannot be recovered; deleting a single one asks, and deleting all of them did not. It now decrypts first and then asks, showing what is on the device, what is in the file, and that keys do not come back.
When the vault could not be read, the app said nothing. It set the file aside and opened empty, which from your side is indistinguishable from CubePilot having thrown your servers away. The first screen now says what happened, where the old file is, and that a backup will bring it back. If the store cannot be opened at all, it says that too — everything works and nothing is being saved is not something you should have to discover.
A downgrade would have eaten whatever a newer version added. The vault records its format version and never read it back. Installing an older build over a newer one would have dropped everything the newer one knew about, on its first save. Unknown fields are now carried through untouched.
Fixed — things that ran on the wrong machine
Docker and Kubernetes actions could hit a different server than the one on screen. Both pages follow the active session, and both re-resolved that session at the moment the action ran rather than using the one the list came from. Containers have random ids, so those mostly missed. Volumes and networks are addressed by name, and db_data exists on staging and on production. Actions are pinned to the list's session now — and both pages finally say which machine they are showing, which nothing on either screen did before.
The command library never said where a command would run. It fires at whichever session is active — systemctl restart nginx, docker compose down — and said only "Ran X" afterwards. It now names the machine before you tap anything, and again in the confirmation.
A log follow survived switching servers. tail -F kept running against the old server and printed into a page that by then belonged to a different one. The log export named the wrong server in its header for the same reason.
Fixed — SFTP
After a reconnect, the file browser was talking to a socket that no longer existed — every listing, upload and delete, until the app was restarted.
Rename, delete, mkdir and chmod returned quietly when the session was gone. No error, no refresh: renaming a file on a closed session looked exactly like renaming it successfully.
Two panes showing the same session were the same pane — walking into a directory on the left dragged the right one with it.
Downloads and uploads held the whole file in memory, so a database dump was simply unfetchable on a phone; downloads also asked where to save after transferring, throwing away a completed transfer if you cancelled. Both stream now, and an upload that would replace a file asks first.
Fixed — security
The app lock counted nothing. A four-digit PIN is ten thousand guesses and there was no cost to any of them. Five wrong now starts a wait that doubles, and the lock screen counts it down. A vault that says the lock is on with no PIN stored no longer locks you out of your own data for ever.
The CSV timeline export was executable. Spreadsheets read a field starting with = as a formula, and those fields hold whatever a server printed. Defanged.
A changed host key showed only the new fingerprint — the one thing you cannot check it against was the old one, which was not on screen. Fingerprints are also printed the way ssh prints them now, without the base64 padding that made them differ in the last character from what you were comparing against.
Closing a session takes its tunnels with it, and now says so before it happens, naming the forwards that will stop.
Better
Selecting text works where the text is. A bar appears over the terminal the moment something is selected — copy, or dismiss. Right-click copies a selection or pastes when there is none. The select-text sheet opens at the newest output instead of the top.
Shell prompts are coloured. They were the one line the highlighter could never touch, because a prompt arrives without a newline. It is also the only landmark there is when scrolling back through a long session.
Imported keys know what they are. Import used to store the PEM and nothing else: no fingerprint to tell two keys apart, and no public key to paste into a server's authorized_keys — which is most of why anyone imports a key. Both are derived from the key itself now.
A jump-host loop is caught when you save the server, not weeks later on a screen nowhere near the mistake.
Deleting a note or a bookmarked entry asks. Everything else in the timeline still goes with one tap.
Connect outside the VPN is a new per-server switch on Android, for a server the VPN cannot reach. The Android side of it is not in this build — and a session set to use it says so in its own terminal when it opens, rather than pretending.
Upgrading
Installs straight over v0.1.16. Same signing key, so your servers, keys and timeline are untouched.
v0.1.16
Things that were quietly wrong. No new features — four fixes, all of the same kind.
Fixed
Deleting a server asks first, and says what goes with it. It used to be one tap on a menu, with no confirmation, taking the saved password or key selection with it. The question now names what is attached to that server: how many sessions will close, how many saved tunnels go too, how many other servers connect through it.
Deleting a server no longer leaves its session running. The tab stayed in the terminal, the notification stayed up, and its forwards kept going — all pointing at a server that was no longer in the list. Its saved tunnels also stayed in the vault forever, unstartable, because their server was gone.
Deleting a private key asks first. This was one tap with no confirmation at all — less friction than removing a Docker container, for the one thing in the app that genuinely cannot be recovered. It now also says how many servers authenticate with that key and will stop connecting.
Tunnels stop when the session carrying them ends. A tunnel travels through one SSH session; when that session closed, the forward was dead but stayed listed as running — and its local port stayed bound, which is why starting a replacement on the same port failed with "address already in use". Closing a session, or giving up on reconnecting, now stops what it carried and says so in the timeline.
The dashboard's tunnel counter was the literal string "0". It had said zero with tunnels running since the first release. All three counters now go to the screen they are counting, too.
What deleting a server deliberately leaves alone
- The timeline. The history of a server you deleted is still history.
- The known host key. It is the same machine tomorrow, with the same key.
- The jump-host setting of servers that route through it. Clearing it would quietly turn a routed connection into a direct one. Those connections fail loudly instead — which is the entire point of having set a jump host — and the delete dialog warns you before it happens.
Upgrading
Installs straight over v0.1.15. Same signing key, so your servers, keys and timeline are untouched.
Checksums
b6dbe26ac8a1d1ad5d2ecfa91ed330670be45a7d10c5613bc3f98e8cf5c087ea CubePilot-v0.1.16-android.apk
1a1d16fbfcc19d71364ee07d717d981dddc749cc2501f0129d4c934388ee4125 CubePilot-v0.1.16-windows-x64.zip
v0.1.15
Jump hosts. Reach a server through a bastion.
Added
Connect through another server. The ordinary shape of a production network: the database is not reachable from your phone, the jump box is, and the jump box can reach the database. Edit a server, set Connect through to another saved server, and that is the whole configuration.
CubePilot dials the bastion, opens a forwarded channel through it, and runs the real connection down that channel. The target's traffic is encrypted end to end — the bastion carries bytes it cannot read, exactly as ssh -J does.
Chains work, up to four deep. Beyond that is almost certainly a mistake in the chain rather than a network, and each hop is a full handshake: five of them over a mobile connection is a minute of waiting before anything appears.
Details
A jump host is a server you are trusting as much as the target, so it is treated like one: its own saved credentials, its own key, and the same host-key checking. Nothing about a bastion is waved through.
Two things it deliberately refuses to do:
- A deleted jump host is an error, not something to skip. Connecting straight to the target instead would send your traffic somewhere you never agreed to send it.
- Servers pointing at each other in a loop — including one set to connect through itself — are reported as a loop rather than being walked until something runs out.
On a reconnect the whole chain is dialled again, because the hops died with the connection that ran through them. None of that prompts: a reconnect happens while you are not watching, and a host key that changed in the meantime is exactly what a machine-in-the-middle looks like. A hop that fails halfway closes the ones already open behind it.
Upgrading
Installs straight over v0.1.14. Same signing key, so your servers, keys and timeline are untouched. Servers saved before this release simply have no jump host, which is what they had before.
Elsewhere
The documentation in the public repository has been brought in line with what actually ships — the installation page in particular described an installer, an MSIX and per-device APKs that have never existed.
Checksums
b871354327b04cb561b9ac3c080ba2aebe096ef77b6d86646b41f0ab43a1bba9 CubePilot-v0.1.15-android.apk
f7bbe321fe9abc790ec0139b9650849cc331003bedf018be80dc6f1cf6041ff2 CubePilot-v0.1.15-windows-x64.zip
v0.1.14
Material You — accent colours from your wallpaper, if you want them.
Added
Colours from your wallpaper. Android 12 and later builds a palette from whatever your wallpaper happens to be. Turn this on in Settings → Appearance and CubePilot takes its accent colours from it: buttons, the selected navigation item, focus rings, the highlights that are blue today.
Details
Only the accents change. The surfaces — the backgrounds, the cards — stay as they were, deliberately: a dark theme that turns grey with somebody's wallpaper is not a dark theme any more, and OLED has to stay actually black or there is no point to it.
Error red stays CubePilot's too. Red means broken in every theme, and a wallpaper does not get a vote.
It is off by default. The palette is part of how the app looks, and replacing it should be something you chose rather than something that happened on first launch.
The switch sits under the theme list rather than inside it, because it applies to whichever of the four themes you are using — it is not a fifth one.
On Android 11 and earlier, and on Windows, the system offers no palette; the switch is there but changes nothing, and everything stays on CubePilot's own colours.
Also
CI now builds the Android and Windows binaries only when a release needs them, rather than on every commit. Nothing in the app changed — but it is why there will be fewer, better-considered releases from here rather than one per change.
Upgrading
Installs straight over v0.1.13. Same signing key, so your servers, keys and timeline are untouched.
Checksums
6e95991661f159e22d02afb1783579a42f0525d208a5a5ff5e396c3255bad097 CubePilot-v0.1.14-android.apk
3213028b4f0eed8b1b1a8f98f67ba45e1ea961bafc912968a0ffc76c956efadd CubePilot-v0.1.14-windows-x64.zip
v0.1.13
An accessibility pass over the screens that needed it most.
Fixed
The key bar above the keyboard now announces its keys. An icon-only key — the arrows, Home, End, PgUp, PgDn — announced nothing at all to a screen reader. Each now carries its tooltip as its label, or the glyph printed on it where there is no tooltip.
A monitoring reading is one announcement. "CPU, 34%", rather than a label, a number and a progress bar read out as three unrelated things.
A server in the list is one item. Not a name, an address, three tags and a pin icon announced separately.
The terminal's tab strip no longer clips at large text sizes. It was a fixed 46dp tall, which cut the labels off for anyone running large system text. It now grows with the text, up to a limit.
Details
The sparklines under the monitoring readings are deliberately hidden from screen readers. A chart is a picture of the number directly above it; announcing it as well would say the same thing twice, worse the second time.
This is a pass, not a finish. It covers the places where something was outright missing or broken. A full audit — every screen, every control, with a screen reader actually running — is still ahead.
Upgrading
Installs straight over v0.1.12. Same signing key, so your servers, keys and timeline are untouched.
Checksums
443a4774b316abdfa1b49246d9ec6e6329f1a8ff74b08096797ec7e5673e73ae CubePilot-v0.1.13-android.apk
cab28d8211e6c0db418c49faccbe15a02fc989c7c539d04e946a6284cab8e461 CubePilot-v0.1.13-windows-x64.zip
v0.1.12
A performance pass. Nothing new to look at — it should simply move better.
Changed
One blur per screen instead of one per card. The Glass theme put a backdrop filter on every card, and each one makes the engine save and blur everything behind it. A list of twenty servers blurred the screen twenty times per frame, which is exactly why a scroll that should have been effortless was not.
They are now grouped filters sharing a single blur, declared once at the root of the app — above the navigator, so routes, sheets and dialogs are all inside it. The blur is computed once and reused, and the cost stops growing with the number of cards on screen. Nothing looks different; it just stops charging for the same work over and over.
The terminal can now draw at 120 Hz. Output arriving from the server was held for one frame at 60 Hz before being written, which quietly capped how often anything could reach the screen — a 120 Hz display spent half its frames with nothing new to draw. The hold is now one frame at 120. At 60 Hz it costs nothing, because Flutter coalesces whatever arrives into the next frame either way.
Charts and terminals paint on their own layers. A new monitoring sample repaints the chart rather than the card around it, and a byte arriving from a server repaints the terminal rather than the page it sits on.
Honestly
No frame rate was measured to produce these notes. What changed is the amount of work done per frame, not a number on a graph. If you want to see it, scroll the server list with the Glass theme on: that is where the difference should be plainest.
Upgrading
Installs straight over v0.1.11. Same signing key, so your servers, keys and timeline are untouched.
Checksums
699fc5f9c31a0481d316bd5127fc465be94e70584387eb41a51f1aad5a89d9c3 CubePilot-v0.1.12-android.apk
ce6e6348c275b4b9f223c55e07704057f5aa35e43b508764e984c4210577f965 CubePilot-v0.1.12-windows-x64.zip
v0.1.11
Smart groups, and connecting a whole group in one tap.
Added
Smart groups. A folder is somewhere a server is. A group is a question you keep asking: everything tagged prod, everything you touched this week, everything on port 2222, everything whose name does not contain test. A server belongs to every group that matches it, and nothing has to be filed anywhere for that to work.
Rules are a field, an operator and a value — name, host, tag, folder, user, port, favourite, pinned, or last used within a number of days; contains, does not contain, is, or starts with. Match all of them or any of them.
The editor shows a live count of what the rules match as you type them. A rule whose effect you cannot see is one you have to save and undo to understand.
Favourites and Recent are there from the start, without being set up. They are built rather than stored, so they cannot be half-deleted and their names follow the app's language rather than whatever it was when your vault was first written.
Connect all. With a group selected, one button opens every server listed under it, one after another.
Details
Connecting runs sequentially rather than all at once. Ten simultaneous handshakes over a phone's connection is how you get ten timeouts. One failure does not stop the rest, and the message at the end says how many of how many actually opened.
It connects what is on screen, not what the group would match. The search box is right there, and a button that ignored it would open servers you cannot see.
Two rules of the matching engine worth knowing:
- A group with no rules matches nothing. Matching everything would turn a half-finished group into a second copy of the whole server list, which looks like a feature and hides the mistake.
- A server you have never connected to does not match "last used". There is no date to compare, which is different from a date long ago.
Groups live in the same encrypted vault as everything else, and a vault written by an earlier version opens unchanged.
Upgrading
Installs straight over v0.1.10. Same signing key, so your servers, keys and timeline are untouched.
Checksums
39c29d8742a4b13ac6de52404129f3380b3a91d4768c495b3a8bb50eecd1f21f CubePilot-v0.1.11-android.apk
464aff5d547c7334e682acc734884ecbd02ef45393b88b227c110c8fd859f55a CubePilot-v0.1.11-windows-x64.zip