diff --git a/.gitignore b/.gitignore
index ffcac66..d8ccdec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,18 @@
# dotenv files
.env
+# Local iOS deployment target (contains a personal device UDID).
+.ios-device
+
+# Apple signing credentials and provisioning artifacts. Keep these in Keychain or
+# an approved secret store; never commit them to the application repository.
+*.cer
+*.p12
+*.p8
+*.mobileprovision
+*.provisionprofile
+*.certSigningRequest
+
# User-specific files
*.rsuser
*.suo
diff --git a/FieldLogger/FieldLogger.csproj b/FieldLogger/FieldLogger.csproj
index 35c2479..b11545b 100644
--- a/FieldLogger/FieldLogger.csproj
+++ b/FieldLogger/FieldLogger.csproj
@@ -26,10 +26,10 @@
$(NoWarn);MVVMTK0045
- Field Logger
+ UM Trace
- com.undergroundmagnetics.fieldlogger
+ com.umagul.trace
1.0
@@ -46,6 +46,10 @@
6.5
+
+
+ ITSAppUsesNonExemptEncryption
+
diff --git a/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy b/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
index 24ab3b4..a93de40 100644
--- a/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
+++ b/FieldLogger/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
@@ -36,8 +36,6 @@ More information: https://aka.ms/maui-privacy-manifest
E174.1
-
+
diff --git a/doc/iOS_TestFlight.md b/doc/iOS_TestFlight.md
new file mode 100644
index 0000000..1f6889e
--- /dev/null
+++ b/doc/iOS_TestFlight.md
@@ -0,0 +1,77 @@
+# UM Trace TestFlight build
+
+## Fixed application identity
+
+- Display name: `UM Trace`
+- Bundle ID: `com.umagul.trace`
+- Apple team ID: `W2N8APPQ2C`
+- App Store Connect SKU: `UM-TRACE-IOS`
+- Distribution profile: `UM Trace App Store`
+
+The provisioning profile and all Apple signing files are local-only and covered by
+`.gitignore`. Never commit a certificate, private key, App Store Connect API key, `.p12`, or
+provisioning profile.
+
+## Required local toolchain
+
+- Xcode 26 at `/Applications/Xcode.app`
+- .NET SDK 10.0.100 as the build host
+- Workload set 10.0.100 with `maui-ios` and its .NET 9/iOS 26 compatibility pack
+- An `Apple Distribution` identity, including its private key, in the login Keychain
+- The `UM Trace App Store` provisioning profile installed locally
+
+This repository's local SDK is installed at `~/.dotnet`. Verify it with:
+
+```sh
+~/.dotnet/dotnet --info
+~/.dotnet/dotnet workload list
+```
+
+## Create an archive
+
+Run the release helper from a normal macOS Terminal session. The build number must increase for
+every upload to App Store Connect:
+
+```sh
+cd /Users/brent/ul-platform/app
+./scripts/publish-testflight.sh 1
+```
+
+The app remains on its existing .NET 9 target. The .NET 10 SDK host supplies the supported .NET 9
+compatibility pack for Xcode 26, avoiding an unrelated Android/Mac Catalyst framework migration.
+The script deliberately supplies Xcode through `DEVELOPER_DIR`; it does not change the machine's
+global `xcode-select` setting. It restores only the iOS target, builds with the iOS 26 SDK, selects
+the installed `UM Trace App Store` profile, and prints the generated `.ipa` path.
+
+Before uploading, verify that this command lists a valid distribution identity:
+
+```sh
+security find-identity -v -p codesigning
+```
+
+If the identity is absent, import a `.p12` containing the matching private key or create a new
+Apple Distribution certificate through Xcode and regenerate the provisioning profile against it.
+
+## Deploy a development build to an iPhone
+
+The local development profile is named `UM Trace Development`. It must include the target phone
+and match an `Apple Development` identity for team `W2N8APPQ2C`. Keep the phone unlocked and
+connected by USB for the first deployment, trust the Mac when prompted, and enable Developer Mode.
+
+Save the phone UDID in the gitignored `.ios-device` file, then run from a normal macOS Terminal:
+
+```sh
+cd /Users/brent/ul-platform/app
+./scripts/deploy-ios-device.sh
+```
+
+You can instead pass a UDID as the first argument or set `UM_TRACE_DEVICE_UDID`. The helper uses
+Xcode 26, builds the `Debug` configuration for `ios-arm64`, signs with the development profile,
+installs the app, and launches it on the selected phone.
+
+## Background and privacy declarations
+
+The iOS bundle declares `bluetooth-central` and `location` background modes, Bluetooth and
+always/when-in-use location explanations, the MAUI `UserDefaults` required-reason API, and exempt
+standard TLS use. Physical-device testing remains required to prove the SRS eight-hour background
+BLE/location session; declaring a background mode does not itself guarantee continuous execution.
diff --git a/scripts/deploy-ios-device.sh b/scripts/deploy-ios-device.sh
new file mode 100755
index 0000000..7da7190
--- /dev/null
+++ b/scripts/deploy-ios-device.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+dotnet_bin="${DOTNET_BIN:-${HOME}/.dotnet/dotnet}"
+developer_dir="${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer}"
+codesign_key="${UM_TRACE_DEVELOPMENT_KEY:-Apple Development: Brent Perteet (WZ56HVC6FD)}"
+codesign_profile="${UM_TRACE_DEVELOPMENT_PROVISION:-UM Trace Development}"
+device_udid="${1:-${UM_TRACE_DEVICE_UDID:-}}"
+
+if [[ -z "${device_udid}" && -f "${repo_dir}/.ios-device" ]]; then
+ IFS= read -r device_udid < "${repo_dir}/.ios-device"
+fi
+
+if [[ -z "${device_udid}" ]]; then
+ echo "Usage: $0 " >&2
+ echo "Alternatively, set UM_TRACE_DEVICE_UDID or save the UDID in ${repo_dir}/.ios-device." >&2
+ exit 2
+fi
+
+if [[ ! -x "${dotnet_bin}" ]]; then
+ echo "Missing .NET SDK at ${dotnet_bin}. Set DOTNET_BIN or install the local SDK." >&2
+ exit 2
+fi
+
+if [[ ! -d "${developer_dir}" ]]; then
+ echo "Missing Xcode developer directory at ${developer_dir}. Set DEVELOPER_DIR." >&2
+ exit 2
+fi
+
+export DEVELOPER_DIR="${developer_dir}"
+
+"${dotnet_bin}" restore "${repo_dir}/FieldLogger/FieldLogger.csproj" \
+ -p:TargetFrameworks=net9.0-ios \
+ -p:RuntimeIdentifier=ios-arm64
+
+# Restoring the MAUI head with a narrowed TargetFrameworks property can rewrite the
+# referenced headless library's assets file. Restore that library explicitly before building.
+"${dotnet_bin}" restore "${repo_dir}/src/FieldLogger.Sync/FieldLogger.Sync.csproj"
+
+"${dotnet_bin}" build "${repo_dir}/FieldLogger/FieldLogger.csproj" \
+ -t:Run \
+ -p:TargetFrameworks=net9.0-ios \
+ -f net9.0-ios \
+ -c Debug \
+ -p:RuntimeIdentifier=ios-arm64 \
+ -p:_DeviceName="${device_udid}" \
+ -p:CodesignKey="${codesign_key}" \
+ -p:CodesignProvision="${codesign_profile}" \
+ --no-restore
diff --git a/scripts/publish-testflight.sh b/scripts/publish-testflight.sh
new file mode 100755
index 0000000..8eda0cf
--- /dev/null
+++ b/scripts/publish-testflight.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+dotnet_bin="${DOTNET_BIN:-${HOME}/.dotnet/dotnet}"
+developer_dir="${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer}"
+build_number="${1:-}"
+codesign_key="${UM_TRACE_CODESIGN_KEY:-Apple Distribution}"
+codesign_profile="${UM_TRACE_CODESIGN_PROVISION:-UM Trace App Store}"
+
+if [[ ! "${build_number}" =~ ^[0-9]+$ ]]; then
+ echo "Usage: $0 " >&2
+ exit 2
+fi
+
+if [[ ! -x "${dotnet_bin}" ]]; then
+ echo "Missing .NET 10 SDK at ${dotnet_bin}. Set DOTNET_BIN or install .NET 10." >&2
+ exit 2
+fi
+
+if [[ ! -d "${developer_dir}" ]]; then
+ echo "Missing Xcode developer directory at ${developer_dir}. Set DEVELOPER_DIR." >&2
+ exit 2
+fi
+
+export DEVELOPER_DIR="${developer_dir}"
+
+"${dotnet_bin}" restore "${repo_dir}/FieldLogger/FieldLogger.csproj" \
+ -p:TargetFrameworks=net9.0-ios \
+ -p:RuntimeIdentifier=ios-arm64
+
+# Restoring the MAUI head with a narrowed TargetFrameworks property can rewrite the
+# referenced headless library's assets file. Restore that library explicitly before publish.
+"${dotnet_bin}" restore "${repo_dir}/src/FieldLogger.Sync/FieldLogger.Sync.csproj"
+
+"${dotnet_bin}" publish "${repo_dir}/FieldLogger/FieldLogger.csproj" \
+ -p:TargetFrameworks=net9.0-ios \
+ -f net9.0-ios \
+ -c Release \
+ -p:RuntimeIdentifier=ios-arm64 \
+ -p:ArchiveOnBuild=true \
+ -p:BuildIpa=true \
+ -p:CodesignKey="${codesign_key}" \
+ -p:CodesignProvision="${codesign_profile}" \
+ -p:ApplicationVersion="${build_number}" \
+ --no-restore
+
+find "${repo_dir}/FieldLogger/bin/Release/net9.0-ios/ios-arm64" \
+ -type f -name '*.ipa' -print