To generate the UniFFI bindings, we don't actually need to be on an
Apple device. To make cross-platform development a bit easier, we
extract the binding generation step into the Makefile.
This PR eliminates JSON-based communication across the FFI boundary,
replacing it with proper
uniffi-generated types for improved type safety, performance, and
reliability. We replace JSON string parameters with native uniffi types
for:
- Resources (DNS, CIDR, Internet)
- Device information
- DNS server lists
- Network routes (CIDR representation)
Also, get rid of JSON serialisation in Swift client IPC in favour of
PropertyList based serialisation.
Fixes: https://github.com/firezone/firezone/issues/9548
---------
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
Apparently if we set the CFBundleDisplayName we hint by default that
we *do* want to show it on newer macOS versions.
This seems to have been uncovered by Xcode 26 build recently.
Fixes#10579
Fixes an issue where the Resources menu would not populate when
launching
the app while already connected by ensuring the initial VPN status
triggers the resource loading handler.
Fixes#9837
Building on top of #10507, setting the initial Internet Resource state
is a piece of cake. All we need to do is thread a boolean variable
through to all call-sites of `Session::connect`. Without the need for
the Internet Resource's ID, we can simply pass in the boolean that is
saved in the configuration of each client.
Resolves: #10255
Instead of the generic "disable any kind of resource"-functionality that
connlib currently exposes, we now provide an API to only enable /
disable the Internet Resource. This is a lot simpler to deal with and
reason about than the previous system, especially when it comes to the
proptests. Those need to model connlib's behaviour correctly across its
entire API surface which makes them unnecessarily complex if we only
ever use the `set_disabled_resources` API with a single resource.
In preparation for #4789, I want to extend the proptests to cover
traffic filters (#7126). This will make them a fair bit more
complicated, so any prior removal of complexity is appreciated.
Simplifying the implementation here is also a good starting point to fix
#10255. Not implicitly enabling the Internet Resource when it gets added
should be quite simple after this change.
Finally, resolving #8885 should also be quite easy. We just need to
store the state of the Internet Resource once per API URL instead of
globally.
Resolves: #8404
---------
Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Bumps
[github.com/getsentry/sentry-cocoa](https://github.com/getsentry/sentry-cocoa)
from 8.55.0 to 8.55.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/getsentry/sentry-cocoa/releases">github.com/getsentry/sentry-cocoa's
releases</a>.</em></p>
<blockquote>
<h2>8.55.1</h2>
<h3>Features</h3>
<h3>Fixes</h3>
<ul>
<li>Fix macOS's frameworks not following the versioned framework
structure (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/6049">#6049</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2c70925b98"><code>2c70925</code></a>
release: 8.55.1</li>
<li><a
href="4aa237de0d"><code>4aa237d</code></a>
chore: Update changelog</li>
<li><a
href="e8f2418539"><code>e8f2418</code></a>
fix(ci): Resolve symlink path before removing architectures (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/6049">#6049</a>)</li>
<li>See full diff in <a
href="https://github.com/getsentry/sentry-cocoa/compare/8.55.0...8.55.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[github.com/getsentry/sentry-cocoa](https://github.com/getsentry/sentry-cocoa)
from 8.49.0 to 8.55.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/getsentry/sentry-cocoa/releases">github.com/getsentry/sentry-cocoa's
releases</a>.</em></p>
<blockquote>
<h2>8.55.0</h2>
<blockquote>
<p>[!Important]
Xcode 26 no longer allows individual frameworks to contain arm64e slices
anymore if the main binary doesn't contain them.
We have decided to split the Dynamic variant and
Sentry-WithoutUIKitOrAppKit of Sentry into two variants:</p>
<ul>
<li><code>Sentry-Dynamic</code>: Without ARM64e</li>
<li><code>Sentry-Dynamic-WithARM64e</code>: <em>With</em> ARM64e
slice</li>
<li><code>Sentry-WithoutUIKitOrAppKit</code>: Without ARM64e</li>
<li><code>Sentry-WithoutUIKitOrAppKit-WithARM64e</code>: <em>With</em>
ARM64e slice</li>
</ul>
<p>If your app does not need arm64e, you don't need to make any changes.
But if your app <em>needs arm64e</em> please use
<code>Sentry-Dynamic-WithARM64e</code> or
<code>Sentry-WithoutUIKitOrAppKit-WithARM64e</code> from 8.55.0 so you
don't have issues uploading to the App Store.</p>
</blockquote>
<h3>Features</h3>
<ul>
<li>Add a new prebuilt framework with arm64e and remove it from the
regular one (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5788">#5788</a>)</li>
<li>Add <code>beforeSendLog</code> callback to
<code>SentryOptions</code> (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5678">#5678</a>)</li>
<li>Structured Logs: Flush logs on SDK flush/close (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5834">#5834</a>)</li>
<li>Add a new prebuilt framework with ARM64e for WithoutUIKitOrAppKit
(<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5897">#5897</a>)</li>
<li>Add source context and vars fields to SentryFrame (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5853">#5853</a>)</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Add support for PDFKit views in session replay (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5750">#5750</a>)</li>
<li>Fix Infinite Session Replay Processing Loop (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5765">#5765</a>)</li>
<li>Fix memory leak in SessionReplayIntegration (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5770">#5770</a>)</li>
<li>Fix reporting of energy used while profiling (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5768">#5768</a>)</li>
<li>Fixed a build error in <code>SentryFeedback.swift</code> when
building with cocoapods on Xcode 14.2 (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5917">#5917</a>)</li>
<li>Fix linking against Sentry on an app extension (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5813">#5813</a>)</li>
</ul>
<h2>8.54.1-alpha.2</h2>
<blockquote>
<p>[!Important]
Xcode 26 no longer allows individual frameworks to contain arm64e slices
anymore if the main binary doesn't contain them.
We have decided to split the Dynamic variant and
Sentry-WithoutUIKitOrAppKit of Sentry into two variants:</p>
<ul>
<li><code>Sentry-Dynamic</code>: Without ARM64e</li>
<li><code>Sentry-Dynamic-WithARM64e</code>: <em>With</em> ARM64e
slice</li>
<li><code>Sentry-WithoutUIKitOrAppKit</code>: Without ARM64e</li>
<li><code>Sentry-WithoutUIKitOrAppKit-WithARM64e</code>: <em>With</em>
ARM64e slice</li>
</ul>
<p>If your app does not need arm64e, you don't need to make any changes.
But if your app <em>needs arm64e</em> please use
<code>Sentry-Dynamic-WithARM64e</code> or
<code>Sentry-WithoutUIKitOrAppKit-WithARM64e</code> from 8.55.0 so you
don't have issues uploading to the App Store.</p>
</blockquote>
<h3>Features</h3>
<ul>
<li>Structured Logs: Flush logs on SDK flush/close (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5834">#5834</a>)</li>
<li>Add a new prebuilt framework with ARM64e for WithoutUIKitOrAppKit
(<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5897">#5897</a>)</li>
<li>Add source context and vars fields to SentryFrame (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5853">#5853</a>)</li>
</ul>
<h3>Fixes</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3c2eee7773"><code>3c2eee7</code></a>
release: 8.55.0</li>
<li><a
href="3ec47ae715"><code>3ec47ae</code></a>
ci: Use iOS18.5 since 18.6 seems to be unavailable sometimes (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5944">#5944</a>)</li>
<li><a
href="1a34ddc00f"><code>1a34ddc</code></a>
ci: Bump iOS and tvOS versions to 18.6 (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5942">#5942</a>)</li>
<li><a
href="67e8e3ecf0"><code>67e8e3e</code></a>
Merge branch 'release/8.54.1-alpha.2'</li>
<li><a
href="45482a6a1b"><code>45482a6</code></a>
fix: Build error on Xcode 14.2 (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5917">#5917</a>)</li>
<li><a
href="9174496cdc"><code>9174496</code></a>
release: 8.54.1-alpha.2</li>
<li><a
href="891fd1d420"><code>891fd1d</code></a>
ref: Make SentryEventDecodable internal in V9 (<a
href="https://redirect.github.com/getsentry/sentry-cocoa/issues/5808">#5808</a>)</li>
<li><a
href="dba4ee8975"><code>dba4ee8</code></a>
Refactor SentryFeedback serialization to have 2 declarations depending
if SDK...</li>
<li><a
href="669f02ce7d"><code>669f02c</code></a>
Add changelog</li>
<li><a
href="4506bdb8b3"><code>4506bdb</code></a>
fix: Build error on Xcode 14.2</li>
<li>Additional commits viewable in <a
href="https://github.com/getsentry/sentry-cocoa/compare/8.49.0...8.55.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Quality of life improvements for macOS devs, mostly relevant when not
using Xcode as daily driver - although some convenience functions &
explicit sentry dependency should make it better there too.
show an alert to the user and ask to quit previous Firezone instance
manually before starting a new one.
Resolves: #10295
---------
Signed-off-by: Mariusz Klochowicz <mariusz@klochowicz.com>
Co-authored-by: Jamil <jamilbk@users.noreply.github.com>
In the following sequence of events, a user will be unable to connect to
the DNS resource for a few seconds because macOS is caching the queries,
preventing connlib from seeing them.
1. User signs in
2. User has _no_ access to DNS Resource A
3. User queries for DNS Resource A - NXDOMAIN -> macOS caches this
4. Admin grants access to DNS Resource A
5. User tries query again -> connlib never sees the query -> cache hit
-> NXDOMAIN
To fix this, we call `networkSettings.apply()` whenever the resource
list has changed. This has been tested to trigger a DNS cache flush on
macOS.
In Xcode 16, how the compiler determines the size of C structs changed.
In Xcode 15 and below, when the compiler saw `__res_9_state()`, it
thought, "This is a C struct. I know its size and layout from the
system's header files. I will generate code to allocate that much memory
and zero it out." This was a type-based operation; it only needed the
"blueprint" for the struct.
In Xcode 16 and later, the compiler sees `__res_9_state()` and thinks,
"This is a C struct. To initialize it, I need to link to the actual
symbol named `___res_9_state` inside the libresolv library." This became
a symbol-based operation, creating a direct dependency that didn't exist
before.
To fix this, we initialize a raw pointer with a manual type
specification to the zeroed-out struct, which reverts to the prior
behavior.
Has been tested on iPhone 12, iOS 17.
Fixes#10108
In
https://github.com/firezone/firezone/pull/10022/files#diff-a84e8f62a17ac67f781019e6ac0456567fd18ffa7c13b3248609d78debb6480eL342
we removed the path connectivity filter that prevented path update loops
on iOS. This was done to try and respond more aggressively to path
updates in order to set system DNS resolvers, because we can't glean
from the path's instance properties that any DNS configuration has
changed - we simply have to assume so.
Unfortunately, that caused an issue where we now enter a path update
loop and effectively never fully bring the tunnel up.
I've spent lots of time looking for a reliable work around that would
allow us to both, (1) respond to path updates for DNS configuration
changes on the system (we have to blindly react to these), and (2) avoid
path update loops, but alas, without a significant time investment,
there doesn't seem to be a way.
So, we only set system resolvers on iOS in the path update handler if
there was _also_ a detectable connectivity change, and settle on the
assumption that **most** DNS configuration changes will be accompanied
by a network connectivity change as well.
When a mac device goes to sleep, it typically does not turn off the WiFi
radio. If the mac never leaves the network it was on upon sleep, then
upon wake it will never receive a path update, and we would not have
performed a connlib network reset.
To fix this, we now properly detect a wake from sleep event and issue a
network reset.
Fixes#10056
In 45466e3b78, the `networkSettings`
variable was no longer saved on the `adapter` instance, causing all
calls of the iOS-specific version of getting system resolvers to return
the connlib sentinels after the tunnel first came up.
This PR fixes that logic bug and also cleans this area of the codebase
up just a tiny bit so it's easier to follow.
Lastly, we also fix a bug where if the tunnel came up while Firezone was
already running, `networkSettings` would be `nil`, and we would read the
default system resolvers, which were the connlib sentinels.
Fixes https://github.com/firezone/firezone/issues/10017
On iOS, we were using the tempfile directory to stage the log export,
and then moving this into place from the share sheet presented to the
user.
For some reason, this has stopped working in iOS 18.5.0, and we need to
stage the file in the standard documents directory instead.
Fixes#10014
Fixes an edge case where a WiFi interface could go offline, then come
back online with the same connectivity, preventing the path update
handler from reset connlib state.
This would cause an issue especially if the WiFi was disabled for more
than 30 seconds / 2 minutes.
On Apple platforms, we tried to be clever about filtering path updates
from the network connectivity change monitor, because there can be a
flurry of them upon waking from sleep or network roaming.
However, because of this, we had a bug that could occur in certain
situations (such as waking from sleep) where we could effectively "land"
on an empty DNS resolver list. This could happen if:
1. We receive a path update handler that meaningfully changes
connectivity, but its `supportsDNS` property is `false`. This means it
hasn't received any resolvers from DHCP yet. We would then setDns with
an empty resolver list.
2. We then receive a path update handler with the _only_ change being
`supportDNS=true`. Since we didn't count this change as a meaningful
path change, we skipped the `setDns` call, and connlib would be stuck
without DNS resolution.
To fix the above, we stop trying to be clever about connectivity
changes, and just use `oldPath != path`. That will increase reset a bit,
but it will now handle other edge cases such as an IP address changing
on the primary interface, any other interfaces change, and the like.
Fixes#9866
When validating the found system resolvers on macOS and iOS, we would
stop after validating the first found resolver (usually IPv4) because
`break` was used instead of `continue`.
Fixes#9914
---------
Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This PR replaces the use of Apple Archive with an API that allows us to
zip the log file contents. This API doesn't handle symlinks well so we
move the symlink out of the way before making the zip. The symlink is
then moved back after the process is completed. Any errors in this
process are ignored as the symlink itself is not a critical component of
Firezone.
The zip compression is marginally less efficient than the Apple Archive.
Instead of compressing ~2GB of logs to 11.8 MB we now get an archive of
12.4 MB. Considering how much easier zip files are to handle, this seems
like a fine trade-off.
<img width="774" alt="Screenshot 2025-06-16 at 00 04 52"
src="https://github.com/user-attachments/assets/8fb6bade-5308-40b9-a446-2a2c364cb621"
/>
Resolves: #7475
---------
Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Jamil Bou Kheir <jamilbk@users.noreply.github.com>
Rust and Swift disagree about the formatting of these, leading to
constant git file dirtiness when working on Apple code.
These were originally added when we didn't have as much automation to
regeneration these on each build.
As recommended by the Sentry team [0], "App hang" tracking should be
disabled before calling into certain system APIs like showing alerts to
prevent false-positives.
[0]: https://github.com/getsentry/sentry-cocoa/issues/3472
When using alternative editors other than Xcode, Intellisense is usually
provided by `sourcekit-lsp`. The LSP server also uses `swift-format`
under the hood to format the code but appears to default to using 4
spaces for indentation. In order to standardise on how much indentation
is used, we add a `.swift-format` file that specifies is.
It appears that the code generation in our `build.rs` generates the code
with different formatting and this therefore constantly shows up as
untracked changes in my editor.
When working on the Swift codebase, I noticed that running the formatter
produced a massive diff. This PR re-formats the Swift code with `swift
format . --recursive --in-place` and adds a CI check to enforce it going
forward.
Resolves: #9534
---------
Co-authored-by: Jamil Bou Kheir <jamilbk@users.noreply.github.com>
Until we implement #3959 for the Apple client, we need to be careful
around how we de-initialise the Rust session. Callback-based designs are
difficult to get right across boundaries because they enable
re-entrances which then lead to runtime errors.
Specifically, freeing the session needs to cleanup the tokio runtime but
that is impossible if the callback is still executed from that exact
runtime. To workaround this, we need to free the session pointer from a
new task.
Moving to #3959 will solve this in a much more intuitive way because we
can ditch the callbacks and instead move to a stream of events that the
host app can consume.
---------
Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
This PR fixes two crashes related to lifetimes on Apple:
- `completionHandler` was being called from within a Task executor
context, which could be different from the one the IPC call was received
on
- The `getLogFolderSize` task could return and attempt to call
`completionHandler` after the PacketTunnelProvider deinit'd
- We were calling the completionHandler from `stopTunnel` manually.
Apple explicitly says not to do this. Instead, we must call
`cancelTunnelWithError(nil)` when we want to stop the tunnel from e.g.
the `onDisconnect`. Apple with then call our `stopTunnel` override. The
downside is that we have no control over the `NEProviderStopReason`
received in this callback, but we don't use it anyway. Instead, we write
the reason to a temporary file and read it from the GUI process when we
detect a status change to `disconnected`. When that occurs, we're able
to show a UI notification (macOS only - iOS can show this notification
from the PacketTunnelProvider itself).
The GUI client already uses the same function to check for
authentication errors. Therefore, this case is already handled there.
For Swift, we can easily expose this getter via the `swift-bridge`
module. For Android, we don't expose it for now. Once we tackle #3959,
this should be easier to do. In the meantime, the UX on Android is not
super terrible. The user gets signed out and we will then receive a 401
when we try to sign-in again the next time.
Resolves: #9289
When pulling IPs from system resolvers, it's possible the IPv6 addresses
may contain scopes which will cause connlib to barf when parsing.
To fix these, we first convert to the Swift-native type `IPv4Address` or
`IPv6Address` and then use the string representation of those types,
which normalizes them to plain addresses.
Fixes#9055
If the tunnel is already down when we try to quit the application, we
were throwing a harmless error because we mistakenly required a
connected status to send the `stopTunnel` command, which is just a no-op
if we're already connected.