Skip to content

Releases: calabinet/calabi

calabi 2.3.0

Choose a tag to compare

@calabinet calabinet released this 18 Oct 08:49

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 with calabi 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_port listener 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 an sni endpoint on a node with no way in for it —
    before this release, every node without sni_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 and outcome=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 as sni, 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.json

That 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: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23

Components

  • calabi — the client (endpoints + mesh + the local console on :7400)
  • calabi-edge — the data plane: role: tunnel for endpoints, role: mesh
    for the mesh relay, role: both for the two
  • calabi-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

Choose a tag to compare

@calabinet calabinet released this 15 Oct 02:30

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: #/tunnels is
    #/endpoints and #/tunnels/new is #/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.
  • 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.json

That 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: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23

Components

  • calabi — the client (endpoints + mesh + the local console on :7400)
  • calabi-edge — the data plane: role: tunnel for endpoints, role: mesh
    for the mesh relay, role: both for the two
  • calabi-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

Choose a tag to compare

@calabinet calabinet released this 09 Oct 01:55

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, logs the 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 ::/0 counts 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 yet with 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 running calabi daemon install --system as 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 in service-install-error.log in 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 on CALABI_COORD_POLICY_FILE with 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.json

That 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: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23

Components

  • calabi — the client (tunnels + mesh + the local console on :7400)
  • calabi-edge — the data plane: role: tunnel for tunnels, role: mesh
    for the mesh relay, role: both for the two
  • calabi-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

Choose a tag to compare

@calabinet calabinet released this 29 Sep 03:55

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 with CALABI_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; a role: mesh node may now
    name one with tunnel.control_cert_pem too. 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 up published 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. So calabi 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 in 100.96.0.0/11
    rather than the real one. Routers run by the daemon, including installed
    services, were not affected. --alias-routes is 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: 0 did not turn a relay's STUN responder off, and
    neither did CALABI_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 says stun_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 leave CALABI_COORD_DERP_STUN_PORT (or stun_port in 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 behind CALABI_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 logs task exited before shutdown with 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 update on 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, so coord_pubkey and require_auth need not be set,
    and require_auth: false no longer turns it off on a node connected to
    calabi.net. Until calabi.net first answers, a relay with no key kept in
    state.dir does 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 the stun_port: 0 fix 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 left state.dir out of a relay-only node's
    config. It now writes the current layout, and every node gets a state.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.json

That 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: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23

Components

  • calabi — the client (tunnels + mesh + the local console on :7400)
  • calabi-edge — the data plane: role: tunnel for tunnels, role: mesh
    for the mesh relay, role: both for the two
  • calabi-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

Choose a tag to compare

@calabinet calabinet released this 23 Sep 13:41

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 the calabinet/calabi image, the local console and
    calabi update said the daemon was not a privileged system service and to
    reinstall it with calabi 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 listed calabi daemon install / start / status / stop / uninstall with 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 with calabi 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 inside public.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: (formerly relay:) 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: mesh can now delete its whole tunnel: block, and one
    running role: tunnel its whole mesh: block.

    public.host is 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: as mesh:, and public.addr as
    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. The identity:, quota:, config_svc: and nats:
    blocks and tunnel.addr / cert.addr went when the edge stopped reaching the
    control plane directly. presence.interval_seconds and cert.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_class and org_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's mesh: 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 read control_cert_pem / control_key_pem once 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 a server: block.
    The file stopped being about
    tunnels a while ago: a machine that only joins your server has a complete,
    working config in which tunnels: is empty. And the coordinator — which
    server the device belongs to, and how it proves itself there — was nested
    under mesh:, which read as though a device that never turns the mesh on
    did not need it. It does: `calabi http ...

Read more

calabi 1.14.0

Choose a tag to compare

@calabinet calabinet released this 21 Sep 07:04

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/monitoring runs 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.json

That 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: 5800bc9938dd507b1a4fb7cc497f9b3ce50e3c20655fdcb5383aac4049f97d23

Components

  • calabi — the client (tunnels + mesh + the local console on :7400)
  • calabi-edge — the data plane: role: edge for tunnels, role: relay
    for the mesh relay, role: both for the two
  • calabi-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

Choose a tag to compare

@calabinet calabinet released this 19 Sep 07:28

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, or coord_pubkey_file where
    the coordinator writes it; calabi-coord pubkey prints it), and accepts
    devices by the coordinator's grants, for tunnels and relay alike. A config
    that still lists accepted_tokens is 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.yaml no longer names the edge. server, token, token_env,
    insecure, ca_file, trust and pins at 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 a mesh: block with a key — and the edge
    comes from it.
  • calabi http, tcp, udp and sni in standalone mode use the device
    this client joined as, and no longer read CALABI_SERVER, CALABI_TOKEN or
    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 fingerprint prints it; invites carry it), or set
    CALABI_COORD_TLS=off to 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 one docker-compose.yml and 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 with CALABI_ENV=production. It runs
    with Docker Compose or podman 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 /data volume.
  • 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, or CALABI_COORD_RELAY_GRANT_KEY_FILE).
    CALABI_COORD_GRANT_PUBKEY_FILE writes the public half for an edge to read;
    calabi-coord pubkey prints 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_ADDR is
    where devices dial it. Its fingerprint comes from CALABI_COORD_EDGE_PIN, or
    the coordinator reads the certificate the edge presents
    (CALABI_COORD_EDGE_PROBE_ADDR when 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=system is for an edge with a public certificate.
    A device waiting for approval gets no edge.
  • Added — Invites. calabi-coord invite prints a calabi://join link, a
    QR code, and the same as a calabi join "…" command line. By default an
    invite admits one device, for 24 hours; --uses, --reusable, --expires,
    --no-expiry and --tag change that. calabi-coord authkey create, list
    and revoke manage 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 -fingerprint prints the fingerprint. Without a state.dir it
    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, every relay.* setting — was logged as applied and
    took effect only at the next restart. A reload that changes anything but
    base_domain is 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 daemon in standalone mode without --config runs the
    console's own tunnels.yaml from its data directory instead of exiting with
    "missing --config".
  • Fixed — calabi mesh commands failed while a calabi http ran beside
    the daemon.
    A calabi http, tcp, udp or sni started while the daemon
    ran recorded its own status page as the daemon's console, and calabi mesh up, down and status then went to that page. Only the daemon records its
    console now.
  • Fixed — calabi daemon start looked for the service's console only beside
    the calabi binary. It now also looks in the system data directory, where the
    desktop app's service keeps it, and otherwise says that calabi daemon status
    will show the address.
  • Fixed — The status page said the requested port was busy when
    CALABI_STATUS_ADDR asked for any port (…:0).

Mesh

  • Added — The mesh is a switch, not the sign-in. Off — in the console,
    or calabi mesh down — the device leaves the mesh and keeps its tunnels, and
    stays off across restarts; calabi mesh up with 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.json in its data directory), so auth_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, or platform for calabi.net), --pin and
    --ca-file on calabi mesh up, and trust:, pins:, ca_file: in the
    mesh: block.
  • Added — The relay address is optional. Without --relay a 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 ...
Read more

calabi 1.12.0

Choose a tag to compare

@calabinet calabinet released this 18 Sep 01:52

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 put Calabi.app wherever 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's 0.0.0.0/0 advertisement when it registered,
    and from 1.9.0 the client dropped the chosen exit device's default route
    before it reached WireGuard. Both now treat 0.0.0.0/0 and ::/0 on 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 /services address 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 status in the same terminal printed
    Access is denied and 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, stop and restart operate the
    service the installer registered
    (com.calabi.daemon). They looked for a
    service named calabi that the installer never creates, and install would
    register a second root service over the same data directory. install now
    refuses, and uninstall prints the removal steps.
  • Fixed — calabi logout said "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 — launchctl on macOS, Restart-Service and
    Stop-Service in 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...
Read more

calabi 1.11.1

Choose a tag to compare

@calabinet calabinet released this 16 Sep 09:27

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 in calabi 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.json

That 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: edge for tunnels, role: relay
    for the mesh relay
  • calabi-coord — the mesh coordinator (build it from source; not shipped as
    a binary yet)

calabi 1.11.0

Choose a tag to compare

@calabinet calabinet released this 16 Sep 07:49

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. Setting CALABI_UPDATE_MANIFEST to 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 update and calabi 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 8080 works 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 honour CALABI_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 — login and logout now 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 — --standalone survives daemon 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.json

That 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: edge for tunnels, role: relay
    for the mesh relay
  • calabi-coord — the mesh coordinator (build it from source; not shipped as
    a binary yet)