Repository navigation
Releases: calabinet/calabi
Release list
calabi 2.3.0
TLS endpoints, on your own node. Nothing to configure: no config file
changed. Update the node before you create a TLS endpoint on it — an older node
can't serve one. Nodes run by Calabi don't take TLS endpoints.
Endpoints
- Added — TLS endpoints. A self-hosted node — one connected to
calabi.net, or one you run with your own coordinator — now also serves TLS
endpoints on its HTTPS port. For each connection it reads the server name
from the start of the TLS handshake, before answering: the name of a TLS
endpoint goes to your service as it is, and the node never decrypts it nor
holds a certificate for it; any other name is served as HTTPS, exactly as
before. A node with no TLS endpoint on it doesn't read the handshake at all.
Create one in the console, or withcalabi sni <local-port> --domain <name>;
the documentation has a page on TLS endpoints, including how your service
gets its certificate (TLS-ALPN-01 works through the endpoint). The separate
sni_portlistener stays, off by default. - Changed — A name is an HTTPS endpoint or a TLS endpoint on a node, not
both. Port 443 serves both kinds, so a second endpoint with the same name
is refused, whichever kind came first. - Fixed — A TLS endpoint on a node that could not serve it showed as
online. Registering ansniendpoint on a node with no way in for it —
before this release, every node withoutsni_port— succeeded, and the
client showed it online while nothing could reach it. The node now refuses
it and says where TLS endpoints are served. - Fixed — The command line called a domain problem an access-protection
rule.calabi http 8080 --domain <name>with a domain that was not
registered or not verified printed "this organization requires every tunnel
to have access protection" in front of the real reason. It now prints the
reason alone, and an organization's protection rule prints its own sentence
once. - Fixed — An IP rule could close a TLS connection without an ordinary
node log explaining why. Refusals now appear at INFO level with the
endpoint's domain and ID, the source IP andoutcome=denied_ip, including
on self-hosted nodes that do not report access records to calabi.net. The
rule is still enforced before a connection reaches the client. TLS
passthrough cannot return an HTTP error page without terminating TLS.
Local console
- Added — TLS in the new-endpoint wizard. It can be chosen when this
device is connected through your own node, the node is up to date, and it
serves a verified domain; otherwise the option says which of these is
missing. Connected to your own server, it can be chosen when the node's
HTTPS port is on. Lists and details call the type TLS. - Changed — Access control offers only what an endpoint's type can use.
TCP, UDP and TLS endpoints pass bytes through, not HTTP requests, so the
node never applied Basic auth, OAuth sign-in or header rewrite on them; the
access-control panel no longer offers these there. An endpoint that already
has them shows which, and saving keeps them as they are. - Changed — Settings your plan no longer includes are listed. When a
plan that included a rate limit, header rewrite or OAuth sign-in ends, an
endpoint that has one keeps it, and the node keeps applying it. The
access-control panel used to hide it; it now lists it, read-only, with a
button to remove it. Errors from saving are shown in the dashboard's language. - Fixed — Connected to your own server, saving access control removed the
OAuth client secret. On an endpoint with its own OAuth sign-in, saving any
access-control change — an IP rule, for example — dropped the client secret,
and the sign-in stopped working. The endpoint list also returned the secret
in plain text. The secret is now kept unless you enter a new one, and lists
say only that one is set. A configuration export still includes it, so an
import restores a working sign-in.
On calabi.net
- Added — TLS endpoints in the web console, on any plan. They need a
self-hosted node that is up to date and a verified domain it serves; the
console says which is missing, and the server refuses the same cases. - Changed — TCP, UDP and TLS endpoints take only IP restrictions and rate
limiting. The node never applied Basic auth, OAuth sign-in or header
rewrite on them. The security panel no longer offers these on such
endpoints, and the server refuses new ones from any client. Ones already
stored stay as they are and can be removed. - Changed — Organization login policies need a plan with OAuth sign-in.
Saving a login policy, or pointing an endpoint at one, now needs the same
plan as setting up sign-in on the endpoint itself; before, neither was
checked. Policies, and endpoints that already point at one, keep working and
can still be listed and removed. - Fixed — An endpoint could hold a domain that was not its
organization's. Endpoints created from the command line assni, endpoints
claimed under a different domain, and TCP or UDP endpoints given a domain
skipped the check that the domain is the organization's own and verified.
As a domain belongs to one endpoint at a time, such an endpoint kept the
domain's owner from using it. Every way a domain reaches an endpoint now
checks it. - Fixed — After a plan ended, some endpoints could not be saved. An
endpoint with a rate limit, header rewrite or OAuth sign-in that the new plan
doesn't include keeps it, and the node keeps applying it — but every save of
that endpoint, even of an IP rule, was refused for a setting the console no
longer showed. Saving now keeps such settings unchanged, the security panel
lists them and can remove them, and changing them still needs a plan that
includes them. Errors from saving are shown in the console's language.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v2.3.0 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at e23a56fc2d502ab417552819f418f4c48378cfb0 — the commit these binaries were
built from — rebuilds all 21 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop), SHA256SUMS.edge and SHA256SUMS.coord
answer a different question: whether your download arrived intact. Archives
themselves are not reproducible (tar/zip record mtimes) — the manifest compares
the binary inside.
Not covered by the manifest
The installers. The two desktop ones are Tauri/Rust builds with their own
toolchains and are not reproducible yet; every installer is listed in
SHA256SUMS, so its integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
calabi-android.apk — built from apps/client-android and apps/client/mobile
in this repository, but not reproducibly yet: the Go core records the absolute
directory it was built in, so the same commit built elsewhere differs. It is
signed with the release key; check the signer before installing:
apksigner verify --print-certs calabi-android.apk
# certificate SHA-256 digest: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23Components
calabi— the client (endpoints + mesh + the local console on :7400)calabi-edge— the data plane:role: tunnelfor endpoints,role: mesh
for the mesh relay,role: bothfor the twocalabi-coord— the coordinator, every device's identity: invites, device
registry, addresses, ACLs; names the edge and signs the grants it accepts
calabi-coord and calabi-edge together are your server
(deploy/server).
Docker images (amd64 + arm64): calabinet/calabi, calabinet/calabi-edge,
calabinet/calabi-coord. The binary inside each is the released linux one,
byte for byte.
calabi 2.2.1
Nothing to configure, and no order to update in. No config file changed,
and neither did anything the client, the edge and the coordinator say to each
other. The changes are in the client, the Android app and the coordinator; the
edge is the 2.2.0 code under the new version number.
Local console
- Changed — Tunnels are called endpoints. The local console says
endpoint wherever it said tunnel — the menu, the list, the wizard, the
messages — in all ten languages, as the web console and the documentation
already do. Where it said visitor it now says caller, source address or
request, whichever was meant. The pages moved with the name:#/tunnelsis
#/endpointsand#/tunnels/newis#/endpoints/new; the old addresses,
with whatever follows them, still lead to the new ones, so a bookmark keeps
working. The command line and what it prints, the configuration keys and
the daemon's own API keep their names: nothing that reads or scripts the
client needs editing. - Changed — An endpoint's address is labelled External. The tag in
front of an address in the endpoint list, and the address row at the end of
the wizard, said Public. They say External now, the name the web console
uses. - Fixed — The Overview page was taller than the window. Once its
figures had loaded, the page came out 68–90 px taller than the space it had:
a scrollbar appeared and the live throughput chart sat below the edge of the
window — every time in the desktop app, whose window has a fixed size. The
charts now take the space they are given, and the page fits. - Fixed — Connected to your own server, the Overview page had three
more faults.- The relay share of today's and this month's traffic always read 0 B, and
the 7-day chart showed no relay bars, however much the device relayed.
Both now come from the device's own mesh metering, as the endpoint share
already did. - The 7-day chart ran backwards, with today on the left, and on a machine
west of Greenwich every date under it was a day early. - With a coordinator connected, the page did not fit in the desktop app. The
card with the coordinator's mesh usage now takes the place of the 7-day
chart in the bottom row, and draws its own last 7 days as a full chart.
- The relay share of today's and this month's traffic always read 0 B, and
- Fixed — The German sign-in page. The text of the button for
connecting to a self-hosted server ran past the edges of the button. It is
shorter now.
Mesh
- Fixed — On Windows, a relay close by could stay marked as refusing
the device. A relay that refused a device and later admitted it is struck
off the refused list once it answers a keepalive. Windows clocks can move in
steps as coarse as 15.6 ms, and a relay on the same machine or the same LAN
answers within one step, so the round trip measured exactly 0 — which the
client read as no answer yet. The refusal stayed on the list: if the link
dropped afterwards, redialling the home relay could wait out the rest of a
backoff of up to five minutes, and the relay's round-trip time came and went
in the status. An answer now counts however short the round trip.
Mobile
- Changed — The Android app says endpoints too. The tab, the list, its
empty and scope notes and the usage figures say Endpoints where they said
Tunnels, in English and in Chinese.
Your server
- Fixed — On a coordinator that stores to SQLite, a device's report of
its endpoints could fail. The coordinator replaces a device's endpoint list
in a transaction that reads first and writes second. If another write was
under way when it came to write, SQLite refused at once instead of waiting
— the busy timeout does not cover that step — and the report failed until
the client sent it again a minute later. Transactions now take the write
lock when they begin, and wait for it under the busy timeout. Nothing to
migrate: the change is in how the coordinator opens its database file, and
it takes effect when the coordinator restarts. A coordinator on PostgreSQL
was not affected.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v2.2.1 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at 7789ed0e2df402597e47adc4e5428d140ebced19 — the commit these binaries were
built from — rebuilds all 21 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop), SHA256SUMS.edge and SHA256SUMS.coord
answer a different question: whether your download arrived intact. Archives
themselves are not reproducible (tar/zip record mtimes) — the manifest compares
the binary inside.
Not covered by the manifest
The installers. The two desktop ones are Tauri/Rust builds with their own
toolchains and are not reproducible yet; every installer is listed in
SHA256SUMS, so its integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
calabi-android.apk — built from apps/client-android and apps/client/mobile
in this repository, but not reproducibly yet: the Go core records the absolute
directory it was built in, so the same commit built elsewhere differs. It is
signed with the release key; check the signer before installing:
apksigner verify --print-certs calabi-android.apk
# certificate SHA-256 digest: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23Components
calabi— the client (endpoints + mesh + the local console on :7400)calabi-edge— the data plane:role: tunnelfor endpoints,role: mesh
for the mesh relay,role: bothfor the twocalabi-coord— the coordinator, every device's identity: invites, device
registry, addresses, ACLs; names the edge and signs the grants it accepts
calabi-coord and calabi-edge together are your server
(deploy/server).
Docker images (amd64 + arm64): calabinet/calabi, calabinet/calabi-edge,
calabinet/calabi-coord. The binary inside each is the released linux one,
byte for byte.
calabi 2.2.0
Nothing to configure, and no order to update in. No config file changed,
and neither did anything the client, the edge and the coordinator say to each
other. The changes are in the client and the coordinator; the edge is the 2.1.0
code under the new version number.
Mesh
- Fixed — A device kept sending its internet traffic to an exit device
that was no longer one. When the exit device a device had chosen stopped
offering to be one, or lost its approval, the device that chose it kept its
internet traffic routed into the mesh with no peer left to take it. It had no
internet access until someone deselected the exit device on it. The client
now routes directly for as long as the chosen device offers no IPv4 default
route, logsthe chosen exit device is not offering the IPv4 default route
once, and goes back to using it when the offer returns — no restart, and
nothing to choose again. A device that offers only::/0counts as not
offering, because sending everything through an exit device covers IPv4
only. Update the devices that use an exit device; one still on 2.1.0 or
earlier behaves as before. - Fixed — A device's home relay moved back and forth between two
relays. A device measures each relay with a small UDP round trip and makes
the fastest one its home, which is where other devices send to reach it when
relayed. The figure was a single round trip, and one lost packet adds a
300 ms retry to it — enough to make the home look slower than a relay that
is really further away. The device moved, then moved back at the next
measurement five minutes later; each move changes its entry in every peer's
map and reconnects it to a relay. A relay's figure is now the best of three
round trips, and a home that stays silent for a whole round is asked once
more a few seconds later before the device leaves it.
Local console
- Added — The page where a device publishes subnets says whether each
one is in effect. Under Mesh → Routing, a subnet you had saved looked the
same whether or not other devices could reach it yet, so one still waiting
for approval read as done. Each subnet, and the offer to act as an exit
device, now shows its state — Published, Pending approval, Not saved or Not
connected — and a stand-in address is shown as assigned only once the route
is approved. While something is pending, a notice says how many subnets
other devices cannot reach yet and who approves them. The daemon's log says
the same:advertised routes are not published yetwith the routes, then
all advertised routes are published. Your own coordinator approves routes
by itself, so there a saved subnet goes straight to Published. - Changed — The Mesh page says which relay is which. It showed two
different things under the one word "Relay": the relay other devices send to
when they reach this device, and the relay this device sends to when it
reaches a peer — which is that peer's choice, and often a different machine.
The first is now "This device's home relay", with its region, its address
and the round trip to it. A relayed peer's tooltip names the second as the
peer's home relay. On calabi.net each also says whether it is one of
Calabi's relays or one of your own.
Windows installer
- Fixed — The desktop installer could finish without registering the
service, and report success. The app then opened on a console that could
not find its daemon, and installing again — any version — ended the same
way. The installer registers the service by runningcalabi daemon install --systemas the person installing, and that command refuses when the
command-line client of that Windows account is in standalone mode: typed in
a shell, it would register a service that signs in to calabi.net, which is
not what someone in standalone mode is asking for. The installer ignored the
refusal. An upgrade never met it, because the service was already there; a
first install, or one after an uninstall, did. The installer now says which
service it wants (--platform, below) and no longer goes by the exit code:
it looks for the registered service, and if there is none, or it will not
start, it stops with the daemon's own message, exits 3 or 4, and leaves the
reason inservice-install-error.login the install directory — where a
silent install (/S) can find it. - Added —
calabi daemon install --platform. From a client in
standalone mode it registers the platform service, for that one install: the
saved mode stays as it is, and nothing is written into the service's
environment. The refusal you get without it now lists it among the ways out.
Your server
- Fixed — A coordinator run from a policy file left every port open.
For a meshnet onCALABI_COORD_POLICY_FILEwith no ACL saved through the
admin API, the file decided which devices appeared in each other's maps, but
the packet filter sent to each device was compiled from saved ACLs alone —
and with none saved, that came out as "anything, from anywhere". So two
devices a rule connected on one port could reach each other on every port.
Devices the file kept apart stayed apart. The filter is now compiled from
the file, and the admin API's access check and save preview, which started
from the same wrong baseline, read it too. Update the coordinator: devices
get the corrected filter with their next map and need no update themselves.
A meshnet whose ACL was saved through the admin API was not affected.
On calabi.net
- Fixed — Clicking an approved route in the console's device list
revoked it. Under Mesh → Devices, an approved subnet was a tag that took
the approval back when clicked — no question asked, and nothing to say what
had just happened — and other devices lost the subnet at once. An approved
route now opens a menu, to copy the address or to revoke, and revoking asks
first and says what it costs: devices lose access immediately, and a
stand-in address goes back to the pool, so approving again may give a
different one. A route waiting for approval is still approved with one
click, and every change is confirmed on screen. Exit devices and services
waiting to be confirmed work the same way. - Changed — Switching a device between Calabi's nodes and your own moves
its home relay right away. The Platform / Self-hosted switch at the top of
the local console also decides which relays the device prefers as its home.
That part followed up to half a minute later, by stopping the mesh session
and joining again, which interrupted every mesh connection for a moment. The
home relay is now picked again in place once the tunnels have reconnected,
and the mesh session stays up. The confirmation shown before switching says
the home relay moves too.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v2.2.0 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at 48e42cd852b0d7ca90eb4d35de641cc3387439e0 — the commit these binaries were
built from — rebuilds all 21 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop), SHA256SUMS.edge and SHA256SUMS.coord
answer a different question: whether your download arrived intact. Archives
themselves are not reproducible (tar/zip record mtimes) — the manifest compares
the binary inside.
Not covered by the manifest
The installers. The two desktop ones are Tauri/Rust builds with their own
toolchains and are not reproducible yet; every installer is listed in
SHA256SUMS, so its integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
calabi-android.apk — built from apps/client-android and apps/client/mobile
in this repository, but not reproducibly yet: the Go core records the absolute
directory it was built in, so the same commit built elsewhere differs. It is
signed with the release key; check the signer before installing:
apksigner verify --print-certs calabi-android.apk
# certificate SHA-256 digest: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23Components
calabi— the client (tunnels + mesh + the local console on :7400)calabi-edge— the data plane:role: tunnelfor tunnels,role: mesh
for the mesh relay,role: bothfor the twocalabi-coord— the coordinator, every device's identity: invites, device
registry, addresses, ACLs; names the edge and signs the grants it accepts
calabi-coord and calabi-edge together are your server
(deploy/server).
Docker images (amd64 + arm64): calabinet/calabi, calabinet/calabi-edge,
calabinet/calabi-coord. The binary inside each is the released linux one,
byte for byte.
calabi 2.1.0
Devices reach relays over TLS. There is nothing to configure, and nothing
stops working while you upgrade: a relay switches to TLS only once the
coordinator, the relay's edge and the device all run 2.1.0, and until then it
works in plaintext as before. Update the coordinator first, then your edges,
then devices, and leave the new mesh.require_tls off until every device has
been updated.
Mesh
- Changed — Devices reach relays over TLS. A relay speaks TLS and the
older plaintext protocol on the same port (3340). A device whose relay map
marks a relay as TLS uses TLS only, and checks its certificate the way it
checks that node's edge: your coordinator's fingerprint for it, or public
roots withCALABI_COORD_EDGE_TRUST=system. A relay presents its node's own
certificate (tunnel.control_cert_pem, or the self-signed one in
state.dir), so there is nothing new to set up; arole: meshnode may now
name one withtunnel.control_cert_pemtoo. Your coordinator reads each
relay's certificate on 3340 once a minute, as it does the edge's, and marks a
relay as TLS only after completing a TLS handshake with it. A device that
fails the check does not fall back to plaintext. The new setting
mesh.require_tls(CALABI_EDGE_RELAY_REQUIRE_TLS, off by default) refuses
plaintext. Clients older than 2.1.0 speak only plaintext, so leave it off
until every client is updated. - Fixed — A subnet router started with
calabi mesh uppublished its
subnets under their real addresses. Since 1.8.0 a subnet router asks for a
stand-in prefix on every route it publishes, but the foreground command still
asked only for the routes named in--alias-routes— a flag its own help
calls ignored, and empty unless given. Socalabi mesh up --advertise-routes
asked for none, and a peer whose own LAN uses the same range could not reach
the subnet. It now asks the way the daemon does: once such a router is
upgraded, peers reach its subnets at a stand-in address in100.96.0.0/11
rather than the real one. Routers run by the daemon, including installed
services, were not affected.--alias-routesis still accepted, so existing
scripts keep running, and is now actually ignored: a value it cannot parse no
longer stops the command. - Fixed —
stun_port: 0did not turn a relay's STUN responder off, and
neither didCALABI_EDGE_RELAY_STUN_PORT=0. The settings table says 0 turns
it off, but the edge read 0 as "use the default" and kept answering STUN on
3478. Now 0 turns the responder off, and leaving the setting out still means
3478. So if one of your relays saysstun_port: 0, its STUN stops when you
upgrade that edge: devices can no longer measure the relay, and do not pick
it over one they can. To keep STUN, delete the line. A coordinator of your
own lists a relay's STUN port from its own settings, so for a relay with STUN
off leaveCALABI_COORD_DERP_STUN_PORT(orstun_portin the map file)
unset too.
Your server
- Fixed — A pinned fingerprint could be matched by any certificate the
server sent, not only its own: a server in the path could present its own
certificate followed by the real one. The client now accepts a pin on the
server's certificate, or on a CA that certificate verifiably chains to. This
is the check behindCALABI_EDGE_PIN, the fingerprint in an invite link, and
now a relay's certificate. Update devices to get it. - Fixed — An edge whose listener or background task stopped on its own
exited quietly, with status 0 and no log line, so it looked like a clean
stop. It now logstask exited before shutdownwith the task's name and exits
with status 1, so the log says why and a restart policy that acts only on
failure restarts it. Stopping the edge with a signal still exits 0.
Updates
- Fixed —
calabi updateon a device signed in to your own server said
it was a dev build, or that updates were disabled. Neither was true, and no
setting turns updates on there: a device connected to a self-hosted server
does not update itself, so your deployment never contacts
download.calabi.net. It now says that, and that whoever runs the server
distributes the client — the coordinator, the edges and the devices run the
same version.
On calabi.net
- Changed — Relays on calabi.net are reached over TLS, including the
one a self-hosted node runs. Devices check them against the calabi.net CA and
their host name. A self-hosted node checks the certificate it serves on 3340
the same way and reports the result, and only a relay that passes is marked
as TLS. A node whose certificate was issued without its public address keeps
plaintext until the certificate is re-issued; the node's log and its details
in the console say so. - Changed — A self-hosted node's relay admits only your organization's
devices. Each device proves a grant signed by calabi.net, as it already
does on Calabi's relays; a device of another organization, or one presenting
no grant, is turned away. The node learns the key from calabi.net when it
registers its relay, socoord_pubkeyandrequire_authneed not be set,
andrequire_auth: falseno longer turns it off on a node connected to
calabi.net. Until calabi.net first answers, a relay with no key kept in
state.dirdoes not listen. Devices need no update: every client with the
mesh already answers the relay's check. - Fixed — A node's own relay with STUN off would have been listed with
STUN port 3478. calabi.net filled in 3478 for a relay that registered itself
with none, so its devices would have measured a port nothing answers on. It
is now listed without a STUN port. Until thestun_port: 0fix above, no
relay could get into that state. - Fixed — A self-hosted node that could not register with calabi.net
said so only in debug logs, so a relay that never showed up in your mesh
looked healthy. The first failure is now a warning that says what it breaks,
a reminder follows every 10 minutes while it lasts, and the recovery is
logged. - Fixed — The console's setup for a self-hosted node wrote the config
layout from before 2.0.0, and leftstate.dirout of a relay-only node's
config. It now writes the current layout, and every node gets astate.dir,
where a relay keeps calabi.net's key and its certificate across restarts.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v2.1.0 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at b49ea83c27ae394555d88d23aa70a9d50aee62c4 — the commit these binaries were
built from — rebuilds all 21 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop), SHA256SUMS.edge and SHA256SUMS.coord
answer a different question: whether your download arrived intact. Archives
themselves are not reproducible (tar/zip record mtimes) — the manifest compares
the binary inside.
Not covered by the manifest
The installers. The two desktop ones are Tauri/Rust builds with their own
toolchains and are not reproducible yet; every installer is listed in
SHA256SUMS, so its integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
calabi-android.apk — built from apps/client-android and apps/client/mobile
in this repository, but not reproducibly yet: the Go core records the absolute
directory it was built in, so the same commit built elsewhere differs. It is
signed with the release key; check the signer before installing:
apksigner verify --print-certs calabi-android.apk
# certificate SHA-256 digest: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23Components
calabi— the client (tunnels + mesh + the local console on :7400)calabi-edge— the data plane:role: tunnelfor tunnels,role: mesh
for the mesh relay,role: bothfor the twocalabi-coord— the coordinator, every device's identity: invites, device
registry, addresses, ACLs; names the edge and signs the grants it accepts
calabi-coord and calabi-edge together are your server
(deploy/server).
Docker images (amd64 + arm64): calabinet/calabi, calabinet/calabi-edge,
calabinet/calabi-coord. The binary inside each is the released linux one,
byte for byte.
calabi 2.0.0
The edge's config file changed. A server that has been running since 1.14.0
will not start on the new edge until its config catches up, so take this part
before you pull.
If you run the server bundle (deploy/server), take the new
docker-compose.yml along with the new image. That file writes the edge's
config, and the version of it shipped before this release is one the new edge
refuses: it had no public: block, which is now required of a node that serves
tunnels. Your .env needs nothing new — the value comes from
CALABI_PUBLIC_HOST, which you already set.
If you wrote your own edge config, two things can stop it: public.host is
now required on a node that serves tunnels, and a handful of settings that had
quietly done nothing for some time are now refused instead of ignored. Both are
spelled out under "Your server" below, and the edge names the exact line on
startup rather than coming up half-working.
Everything that merely moved still loads from where it was, so the rest of your
file is fine as it stands. A machine that only relays mesh traffic does not need
public.host at all. The client's own config file is renamed in this release
too, but it migrates itself and needs nothing from you — also below.
Updates
- Fixed — A client in a container was told to reinstall itself as a system
service. Running thecalabinet/calabiimage, the local console and
calabi updatesaid the daemon was not a privileged system service and to
reinstall it withcalabi daemon install --system— a command the same binary
refuses inside a container, because a container supervises its own process and
has no OS service to install. A container is now recognised before anything
else is decided, and gets the answer that works: pull the new image and
recreate the container. It still checks, so the console still says a newer
version exists. A container running as root was also a case where the daemon
believed it could install: it downloaded and verified an installer on every
cycle and only then found there was no service manager to restart. It no
longer starts.
Local console
- Fixed — The console gave a container four more pieces of advice it
could not follow. Settings listedcalabi daemon install / start / status / stop / uninstallwith copy buttons — every one of which this binary refuses
in a container — and the pinned-identity notes said to reinstall or uninstall
the service to change the key or take the machine offline. The daemon now
tells the console it runs in a container, and those four places say what
works there instead: recreate the container with a different
CALABI_API_KEY, stop or remove it, manage it with your container runtime.
The same correction reaches the log a container operator actually reads: a
rejected API key no longer answers withcalabi daemon install --api-key.
Clients installed on a machine are unchanged, wording included.
On calabi.net
-
Fixed — A payment that settled slowly could leave your order
unpaid. Paying in USD goes through Paddle, and its local payment methods
(WeChat Pay, Alipay and similar) confirm the money some minutes after you are
finished — long enough, occasionally, to miss the 30-minute window an order
stayed open for. The order was marked expired, the payment then arrived with
nothing left to apply it to, and the money had been taken. USD orders now stay
open for two hours, and a payment that still arrives after that completes the
order instead of being dropped. Orders you canceled yourself are not affected:
a payment on one is reported for a human to look at, never turned into a
subscription behind you. -
Fixed — A payment link sent by Paddle said it was invalid. The payment
page read the transaction id from?txn=, but links Paddle generates itself —
the one behind "copy payment link", and the reminder it emails about an
unfinished payment — carry it as?_ptxn=. Every one of those landed on
"this payment link is invalid or has expired" with the id sitting in the
address bar under a different name. Both spellings work now. Opening such a
link fills the page in; it does not start a checkout on its own, so the
agreements still have to be ticked.
Your server
-
Changed — The edge config is grouped by service, and the node has one
address. Two things were wrong with the old file at once.Nothing in it said which half of the program would read a given setting:
control:,http:,relay:and the rest all sat at the top level, so a
relay-only config looked exactly like an edge's. And four of those blocks
named an address —control: { addr: ":7443" }— whose host half was always
empty, because every one of those listeners has to be reachable from outside
the machine. They named ports while looking like addresses, sitting among the
settings that really are addresses. The port was then written twice: once on
the listener, once insidepublic.addr, with nothing comparing them — move
the listener, forget the other line, and every client goes on dialing a port
nothing is listening on.The layout now follows the two services.
tunnel:holds what only tunnels
read,mesh:(formerlyrelay:) what only the mesh relay reads, and the top
level what both use. Where this node can be reached is one setting, and each
service names only the port it wants:node_label: my-server role: both mode: standalone coord_pubkey_file: ./coord.pub state: { dir: ./state } public: host: server.example.com tunnel: base_domain: tunnels.example.com control_port: 7443 http_port: 80 https_port: 443 mesh: derp_port: 3340 stun_port: 3478
A node running
role: meshcan now delete its wholetunnel:block, and one
runningrole: tunnelits wholemesh:block.public.hostis required on a node that serves tunnels. It is what
clients dial and the name its certificate is issued for. It used to be
optional, falling back to the listener's bind address — a usable dial string
only on the one machine that is also the client. A node that only relays mesh
traffic still does not need it; it is named in your coordinator's relay map.
From the environment:CALABI_EDGE_PUBLIC_HOST.Apart from that line, an existing file keeps working. Every setting that
moved still loads from where it was:control: { addr: ":7443" }as
tunnel.control_port: 7443,https: { self_signed: true }as
tunnel.https_self_signed: true,relay:asmesh:, andpublic.addras
public.host— the last provided the port written into it matches the
listener's. A file that spells one setting both ways, or whose two ports
disagree, is refused rather than guessed at, naming the one to keep.One thing this shape cannot express: an external port different from the one
bound. If you reach your edge through a mapping like that, bind the external
port directly. A bind address that names a host —control.addr: "10.0.0.5:7443"— is refused rather than quietly widened to every interface.Some settings are now refused rather than ignored. They had not done
anything for some time, and a file that still carries one describes a node
that does not exist. Theidentity:,quota:,config_svc:andnats:
blocks andtunnel.addr/cert.addrwent when the edge stopped reaching the
control plane directly.presence.interval_secondsandcert.refresh_seconds
go now: both had a working default, and in the whole history of this
repository no deployed config ever set either one.edge_classandorg_id
go for a different reason — which paying plans are routed to a node, and which
organization it belongs to, are not decisions a node should be making from a
file on its own disk. On calabi.net both come from the certificate that node
authenticates with, and a server you run yourself never used either. Delete
them from your file.Refusing rather than ignoring is the point. Removing the settings alone would
have been enough to stop them working — a key the edge does not know is
skipped — and that is the outcome worth avoiding: you would set a cadence, get
the default, and never be told.One more rename, for anyone running several edges in one region: the edge
config'smesh:block used to mean edge-to-edge forwarding of tunnel
traffic, which has nothing to do with the mesh. It is now
tunnel.peer_forward:. This is the one old spelling that is not migrated
silently —mesh:means the relay now, and quietly reading a peer-forward
block as relay settings would take a node out of its region's forwarding pool
without saying so. A file that still has it is refused, and the error says
what to rename it to. -
Changed — A replaced control certificate is picked up without a
restart. The edge readcontrol_cert_pem/control_key_pemonce at start.
Renewing those files, or swapping a self-signed certificate for a real one,
changed nothing until the next restart — the edge went on presenting the copy
it had read, and kept presenting it after it expired. It now re-reads them
when they change. A rotation caught half-done (the certificate written, the
key not yet) keeps serving the pair that still works and tries again on the
next connection, rather than turning a moment into a refused device. -
Changed — The daemon's config file is now
calabi.yaml, and the
coordinator moved to aserver:block. The file stopped being about
tunnels a while ago: a machine that only joins your server has a complete,
working config in whichtunnels:is empty. And the coordinator — which
server the device belongs to, and how it proves itself there — was nested
undermesh:, which read as though a device that never turns the mesh on
did not need it. It does: `calabi http ...
calabi 1.14.0
Your phone can refuse to be reached, and a server you run yourself can be
watched. Most of this release is fixes; the two additions are in Mobile and
Your server below.
What is in this repository and what is not. Calabi is also a hosted service,
and one release covers both. Mesh, Your server and Mobile are
changes in what this release ships, built from this tree. The last section,
On calabi.net, is the hosted control plane: its code is not in this
repository, and a self-hosted deployment does not get it. This time that section
is the larger one — bandwidth limits and tunnel approval are decisions a control
plane makes, and a coordinator with no control plane behind it makes none of
them.
Mesh
- Fixed — Switching organization kept the exit device of the one left.
The phone and the desktop went on showing it, and looked for a device of that
name in the new organization; one there that offered an exit would have
carried all the traffic. Switching to another organization or signing out now
clears the exit device, and so does leaving a self-hosted server on the phone.
Switching to the organization you are already in keeps it.
Your server
- Added — Monitoring.
deploy/server/monitoringruns Prometheus beside
the coordinator and the edge, scraping both over loopback, with three alerts:
either program stopped answering, the edge has been failing or shedding
visitor traffic for ten minutes, the coordinator has been failing device RPCs
for ten minutes. It reaches nothing of ours. The alerts come with unit tests
and instructions to run them, because a rule naming a metric nothing emits
parses perfectly, loads without a warning, and then never fires — which looks
exactly like a healthy server. The README says which failures the alerts
deliberately ignore, and how to add your tunnels' upstreams if you run those
too.
Mobile
The app signs in to calabi.net, or joins a self-hosted server directly.
- Added — Block incoming connections, under Settings → This device —
the same switch the desktop has had since 1.10.0. Nobody on the mesh can open
a connection to the phone, whatever the access rules allow; connections the
phone starts still work. It is the one access-control decision that belongs to
the person holding the device rather than to an administrator. While it is on,
the Mesh tab says so — a phone refusing everything otherwise looks exactly
like a healthy one. - Changed — Device rows no longer list the services on each device. A
service could only be opened in a browser or copied, and most of what runs on
a mesh is neither — SSH, a database, a file share. Tapping the browser one
while the mesh was off sent you to an address that could not answer. - Fixed — A server the phone cannot reach now says so. It used to print
the network stack's own error, in English, on three tabs at once. It is one
sentence now, in one place, with the other screens saying only that they have
nothing to show. - Fixed — The exit-device list said no device on this server offers to be
an exit device after failing to reach that server, and the usage figures
showed an empty card for the same reason. Both now say the list could not be
read. - Fixed — Forgot password on the sign-in page opened the page the reset
email links to, which said the link was invalid or expired. It now opens the
form that sends the email. calabi.net already forwards the old link there, so
earlier versions of the app work too.
On calabi.net
The hosted control plane. None of this is in this repository, and a
self-hosted deployment does not get it. The code for the first two entries is
here, but what switches them on is not: an edge and a coordinator with no
control plane behind them apply no limits at all.
- Added — Bandwidth is limited in two tiers. Each tunnel has its own
allowance, and everything an organization runs shares a second one above it.
Before, the allowance sat on a client connection, so an organization got a
multiple of it by opening several. - Added — Relayed mesh traffic is limited the same way, per device and
per organization. The client slows itself down to the rate it is given, and
the relay enforces it if the client does not — a relay cannot slow traffic
down gently, it can only drop, and inside WireGuard a dropped packet costs a
round trip. - Added — Tunnels created by members can require an administrator's
approval, under Tunnels → Settings. Off by default, and turning it on does
not touch tunnels that already exist. A tunnel waiting for approval is created
but not served; the daemon and the CLI say which it is instead of reporting a
failure. Turning the setting back off releases everything queued. - Changed — The website and the console follow your system's light or dark
appearance, and keep the same choice across both. The sign-in and sign-up
pages have a dark appearance too, so signing in no longer changes the skin
under you.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v1.14.0 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at d64305c0d9961db21221aad55b73dfdb8bf9bb49 — the commit these binaries were
built from — rebuilds all 21 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop), SHA256SUMS.edge and SHA256SUMS.coord
answer a different question: whether your download arrived intact. Archives
themselves are not reproducible (tar/zip record mtimes) — the manifest compares
the binary inside.
Not covered by the manifest
The installers. The two desktop ones are Tauri/Rust builds with their own
toolchains and are not reproducible yet; every installer is listed in
SHA256SUMS, so its integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
calabi-android.apk — built from apps/client-android and apps/client/mobile
in this repository, but not reproducibly yet: the Go core records the absolute
directory it was built in, so the same commit built elsewhere differs. It is
signed with the release key; check the signer before installing:
apksigner verify --print-certs calabi-android.apk
# certificate SHA-256 digest: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23Components
calabi— the client (tunnels + mesh + the local console on :7400)calabi-edge— the data plane:role: edgefor tunnels,role: relay
for the mesh relay,role: bothfor the twocalabi-coord— the coordinator, every device's identity: invites, device
registry, addresses, ACLs; names the edge and signs the grants it accepts
calabi-coord and calabi-edge together are your server
(deploy/server).
Docker images (amd64 + arm64): calabinet/calabi, calabinet/calabi-edge,
calabinet/calabi-coord. The binary inside each is the released linux one,
byte for byte.
calabi 1.13.0
Your own server, as one thing. calabi-coord and calabi-edge together are
a self-hosted Calabi server, and the coordinator is every device's identity. A
device joins it once — an invite scanned on the phone, pasted into the console,
or calabi join in a terminal — and from then on the coordinator tells it where
the edge is and signs the grant the edge lets it in with. The same device has
tunnels and the mesh, and the mesh can be switched off on it without losing the
tunnels. deploy/server runs the coordinator and the edge from one compose file
and one .env.
What is in this repository and what is not. Your server, Tunnels,
Mesh, Local console and Mobile below are changes in what this
release ships, built from this tree. The last section, On calabi.net, is the
hosted control plane: its code is not in this repository, and a self-hosted
deployment does not get it.
Things that behave differently for setups that already exist.
- A self-hosted edge takes no tokens any more. It needs
mode: standalone
and the coordinator's public key (coord_pubkey, orcoord_pubkey_filewhere
the coordinator writes it;calabi-coord pubkeyprints it), and accepts
devices by the coordinator's grants, for tunnels and relay alike. A config
that still listsaccepted_tokensis refused at start and says why; an empty
list is ignored, so calabi.net's own edges, whose files carry
accepted_tokens: [], start as before. tunnels.yamlno longer names the edge.server,token,token_env,
insecure,ca_file,trustandpinsat the top level are refused in a
file you wrote, and dropped with a warning from the console's own. Join the
coordinator —calabi join, or amesh:block with a key — and the edge
comes from it.calabi http,tcp,udpandsniin standalone mode use the device
this client joined as, and no longer readCALABI_SERVER,CALABI_TOKENor
CALABI_INSECURE. A client that has not joined says so.- A standalone relay always checks grants, and needs the coordinator's key.
- A self-hosted coordinator serves TLS. One without certificate files used
to serve plaintext; it now makes a self-signed certificate on its first start
and keeps it in./coord-tls(CALABI_COORD_TLS_DIR). Pin its fingerprint on
the devices (calabi-coord fingerprintprints it; invites carry it), or set
CALABI_COORD_TLS=offto stay on plaintext. - Removing a key from the coordinator's key file no longer takes its devices
off. Devices come back by proving they hold their device key. Disable or
delete a device to remove it (calabi-coord device). - A coordinator with a database has no built-in key.
dev-meshnet-1-key
works only on a coordinator with neither a key file nor a database. - A connection to your coordinator is never checked against the CA compiled
into the client, which is calabi.net's.ca_file:is now the only CA
trusted for it, not one more.
Your server
- Added —
deploy/server: the coordinator and the edge on one machine,
from onedocker-compose.ymland one.env(the public address, an admin
token, the tunnel domain). The edge's config is written from the.env, the
coordinator hands the edge its public key over a shared volume and reads the
edge's certificate itself, and both run withCALABI_ENV=production. It runs
with Docker Compose orpodman compose. - Added — The coordinator is released. Archives for the same seven
platforms as the client and the edge,SHA256SUMS.coord, entries in the build
manifest, and a docker image,calabinet/calabi-coord(amd64, arm64), which
keeps its certificate, its grant key and — by default — a SQLite database in
the/datavolume. - Added — Grants. A coordinator without a platform behind it signs a
grant for each device with a key it creates on first start
(./coord-grant.key, orCALABI_COORD_RELAY_GRANT_KEY_FILE).
CALABI_COORD_GRANT_PUBKEY_FILEwrites the public half for an edge to read;
calabi-coord pubkeyprints it. A grant lasts an hour and devices renew it; a
disabled or deleted device keeps its tunnels at most until its grant runs out. - Added — The coordinator names the edge.
CALABI_COORD_EDGE_ADDRis
where devices dial it. Its fingerprint comes fromCALABI_COORD_EDGE_PIN, or
the coordinator reads the certificate the edge presents
(CALABI_COORD_EDGE_PROBE_ADDRwhen it reaches the edge another way) — every
few seconds until it has one, then every minute — so an edge that replaces its
certificate is followed by every device without anyone confirming it.
CALABI_COORD_EDGE_TRUST=systemis for an edge with a public certificate.
A device waiting for approval gets no edge. - Added — Invites.
calabi-coord inviteprints acalabi://joinlink, a
QR code, and the same as acalabi join "…"command line. By default an
invite admits one device, for 24 hours;--uses,--reusable,--expires,
--no-expiryand--tagchange that.calabi-coord authkey create,list
andrevokemanage keys without the link. The coordinator stores only a hash
of each key. - Added —
calabi-coord device list | approve | disable | enable | delete, so managing devices needs no hand-written calls to the admin API. - Added — The edge keeps its self-signed control certificate in
state.dir(control.crt,control.key) when it has no certificate files;
calabi-edge -fingerprintprints the fingerprint. Without astate.dirit
still makes a new certificate at every start, and now warns. - Fixed — A self-hosted relay crashed a minute after devices used it. A
relay with a label and no control plane set up usage reporting with nothing to
report to, and the first report crashed the process. Relays that report to
calabi.net (BYOI) were not affected. - Fixed — A coordinator on SQLite could fail a write with "database is
locked" when two writes met; it now waits its turn. - Fixed — An edge hot-reload could say "applied" and change nothing.
Only a fixed list of fields was compared, so a change to any field added
since —mode,role, everyrelay.*setting — was logged as applied and
took effect only at the next restart. A reload that changes anything but
base_domainis now refused, naming the fields, and goes through the same
steps as start-up. - Changed — (coordinator) Connection records keep the relayed part of each
hour's bytes; the new columns are added on start. The key file is re-read when
it changes, every two seconds.
Tunnels
- Added —
calabi join <invite>, the terminal's way onto your server:
it hands the join to the running daemon, or saves it and starts the daemon.
--name,--pin,--replace,--no-start-daemon. - Changed — A device signs in to your edge with its coordinator's grant
and the proof of its own key, answering a challenge the edge makes for each
connection. It dials the edge pinned to the fingerprint the coordinator gave,
and renews the grant on a live session before it runs out. - Changed —
calabi daemonin standalone mode without--configruns the
console's owntunnels.yamlfrom its data directory instead of exiting with
"missing --config". - Fixed —
calabi meshcommands failed while acalabi httpran beside
the daemon. Acalabi http,tcp,udporsnistarted while the daemon
ran recorded its own status page as the daemon's console, andcalabi mesh up,downandstatusthen went to that page. Only the daemon records its
console now. - Fixed —
calabi daemon startlooked for the service's console only beside
thecalabibinary. It now also looks in the system data directory, where the
desktop app's service keeps it, and otherwise says thatcalabi daemon status
will show the address. - Fixed — The status page said the requested port was busy when
CALABI_STATUS_ADDRasked for any port (…:0).
Mesh
- Added — The mesh is a switch, not the sign-in. Off — in the console,
orcalabi mesh down— the device leaves the mesh and keeps its tunnels, and
stays off across restarts;calabi mesh upwith no arguments puts it back. A
daemon reports its tunnels to the coordinator either way. - Added — A key is only for joining. A device that has joined comes back
by proving it holds its device key. A daemon remembers which device it is
(mesh-reauth.jsonin its data directory), soauth_key:can come out of its
mesh:block. A key's expiry and use limit apply to new devices. - Added — Leaving. A device that leaves from the phone or the console is
marked as signed out and needs a new invite to come back. Its record, address
and seat stay. - Added — Checking the coordinator's certificate:
--trust(system,
pin,ca,plaintext, orplatformfor calabi.net),--pinand
--ca-fileoncalabi mesh up, andtrust:,pins:,ca_file:in the
mesh:block. - Added — The relay address is optional. Without
--relaya device takes
its relays from the coordinator's directory. - Added — Tunnels and traffic on your coordinator. A daemon that has
joined reports its tunnels — name, type, addresses, whether each is up, bytes —
every five minutes and whenever the list changes. The coordinator keeps the
list and, with a database, tunnel traffic by the hour for 92 days, and gives
both, with this month's relayed traffic, to the phone and the console.
calabi.net's coordinator refuses these calls. - Fixed — A disabled device kept its session. Every call on a disabled
device's session is now refused. - Fixed — Desktop exit devices: relays dialed later went into the mesh.
Every relay in the coordinator's directory now keeps the machine's own route,
including one ...
calabi 1.12.0
The first Android app, and staged rollouts for the automatic update. Client
and edge move together, as always; the edge has no changes of its own.
What is in this repository and what is not. Calabi is also a hosted service,
and one release covers both. Updates, Mesh, Local console and
Mobile below are changes in what this release ships, built from this tree.
The last section, On calabi.net, is the hosted control plane: its code is not
in this repository, and a self-hosted deployment does not get it.
Two things behave differently for setups that already exist.
- A platform daemon now reports how each of its self-updates went — from and
to which version, which step failed, the error — to the control plane it is
signed in to, over the connection it already registers the device on. A
standalone daemon has no update agent and reports nothing. - On calabi.net, a newly advertised subnet route waits for an administrator.
Routes that are already working keep working. An organization that wants
routes to take effect on their own turns off Routes need approval under
Mesh → Settings. A self-hosted coordinator is unchanged: it has no console to
approve in, so it still approves every route itself, exit routes included.
Updates
- Added — Staged rollouts. A release can reach machines over a number of
hours instead of all at once. Each machine works out from its own install ID
whether its turn has come, and the local console says so — being rolled out
in stages; this machine's turn has not come yet — with the time its turn is
expected. Update now is not held back, and neither is a machine below the
minimum supported version. Clients 1.11.x and older do not know about stages
and install 1.12.0 as soon as they see it. - Added — Update results (see above). The daemon records that an update
started before it launches the installer, so an install that takes the service
down and never brings it back still shows up — as started, with no result. The
next process to start reports whether it is the new version. Results that
cannot be sent yet wait in a local queue of at most 50. - Fixed — macOS: an automatic update could stop at "Updating" for good.
The package let the installer putCalabi.appwherever it found another copy
with the same bundle ID, and on a machine with a second copy the install
stopped before the service was restarted. The package now always installs to
/Applications. The daemon also waits for the installer it started: if the
installer exits and the daemon is still the old one, the console reports the
failure with the installer's last line of output, and a second installer is
never started while one is running.
Mesh
- Fixed — Exit devices did not work. Two route checks each read the
default route as a claim on the mesh's own address range: from 1.8.0 the
coordinator dropped a device's0.0.0.0/0advertisement when it registered,
and from 1.9.0 the client dropped the chosen exit device's default route
before it reached WireGuard. Both now treat0.0.0.0/0and::/0on their
own; every other route overlapping the mesh range is still refused. On
calabi.net the coordinator side is already fixed — a device that already had
exit-device mode on needs it turned off and on once to advertise again. A
self-hosted coordinator needs this release. - Fixed — Linux subnet routers piled up NAT rules across restarts. A
router that crashed or was killed, rather than stopped, added its masquerade
rule again on the next start; one machine had forty identical copies. Rules
now carry a comment naming the device, and each start removes the ones it left
behind — including uncommented copies from older versions — before adding its
own. With nftables every device has its own table, so two clients on one
machine no longer delete each other's rules. The platform daemon also waits for
the mesh to shut down, up to 15 seconds, before it exits. - Changed — (coordinator) A device's periodic endpoint report no longer
pushes a new network map to every peer unless its endpoints or home region
actually changed. In a mesh of N devices, each one used to receive about N maps
a minute.
Local console
- Changed — Services are a tab of the Mesh page, beside Devices and Routing,
as they are in the web console. The old/servicesaddress redirects. - Fixed — On a machine with the desktop app installed,
calabi login
started a second daemon. From a terminal without administrator rights it
could not query the service, took that for "not installed", and started
another daemon on:7401— a second device, joining the mesh as whoever typed
the command. Not being allowed to query the service now counts as installed:
login leaves the service alone and says whether it runs on its own sign-in or
on an API key. - Fixed —
calabi daemon statusin the same terminal printed
Access is deniedand exited 1. It now says the service is installed, that
this shell cannot query its state, and where its console answers. - Fixed — macOS:
calabi daemon start,stopandrestartoperate the
service the installer registered (com.calabi.daemon). They looked for a
service namedcalabithat the installer never creates, andinstallwould
register a second root service over the same data directory.installnow
refuses, anduninstallprints the removal steps. - Fixed —
calabi logoutsaid "logged out" when nothing had been: when the
daemon runs on an API key and refused, when an installed service it cannot see
was still signed in, and when there was no sign-in at all. It now says what it
did and did not touch, and exits 1 when refused. - Fixed — The command line and the running daemon no longer spend the same
refresh token. A refresh token works once; when both refreshed at the same
moment, the one that lost was signed out. - Fixed — Settings no longer offers Sign out on a daemon that runs on an
API key or in standalone mode, where it failed and the page flashed back as if
it had worked. On an API key it shows the identity the service runs as, and
how to change it: reinstall the service with a different key. - Fixed — The desktop tray's Restart and Stop hints give commands that
work on that platform —launchctlon macOS,Restart-Serviceand
Stop-Servicein an administrator PowerShell on Windows — instead of
sudo calabi daemon restart, which neither installer puts on the PATH.
Mobile
Calabi for Android, first release. It makes a phone a device in your
organization's mesh. It signs in to calabi.net; it does not join a self-hosted
coordinator. Android 8.0 or later, 64-bit and 32-bit ARM, in English and
Simplified Chinese.
It is not on Google Play: calabi-android.apk is on the download page, and
Android warns before installing an app from outside the store. It does not
update itself — a newer APK installs over it. The certificate it is signed with
is printed on the releases page, to check before installing.
- Mesh device — connect and disconnect in the app or from a Quick Settings
tile, and see the organization's devices and whether each is reached directly
or through a relay. After Wi-Fi drops, the mesh is back within about two
seconds of the network returning. - Exit device — send all of the phone's traffic out through a device in
your mesh. Other mesh devices stay on direct connections meanwhile. - Connect at startup — off by default.
- Background running — some Android builds stop background apps, VPN
included, a while after the screen goes off. When the app finds it was stopped
that way it says so and opens the vendor's page for allowing background
activity (Huawei, Honor, Xiaomi); on other phones it asks to be exempt from
battery optimization. - Tunnels, read-only — the tunnels you can see in the organization, with
their public addresses to copy, share or open, and access records for the last
24 hours or 7 days. The phone does not serve tunnels. - Usage — this month's traffic against the plan's limit, the last seven
days, and mesh device seats. - Replace an old device — reinstalling the app creates a new mesh key, so
the phone joins as a new device. The app offers to remove your own offline
device of the same platform and take over its name.
On calabi.net
The hosted control plane. None of this is in this repository, and a
self-hosted deployment does not get it.
- Added — An organization update policy, under Clients → Update policy.
Administrators can require the organization's machines to install at least
security updates, or everything, automatically, and cap how many days a
machine may hold an update back. It only tightens: each machine follows
whichever of its own setting and the organization's is stricter, and gets its
own choice back when the policy is removed. It needs a 1.12.0 client, whose
local console shows the setting in effect and greys out what the organization
does not allow. - Added — The client list marks a machine whose last update failed, and the
client's details show its most recent update. - Changed — New subnet routes wait for an administrator (see the top of
this release). The switch is Routes need approval under Mesh → Settings; an
exit device always needs approval. - Changed — In Mesh → Devices, a device offering to be an exit device shows
one Exit device tag — green when approved, orange when waiting — which an
administrator clicks to approve or revoke. - Fixed — Turning off mesh connection records did not stay off: the stored
records were deleted once, recording carried on, and the switch showed as on
after a refresh. An organization that turned them off before this fix needs to
turn them off again. - **Fix...
calabi 1.11.1
A fix release for the automatic update 1.11.0 introduced. Client and edge move
together, as always; the edge has no changes of its own.
Updates
- Fixed — On Windows, an automatic update reported success and replaced
nothing. The installer overwrote the daemon's executable while the service
was still running it. Windows does not allow that, and a silent install does
not fail on it: it skips the file, exits successfully, and records the new
version in Apps & features. The desktop shell was updated, the daemon was not,
and the next restart tried again. The installer now stops the service and
waits until the file is actually released before copying — and if it cannot,
it restarts the service and refuses, rather than skipping the file. Verified
on a running service: the daemon is replaced and the service is back within
two seconds. - Fixed — A client installed some other way was handed the desktop
installer. A Windows client installed with scoop or from a zip and running
as a service received the desktop installer: the machine gained a desktop app
while its daemon stayed at the old version. A Homebrew install on macOS was
handed the .pkg the same way. Such an install now says that it updates the way
it was installed —scoop update calabi,brew upgrade calabi, or a new
archive — in the local console and incalabi update, and never runs the
installer. The Windows installer also refuses to take over a Calabi service it
did not create, which protects machines still running an older client.
On calabi.net
The hosted control plane. None of this is in this repository, and a
self-hosted deployment does not get it.
- Added — The download page offers the macOS desktop package, and a GitHub
download link beside every file. - Fixed — Actions inside a drawer no longer open a second dialog on top of
it: creating a tunnel for a client that is offline, and reopening a support
ticket, now ask in place.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v1.11.1 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at 3082d9caf82640520ff1c19bd2518ea50dfb4c8c — the commit these binaries were
built from — rebuilds all 14 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop) and SHA256SUMS.edge answer a different
question: whether your download arrived intact. Archives themselves are not
reproducible (tar/zip record mtimes) — the manifest compares the binary
inside.
Not covered by the manifest
The two desktop installers. Both are Tauri/Rust builds with their own
toolchains and are not reproducible yet; both are listed in SHA256SUMS, so
their integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
Components
calabi— the client (tunnels + mesh + the local console on :7400)calabi-edge— the data plane:role: edgefor tunnels,role: relay
for the mesh relaycalabi-coord— the mesh coordinator (build it from source; not shipped as
a binary yet)
calabi 1.11.0
A client that knows when it is out of date. Until this release the only
machines that ever learned a newer version existed were the ones somebody
happened to open a console on; a server installed once and left alone could sit
six versions behind with nothing anywhere saying so. Every daemon now checks,
says so where you will see it, and — where it is allowed to — installs it on a
schedule you choose. Client and edge move together, as always.
What is in this repository and what is not. Calabi is also a hosted service,
and one release covers both. Updates, Tunnels and Local console below
are changes in the binaries attached to this release, built from this tree. The
last section, On calabi.net, is the hosted control plane: its code is not in
this repository, and a self-hosted deployment does not get it.
Two things behave differently for setups that already exist.
- Every daemon now makes one request every six hours to
download.calabi.net, to read a signed list of published versions. It
downloads and installs nothing. SettingCALABI_UPDATE_MANIFESTto an empty
value turns the check off entirely. - A daemon running as a privileged system service can now replace itself.
What it does unasked is a setting on that machine, which starts at automatic
on Windows and macOS and at security updates only on Linux — a Linux daemon
is usually a server, and a server should not restart itself for a routine
release. A daemon that is not a privileged service never installs
anything: it reports and waits for you.
Updates
- Added — Every daemon checks; only a privileged service installs. These
are two different permissions and they are now two different answers. A daemon
that cannot install says which of the reasons applies — nothing was published
for this platform, this install is not a system service, the manifest is
malformed — instead of failing a check every six hours and telling nobody. - Added — A per-machine setting, in the local console under Settings:
automatic, security updates only, or notify me. Under automatic an
update waits for a maintenance window (03:00–05:00 by default, on the
machine's clock) and waits again while traffic is still moving through the
client — with a backstop, seven days by default, after which it stops waiting.
A laptop that is closed every night and a server that is busy every night both
still land the update. - Added — Security releases skip the waiting rules but do not override
notify me. A release below the publisher's minimum supported version
overrides even that, and the console says so plainly rather than letting the
restart be a surprise. - Added —
calabi updateandcalabi update --check, for the machines
that never have a console open. They drive the running daemon rather than
doing the work themselves, so there is exactly one set of signature checks in
front of the installer. - Added — Linux installs in place — the new binary is unpacked, run once
to prove it works, swapped in, and the service is restarted. Windows and macOS
run their own installers, as before. - Added — The version list is signed as a whole. One signature covers the
version, every platform's installer URL, its SHA-256 and its own signature, so
a manifest cannot be edited to point a privileged service at a different file.
A daemon also refuses a manifest offering a version older than one it has
already seen, unless the publisher marked it a deliberate rollback — replaying
a genuinely signed older release is a downgrade that needs no key. - Changed — The local console's Settings page: updates are their own card,
and the account card is gone — the organization you are serving now appears in
the account menu, where the rest of your identity already was.
Tunnels
- Fixed —
calabi http 8080works in an official build. One-shot
commands never looked for an edge; they used a default address that release
builds deliberately leave empty, so the first command in the documentation
failed for everyone who had not also installed the service. They now find an
edge the same way the daemon does, and they honourCALABI_EDGE_AFFINITY, so
an organization with its own edges does not silently land on the platform. - Fixed — Restarting a self-hosted edge no longer takes the command line down
with it. The edge a client is anchored to is a preference, not a wall. - Fixed — A one-shot command no longer serves the full dashboard page, which
had no API behind it, and no longer counts tunnels pushed from the control
plane as its own — which is why its own tunnel appeared twice. - Fixed — A tunnel created from the command line now registers the device,
like every other way of creating one. - Fixed — A tunnel whose upstream has never been probed is no longer reported
as healthy. - Fixed — (edge) A subdomain sequence that rolled backwards could land on a
row belonging to another organization, and the edge claimed it.
Local console
- Fixed —
loginandlogoutnow tell the running daemon. Signing out used
to delete the credentials file while the daemon kept serving the old session. - Fixed — The address the console prints is where it is listening now, not a
pointer left behind by a previous start; and when it is not on:7400—
which is normal with a second client on the machine — the CLI says where it
is instead of looking broken. - Fixed —
--standalonesurvivesdaemon install. The service came back
wired to the platform, quietly undoing the choice. - Fixed — The overview's tunnel card showed the organization's quota rather
than the one that applies to you, and the relay figure had quietly become a
local measurement while its label still said otherwise.
On calabi.net
The hosted control plane. None of this is in this repository, and a
self-hosted deployment does not get it.
- Fixed — The same account saw traffic figures three orders of magnitude
apart depending on whether it was the web console or an agent asking. Traffic
is one figure for the whole organization, for every member — the narrower
answer does not exist, because relayed bytes are recorded between devices and
can never be attributed to a person. Per-member quotas are unaffected; those
are still per member. - Fixed — The monthly-traffic card said "0" for organizations serving from
their own edges. - Changed — A tunnel's Settings tab is administrators only. Mesh
connection records are now visible to auditors, and members can see the
records for their own devices. - Changed — Creating and editing a tunnel, and adding a self-hosted node,
are drawers rather than full pages. - Added — A tunnel records which door it was created from — command
line, local console, web console or API key — shown in the admin console.
These are the same files published at https://download.calabi.net — same
version, same bytes. This release is not a separate CI build; it is that build,
uploaded here as well.
Verify them yourself
gh release download v1.11.0 --pattern build-manifest.json
bash scripts/verify-reproducible-build.sh build-manifest.jsonThat clones this repository at 27339716930a6942f1e3c4d8b7e2814bfd22937c — the commit these binaries were
built from — rebuilds all 14 Go artifacts and compares hashes. The
verify-release workflow runs exactly that on GitHub's runners for this
release, so you can also just read its result.
SHA256SUMS (client + desktop) and SHA256SUMS.edge answer a different
question: whether your download arrived intact. Archives themselves are not
reproducible (tar/zip record mtimes) — the manifest compares the binary
inside.
Not covered by the manifest
The two desktop installers. Both are Tauri/Rust builds with their own
toolchains and are not reproducible yet; both are listed in SHA256SUMS, so
their integrity is checkable.
calabi-desktop-windows-x64-setup.exe — the calabi daemon it carries is a
different matter: since v1.7.1 that is the released calabi-windows-amd64
binary itself, byte for byte, so the program you actually run is covered by the
manifest even though the installer around it is not.
calabi-desktop-macos-universal.pkg — here the daemon is NOT covered either.
It is a universal (arm64 + x86_64) binary produced by lipo, so it is not byte
identical to either released macOS archive. It is also neither code-signed nor
notarized yet: macOS will stop it on first open, and the download page says how
to proceed. If you would rather not trust a build you did not make, the client
is in this repository — build it.
Components
calabi— the client (tunnels + mesh + the local console on :7400)calabi-edge— the data plane:role: edgefor tunnels,role: relay
for the mesh relaycalabi-coord— the mesh coordinator (build it from source; not shipped as
a binary yet)