Files
buzz/mobile/lib/shared/push/push_relay_capability_provider.dart
Tom Brow 76f7e5ef0c feat(push): make gateway origin configurable (#7261)
Make the push gateway origin configurable across the relay, gateway,
mobile client, and deployment chart.

This is a fresh deployment. The gateway has only run on personal
development infrastructure, and push has no existing enabled users.
Legacy migration, configured-to-unconfigured mobile transitions, and
rolling upgrades from pre-launch gateway binaries are not supported.

Populated pre-launch databases are rejected with an informative error
before migrations run; their data is untouched. Mobile builds may omit
the gateway URL, leaving push unavailable. Once push is enabled,
subsequent builds must retain that configuration.

### Validation

- All applicable CI checks pass at
`5f546cfaf403f0386f146d213adfb4392f4ec29c`, including Rust,
mobile/Swift, desktop/integration, security, image builds, and Helm
checks. An unrelated agent cancellation test passed on retry.
- PostgreSQL CI passed 384 tests, including the production-entry-point
regression for refusing populated pre-launch databases before
migrations, fresh initialization, and subsequent initialized
deployments.
- Focused local validation passed all 10 gateway PostgreSQL tests under
the CI discovery filter and all 3 unconfigured-mobile tests.
Repository-wide PostgreSQL discovery validation and the complete
pre-push hooks passed.
- Physical-device APNs/App Attest behavior and deployment to a live
cluster were not verified in this work.

---------

Signed-off-by: Tom Brow <tomb@block.xyz>
Co-authored-by: Codex <noreply@openai.com>
2026-09-15 14:56:45 -07:00

64 lines
2.2 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../community/community_provider.dart';
import '../relay/relay_provider.dart';
import '../relay/relay_session.dart';
import 'dev_push_lease.dart';
typedef BuzzPushDescriptorFetcher =
Future<BuzzPushLeaseDescriptor> Function(String relayBaseUrl);
final buzzPushDescriptorFetcherProvider = Provider<BuzzPushDescriptorFetcher>(
(ref) => fetchBuzzPushLeaseDescriptor,
);
/// The fully validated push capability advertised by the current relay.
///
/// An unconfigured artifact has no capability and never starts discovery.
/// Discovery fails closed. An absent, malformed, or unreachable NIP-11 push
/// descriptor is represented as no capability, so no notification permission,
/// APNs registration, gateway enrollment, or relay lease can begin.
final currentRelayPushDescriptorProvider =
FutureProvider.autoDispose<BuzzPushLeaseDescriptor?>((ref) async {
if (!Env.pushGatewayConfigured) return null;
final session = ref.watch(relaySessionProvider);
final config = ref.watch(relayConfigProvider);
final community = ref.watch(activeCommunityProvider).value;
final memberPubkey = ref.watch(myPubkeyProvider);
if (session.status != SessionStatus.connected ||
community == null ||
config.nsec == null ||
config.nsec!.isEmpty ||
memberPubkey == null ||
memberPubkey.isEmpty) {
return null;
}
return discoverBuzzPushRelayCapability(
config.baseUrl,
fetchDescriptor: ref.read(buzzPushDescriptorFetcherProvider),
);
});
Future<BuzzPushLeaseDescriptor?> discoverBuzzPushRelayCapability(
String relayBaseUrl, {
required BuzzPushDescriptorFetcher fetchDescriptor,
}) async {
try {
return await fetchDescriptor(relayBaseUrl);
} catch (error, stackTrace) {
debugPrint('Current relay does not advertise valid push: $error');
debugPrintStack(stackTrace: stackTrace);
return null;
}
}
Future<void> startBuzzPushRegistrationIfCapable(
BuzzPushLeaseDescriptor? descriptor, {
required Future<void> Function() startRegistration,
}) async {
if (descriptor == null) return;
await startRegistration();
}