diff --git a/PRIVACY.md b/PRIVACY.md
index 08ef1e94ea..42a65123c3 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -3,7 +3,7 @@
**App:** Amethyst (Android Nostr client)
**Publisher:** Vitor Pamplona
**Contact:** amethyst@vitorpamplona.com
-**Last updated:** 2026-05-24
+**Last updated:** 2026-09-12
Amethyst is free, open-source software (MIT License — see `LICENSE`). It is not a service. There is no Amethyst server, no Amethyst account, and the developer has no access to data stored on your device.
@@ -20,6 +20,7 @@ Using the app causes the following data to leave your phone:
- **Nostr events** you publish, sent to the relays you have configured.
- **Subscriptions** (filters describing what you want to read), sent to those relays.
- **Media uploads** (images, audio, video), sent to the media server you select.
+- **Workout summaries**, when you choose to publish one — see [Health and fitness data](#health-and-fitness-data-health-connect) below.
- *(Google Play build, push notifications enabled)* a per-device push token, your public key, and a preferred relay, registered with Google Firebase Cloud Messaging so a notification proxy can wake the app.
- *(F-Droid build, push notifications enabled)* a per-device token registered with whichever UnifiedPush distributor you install (e.g. ntfy).
@@ -29,6 +30,36 @@ The developer does not run any server that aggregates or stores this data.
Configuration, cached events, keys, drafts, and other operational data live in the app's local storage. Other apps cannot read it on a standard, non-rooted Android device. You can wipe it by clearing the app's storage or uninstalling.
+### Health and fitness data (Health Connect)
+
+Amethyst's **Workouts** section lets you publish a summary of a finished workout to the Nostr relays you choose (a NIP-101e kind 1301 event), so the people who follow you can see it. To save you typing the numbers in by hand, Amethyst can read the workout your watch or fitness app already saved to **Android Health Connect** and pre-fill the post.
+
+The feature is optional and off until you grant the permissions. Amethyst asks for them only when you open the New Workout composer — never on first launch.
+
+**What Amethyst reads, and what each type is for:**
+
+| Health Connect data type | Permission | What it is used for |
+| --- | --- | --- |
+| ExerciseSession | `READ_EXERCISE` | The workout itself: activity type, start time and duration — the title, date and duration of the post. |
+| Distance | `READ_DISTANCE` | The distance of the run, ride, walk or swim. |
+| ActiveCaloriesBurned | `READ_ACTIVE_CALORIES_BURNED` | The energy the workout burned. |
+| TotalCaloriesBurned | `READ_TOTAL_CALORIES_BURNED` | Fallback energy figure for sources that only record total energy. |
+| HeartRate | `READ_HEART_RATE` | Average and maximum heart rate over the workout — how hard the effort was. |
+| Steps | `READ_STEPS` | The step count of a run, walk or hike. |
+| ElevationGained | `READ_ELEVATION_GAINED` | How much you climbed. |
+
+Health Connect groups a few data types under one permission: `READ_EXERCISE` also covers CyclingPedalingCadence and `READ_STEPS` also covers StepsCadence. Amethyst does not read, store, or publish cadence — those types come attached to the permissions above and are never requested separately.
+
+**Limits on this access:**
+
+- **Read-only.** Amethyst never writes to Health Connect.
+- **Foreground only.** Reads happen only while the New Workout composer is on screen. Amethyst does not request `READ_HEALTH_DATA_IN_BACKGROUND` and has no background health worker.
+- **Last 7 days only.** Only sessions that finished in the previous 7 days are offered. Amethyst does not request `READ_HEALTH_DATA_HISTORY`.
+- **No location.** Amethyst does not request `READ_EXERCISE_ROUTE`, so it never receives the GPS track of a workout.
+- **Nothing is uploaded automatically.** Health data stays on your device until you pick a suggestion, review the pre-filled post, and publish it yourself. The developer runs no server; a published post goes to the Nostr relays you configured, and those numbers then become public like any other post you make.
+- **No other use.** Health data is never used for advertising, analytics, profiling, or sale, and is never shared with third parties. It is not used to determine your eligibility for insurance, credit, or employment, and is not transferred to any such party.
+- **Revocable.** Turn the feature off under Settings → Compose → "Suggest workouts to share", or revoke the permissions in Health Connect at any time. Amethyst keeps the workout suggestions it has already shown only in memory; revoking access stops all reads immediately.
+
### What relays can see
A relay you connect to sees:
diff --git a/amethyst/src/main/AndroidManifest.xml b/amethyst/src/main/AndroidManifest.xml
index 2bf34594e9..dd2530ce41 100644
--- a/amethyst/src/main/AndroidManifest.xml
+++ b/amethyst/src/main/AndroidManifest.xml
@@ -188,11 +188,6 @@
-
-
-
-
-
@@ -446,15 +441,28 @@
-
+
+
+
+
+
+
+
+
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleActivity.kt
new file mode 100644
index 0000000000..140313cf99
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleActivity.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2025 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.health
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.net.toUri
+import com.vitorpamplona.amethyst.ui.StringResSetup
+import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
+
+/**
+ * The Health Connect permissions rationale — the screen Health Connect itself opens when the user
+ * taps "privacy policy" / "read more" next to Amethyst, on both the pre-request dialog and the
+ * later data-management screens.
+ *
+ * Declaring the intent filters is not optional: without a target for
+ * `androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE` (Android 13 and lower) or
+ * `ACTION_VIEW_PERMISSION_USAGE` + `CATEGORY_HEALTH_PERMISSIONS` (Android 14+) the permission
+ * request fails silently and no dialog appears at all.
+ *
+ * Deliberately its own activity rather than a route inside MainActivity: Health Connect launches it
+ * cold, from outside the app, with no account loaded and no guarantee the user is even logged in.
+ * It shows static, read-only copy and touches nothing account-scoped.
+ */
+class HealthConnectRationaleActivity : AppCompatActivity() {
+ companion object {
+ const val PRIVACY_POLICY_URL = "https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ enableEdgeToEdge()
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ AmethystTheme {
+ StringResSetup()
+ HealthConnectRationaleScreen(
+ onOpenPrivacyPolicy = {
+ runCatching { startActivity(Intent(Intent.ACTION_VIEW, PRIVACY_POLICY_URL.toUri())) }
+ },
+ onClose = { finish() },
+ )
+ }
+ }
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleScreen.kt
new file mode 100644
index 0000000000..f7d88845d0
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleScreen.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2025 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.health
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
+import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
+import com.vitorpamplona.amethyst.commons.resources.Res
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_calories
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_distance
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_elevation
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_exercise
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_headline
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_heart_rate
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_intro
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_limit_optional
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_limit_publish
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_limit_window
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_limit_write
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_limits_title
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_privacy_policy
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_steps
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_title
+import com.vitorpamplona.amethyst.commons.resources.health_connect_rationale_what_title
+import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
+import com.vitorpamplona.amethyst.ui.stringRes
+
+/**
+ * Static, account-free explanation of what Amethyst reads from Health Connect and why. Shown both
+ * from Health Connect itself (see [HealthConnectRationaleActivity]) and from the Connect card in
+ * the workout composer, so the user can read the rationale before granting anything.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun HealthConnectRationaleScreen(
+ onOpenPrivacyPolicy: () -> Unit,
+ onClose: () -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ ShorterTopAppBar(
+ title = { Text(stringRes(Res.string.health_connect_rationale_title)) },
+ navigationIcon = {
+ IconButton(onClose) {
+ Icon(
+ symbol = MaterialSymbols.Close,
+ contentDescription = null,
+ )
+ }
+ },
+ )
+ },
+ ) { padding ->
+ Column(
+ modifier =
+ Modifier
+ .fillMaxSize()
+ .padding(padding)
+ .verticalScroll(rememberScrollState())
+ .padding(horizontal = 20.dp, vertical = 12.dp),
+ verticalArrangement = Arrangement.spacedBy(14.dp),
+ ) {
+ Text(
+ text = stringRes(Res.string.health_connect_rationale_headline),
+ style = MaterialTheme.typography.titleMedium,
+ )
+ Text(
+ text = stringRes(Res.string.health_connect_rationale_intro),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+
+ SectionTitle(stringRes(Res.string.health_connect_rationale_what_title))
+ Bullet(stringRes(Res.string.health_connect_rationale_exercise))
+ Bullet(stringRes(Res.string.health_connect_rationale_distance))
+ Bullet(stringRes(Res.string.health_connect_rationale_calories))
+ Bullet(stringRes(Res.string.health_connect_rationale_heart_rate))
+ Bullet(stringRes(Res.string.health_connect_rationale_steps))
+ Bullet(stringRes(Res.string.health_connect_rationale_elevation))
+
+ SectionTitle(stringRes(Res.string.health_connect_rationale_limits_title))
+ Bullet(stringRes(Res.string.health_connect_rationale_limit_window))
+ Bullet(stringRes(Res.string.health_connect_rationale_limit_write))
+ Bullet(stringRes(Res.string.health_connect_rationale_limit_publish))
+ Bullet(stringRes(Res.string.health_connect_rationale_limit_optional))
+
+ OutlinedButton(
+ onClick = onOpenPrivacyPolicy,
+ modifier = Modifier.fillMaxWidth().padding(top = 6.dp),
+ ) {
+ Text(stringRes(Res.string.health_connect_rationale_privacy_policy))
+ }
+ }
+ }
+}
+
+@Composable
+private fun SectionTitle(text: String) {
+ Text(
+ text = text,
+ style = MaterialTheme.typography.titleSmall,
+ color = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.padding(top = 6.dp),
+ )
+}
+
+@Composable
+private fun Bullet(text: String) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Text(
+ text = "•",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ Text(
+ text = text,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/DetectedWorkoutCarousel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/DetectedWorkoutCarousel.kt
index 43147533ab..6a0a88fd85 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/DetectedWorkoutCarousel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/DetectedWorkoutCarousel.kt
@@ -20,6 +20,7 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.suggestion
+import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -39,6 +40,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -60,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.resources.Res
import com.vitorpamplona.amethyst.commons.resources.workout_from_health_connect
import com.vitorpamplona.amethyst.commons.resources.workout_suggestion_connect_button
+import com.vitorpamplona.amethyst.commons.resources.workout_suggestion_connect_details
import com.vitorpamplona.amethyst.commons.resources.workout_suggestion_connect_message
import com.vitorpamplona.amethyst.commons.resources.workout_suggestion_connect_title
import com.vitorpamplona.amethyst.commons.resources.workout_suggestion_distance_km
@@ -68,6 +71,7 @@ import com.vitorpamplona.amethyst.service.workouts.health.DetectedWorkout
import com.vitorpamplona.amethyst.service.workouts.health.HealthConnectManager
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.health.HealthConnectRationaleActivity
import com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.labelRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.symbol
import com.vitorpamplona.amethyst.ui.stringRes
@@ -128,7 +132,12 @@ fun DetectedWorkoutCarousel(
when (granted) {
null -> return // not checked yet — render nothing so the prompt never flashes
- false -> ConnectCard(modifier) { permissionLauncher.launch(HealthConnectManager.PERMISSIONS) }
+ false ->
+ ConnectCard(
+ modifier = modifier,
+ onDetails = { context.startActivity(Intent(context, HealthConnectRationaleActivity::class.java)) },
+ onConnect = { permissionLauncher.launch(HealthConnectManager.PERMISSIONS) },
+ )
true -> {
if (workouts.isEmpty()) return
Column(
@@ -163,6 +172,7 @@ fun DetectedWorkoutCarousel(
@Composable
private fun ConnectCard(
modifier: Modifier,
+ onDetails: () -> Unit,
onConnect: () -> Unit,
) {
OutlinedCard(
@@ -202,7 +212,14 @@ private fun ConnectCard(
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
) {
+ // The rationale is one tap away *before* the system dialog, not only from inside
+ // Health Connect: the user should be able to read what Amethyst reads and why
+ // before deciding to grant anything.
+ TextButton(onClick = onDetails) {
+ Text(stringRes(Res.string.workout_suggestion_connect_details))
+ }
Button(onClick = onConnect) {
Text(stringRes(Res.string.workout_suggestion_connect_button))
}
diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml
index f4b8e746c9..a67ef65287 100644
--- a/amethyst/src/main/res/values/strings.xml
+++ b/amethyst/src/main/res/values/strings.xml
@@ -2766,4 +2766,9 @@
Value-for-Value
+
+
+ Health Connect and Amethyst
diff --git a/commons/src/commonMain/composeResources/values/strings.xml b/commons/src/commonMain/composeResources/values/strings.xml
index 2fc100f8f9..6cee50140c 100644
--- a/commons/src/commonMain/composeResources/values/strings.xml
+++ b/commons/src/commonMain/composeResources/values/strings.xml
@@ -666,6 +666,23 @@
Connect
%1$s km
From Health Connect
+ What Amethyst reads
+ Health Connect and Amethyst
+ Amethyst reads finished workouts so it can pre-fill a workout post for you.
+ Amethyst is a Nostr social client. Its Workouts section lets you publish a summary of a finished workout to the Nostr relays you choose, so the people who follow you can see what you did. Instead of typing every number by hand, Amethyst can read the workout your watch or fitness app already saved to Health Connect and pre-fill the post. You always see the pre-filled post and decide whether to publish it.
+ What Amethyst reads, and why
+ Exercise · which activity you did, when it started and how long it lasted — this is the workout itself, and the title and duration of the post.
+ Distance · how far you went, shown as the distance of the run, ride, walk or swim.
+ Active and total calories · the energy the workout burned. Active calories are used when your source records them; total calories are the fallback for sources that only record total energy.
+ Heart rate · the average and maximum heart rate over the workout, the standard measure of how hard the effort was.
+ Steps · the step count of a run, walk or hike.
+ Elevation gained · how much you climbed, which is what separates a flat ride from a hilly one.
+ What Amethyst does not do
+ Only reads workouts that finished in the last 7 days, and only while the Workouts composer is open. It never reads in the background.
+ Never writes anything to Health Connect, and never asks for your exercise route, location or any other health data type.
+ Nothing leaves your phone until you tap a suggestion and publish the post yourself. Amethyst has no server: the post goes to the Nostr relays you configured.
+ The whole feature is optional. Turn it off under Settings → Compose, or revoke the permissions in Health Connect at any time — the rest of Amethyst keeps working.
+ Read the full privacy policy
No nSites found yet.
Reload
The in-app browser needs Android 11 or newer.
diff --git a/docs/health-connect-play-declaration.md b/docs/health-connect-play-declaration.md
new file mode 100644
index 0000000000..5bc2ed8b12
--- /dev/null
+++ b/docs/health-connect-play-declaration.md
@@ -0,0 +1,168 @@
+# Health Connect — Play Console declaration
+
+Source text for the Health Connect permissions declaration in Play Console, written against what
+`HealthConnectManager` actually does. Keep this file and the declaration in sync: Google re-reviews
+the declaration on every Health Connect permission change.
+
+Related code and copy:
+
+- `amethyst/src/main/java/com/vitorpamplona/amethyst/service/workouts/health/HealthConnectManager.kt` — the only place the app touches Health Connect.
+- `amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/DetectedWorkoutCarousel.kt` — the only UI that calls it.
+- `amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/health/HealthConnectRationaleActivity.kt` — the in-app rationale screen Health Connect links to.
+- `PRIVACY.md` § "Health and fitness data (Health Connect)" — the public policy the declaration points at.
+
+---
+
+## 1. App functionality
+
+> Amethyst is a social media client for the Nostr protocol — an open, decentralized social network.
+> Users post updates and their followers read them. There is no Amethyst server and no Amethyst
+> account: posts are signed on the device and sent to the public relay servers the user chooses.
+>
+> One of the things users post is a workout summary. Amethyst's Workouts section publishes a
+> structured workout post (a NIP-101e "kind 1301" event) — the activity, when it happened, how long
+> it lasted, and the metrics that describe the effort — so the people who follow the user can see
+> what they did, congratulate them, and compare with their own. This is the fitness equivalent of
+> sharing a run on Strava or a ride on Garmin Connect, except the post goes to the user's own
+> chosen relays instead of a company's servers.
+>
+> Health Connect is used for exactly one thing: to fill that post in. Without it the user has to
+> retype numbers their watch already recorded — activity, duration, distance, calories, heart rate,
+> steps, climb — which is slow and error-prone enough that most people simply do not post. With it,
+> the composer shows the workouts that finished in the last 7 days as one-tap cards; tapping one
+> pre-fills the form, and the user then edits and decides whether to publish.
+
+## 2. Reviewer walkthrough
+
+> 1. Install and open Amethyst, and sign in (a new key can be generated in-app).
+> 2. Open the navigation drawer (hamburger, top-left) and, under the **Feeds** section, tap
+> **Workouts**.
+> 3. Tap the **+** button.
+> 4. At the top of the composer is a card titled **"Share your workouts"**. Tap **What Amethyst
+> reads** to see the in-app rationale screen listing each data type and its purpose, then
+> **Connect** to trigger the Health Connect permission request.
+> 5. Grant the permissions. The card is replaced by a horizontal list of the workouts Health Connect
+> holds from the last 7 days, labelled **"From Health Connect"**.
+> 6. Tap any workout. The composer below is pre-filled with its activity type, title, duration,
+> distance, calories, heart rate, steps and elevation gain, ready to edit and publish.
+>
+> Note for testing on an emulator or a fresh device: the carousel only appears once Health Connect
+> actually holds a finished exercise session from the last 7 days, written by some fitness app or
+> watch. With an empty Health Connect database the composer correctly shows nothing.
+
+## 3. Per-permission justification
+
+Paste one row per permission into the corresponding field.
+
+### READ_EXERCISE — ExerciseSession (and CyclingPedalingCadence)
+
+> This is the workout itself and the anchor for everything else. Amethyst reads the exercise
+> session's activity type, start time and end time, and turns them into the post's activity, date
+> and duration: "Running, 42:15, yesterday". The session's time window is also what every other
+> metric below is aggregated over, so without this permission the feature cannot exist at all — the
+> app would have no notion of "a workout" to attach numbers to. The session's own title, when the
+> source app set one, becomes the suggested post title.
+>
+> Benefit to the user: the workout their watch recorded appears as a one-tap suggestion instead of a
+> blank form.
+>
+> Note on CyclingPedalingCadence: Health Connect grants that data type under the same
+> READ_EXERCISE permission. Amethyst does not read, store, or publish cadence — it reads only
+> ExerciseSessionRecord. There is no separate permission available to request one without the other.
+
+### READ_DISTANCE — Distance
+
+> Amethyst aggregates the distance recorded over the workout's time window and fills it into the
+> distance field of the post. Distance is the single most important number in a running, cycling,
+> walking, hiking, rowing or swimming post — "5.2 km" is what the post is about, and a shared
+> workout without it is largely meaningless to the people reading it.
+>
+> Benefit to the user: they do not have to look up and retype the distance their watch already
+> measured, and the figure published is the accurate recorded one rather than a remembered estimate.
+
+### READ_ACTIVE_CALORIES_BURNED — ActiveCaloriesBurned
+
+> Amethyst aggregates active calories over the workout's time window and fills in the post's energy
+> field. Active calories (energy burned by the activity, excluding resting metabolism) are the
+> correct figure for describing a workout, and the one other fitness apps and Nostr fitness clients
+> publish, so using it keeps Amethyst's posts comparable with theirs.
+>
+> Benefit to the user: the effort figure in their post is the one their device computed, and it is
+> filled in automatically.
+
+### READ_TOTAL_CALORIES_BURNED — TotalCaloriesBurned
+
+> Fallback for the field above. Not every source writes ActiveCaloriesBurned — several popular
+> watches and fitness apps record only total energy for a session. When active calories are absent,
+> Amethyst uses total calories for the same field so the energy figure is not simply blank for those
+> users. When active calories are present they are always preferred, because total calories include
+> basal burn and would over-report the workout.
+>
+> Benefit to the user: the feature works consistently regardless of which watch or fitness app they
+> use, instead of silently dropping a metric for a subset of devices.
+
+### READ_HEART_RATE — HeartRate
+
+> Amethyst aggregates the average and maximum heart rate over the workout's time window and fills in
+> the post's two heart-rate fields. Heart rate is the standard measure of how hard an effort was and
+> is what makes two workouts of the same distance comparable — an easy recovery run and a hard
+> tempo run look identical without it. It is a headline field of the NIP-101e workout post format
+> Amethyst publishes.
+>
+> Benefit to the user: their followers can see how hard the session actually was, not just how far
+> it went, without the user transcribing two more numbers by hand.
+
+### READ_STEPS — Steps (and StepsCadence)
+
+> Amethyst aggregates the step count over the workout's time window and fills in the post's steps
+> field. For walking, running and hiking posts the step count is a primary metric — for a walk it is
+> often the metric the user cares about most — and it is one of the fields of the workout post
+> format.
+>
+> Benefit to the user: walk, run and hike posts carry the step count automatically.
+>
+> Note on StepsCadence: Health Connect grants that data type under the same READ_STEPS permission.
+> Amethyst does not read, store, or publish cadence — it reads only the aggregated step count.
+> There is no separate permission available to request one without the other.
+
+### READ_ELEVATION_GAINED — ElevationGained
+
+> Amethyst aggregates elevation gained over the workout's time window and fills in the post's climb
+> field. Elevation is what distinguishes a flat ride or run from a hilly one — 30 km with 800 m of
+> climbing is a completely different effort from 30 km on the flat — and it is the defining metric
+> of a hiking post. It is one of the fields of the workout post format.
+>
+> Benefit to the user: hill and trail workouts are described accurately in the post rather than
+> looking like flat ones.
+
+## 4. Scope and data handling (state this alongside the table above)
+
+> - **Read-only.** Amethyst holds no write permissions and never writes to Health Connect.
+> - **Foreground only.** Reads happen only while the New Workout composer is on screen, in direct
+> response to the user opening it. Amethyst does not request READ_HEALTH_DATA_IN_BACKGROUND and
+> has no background worker, service or job that touches health data.
+> - **Last 7 days only.** Only sessions finishing in the previous 7 days are read. Amethyst does not
+> request READ_HEALTH_DATA_HISTORY.
+> - **No location.** Amethyst does not request READ_EXERCISE_ROUTE and never receives the GPS track
+> of a workout.
+> - **No transmission without an explicit user action.** Health data is used to populate an on-screen
+> form. Nothing leaves the device unless the user taps a suggestion, reviews the pre-filled post,
+> and publishes it — at which point the post goes to the Nostr relays that user configured. The
+> developer operates no server, so no health data is ever received, stored or processed by the
+> developer or any third party on the developer's behalf.
+> - **No secondary use.** Health data is never used for advertising, analytics, profiling, marketing
+> or sale, is never shared with data brokers or information-resellers, and is never used for
+> determining eligibility for insurance, credit or employment.
+> - **No persistence beyond the session.** Suggestions are held in memory while the composer is open.
+> The only thing stored is whatever the user chose to publish, as an ordinary post.
+> - **Revocable, and optional.** The user can turn the feature off at Settings → Compose Settings →
+> "Suggest workouts to share", or revoke the permissions in Health Connect, at any time; the rest
+> of the app is unaffected.
+> - **In-app rationale.** Amethyst handles both ACTION_SHOW_PERMISSIONS_RATIONALE (Android 13 and
+> below) and ACTION_VIEW_PERMISSION_USAGE + CATEGORY_HEALTH_PERMISSIONS (Android 14+), showing a
+> screen that lists each data type, its purpose, and a link to the full privacy policy. The same
+> screen is reachable in-app from the "What Amethyst reads" link on the Connect card, before the
+> permission request.
+> - **Privacy policy:** https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md — see the
+> section "Health and fitness data (Health Connect)", which lists every data type, its purpose, and
+> each of the limits above.
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index 011c0e9fd2..5f11d74d80 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -5,3 +5,31 @@ is an open protocol that is able to create a censorship-resistant global "social
notes and other stuff using relays. It doesn't rely on any trusted central server, hence it is resilient;
it is based on cryptographic keys and signatures, so it is tamperproof; it does not rely on P2P techniques,
therefore it works.
+What you can do with Amethyst
+
+- Post text, photos, videos, long-form articles, polls and voice notes to the relays you choose.
+- Follow people, reply, repost, react and quote.
+- Send private, end-to-end encrypted direct messages and join group chats.
+- Browse dedicated feeds: articles, pictures, short videos, live streams, podcasts, music, communities,
+classifieds, calendars and more.
+- Send and receive Bitcoin Lightning zaps.
+- Share your workouts — see below.
+
+Workouts
+Amethyst has a Workouts section where you can publish a summary of a run, ride, walk, hike, swim, row
+or gym session so the people who follow you can see what you did. Open the left drawer, tap
+Workouts, then the + button to write one.
+Typing every number by hand is tedious, so Amethyst can pre-fill the post from a workout your watch or
+fitness app already saved to Android Health Connect (Samsung Health, Google Fit, Fitbit, Garmin Connect,
+Strava and others all write there). With your permission, Amethyst reads finished workouts from the last
+7 days and offers them as one-tap suggestions: pick one and the activity, duration, distance, calories,
+heart rate, steps and elevation gain are filled in for you. You review the post and decide whether to
+publish it.
+This is optional and read-only. Amethyst asks for Health Connect permission only when you open the New
+Workout composer, never reads in the background, never writes to Health Connect, never asks for your
+exercise route or location, and never uploads anything until you publish a post yourself. You can switch
+it off under Settings → Compose Settings, or revoke access in Health Connect at any time. Full
+details are in the privacy policy at
+github.com/vitorpamplona/amethyst.
+Amethyst is free and open source (MIT). There is no Amethyst server and no Amethyst account: your
+posts go straight to the Nostr relays you configure.