From 91d7d325d45389e9baac7175e44d43a5e3d8a9c2 Mon Sep 17 00:00:00 2001 From: Tom Brow Date: Tue, 20 Oct 2026 15:29:43 -0700 Subject: [PATCH] Prepare explicit bb-block staging prototype environment Signed-off-by: Tom Brow --- mobile/lib/prototype/README.md | 21 ++++++++++ mobile/lib/prototype/owned_communities.dart | 5 ++- .../prototype/remote_agent_controller.dart | 5 ++- .../lib/shared/hosted/hosted_environment.dart | 6 +++ .../hosted/hosted_environment_test.dart | 39 +++++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/mobile/lib/prototype/README.md b/mobile/lib/prototype/README.md index 5e0f462473..d9f347ad7f 100644 --- a/mobile/lib/prototype/README.md +++ b/mobile/lib/prototype/README.md @@ -116,3 +116,24 @@ complete sign-in/current-community readiness, scan the hand-authored demo QR wit Camera, verify no automatic creation, edit/submit, and verify avatar persistence and a real agent response. Source/widget tests and QR image decoding do not prove these physical-device steps. No production or conference-release inclusion. + +## bb-block staging preparation + +The explicit internal staging build target is +`BUZZ_BUILDERLAB_URL=https://test.blockstaging.build`, with +`BUZZ_AUTH_DOMAINS=buzz.test.blockstaging.build,communities.test.blockstaging.build`. +Only the existing `buzz.test.blockstaging.build` relay and community subdomains +map to that account origin. The login origin, push host and arbitrary sibling +hosts are not accepted as community relays. Public staging sessions and +account-scoped identities remain separate; do not transfer tokens across realms. + +This configuration is prepared, not physically validated or installed. The +bb-block edge is WARP-only. Historical BYOD access evidence covers the exact +relay and push hosts, not the login origin. Native OAuth eligibility and the +owner's `ENABLE_BEEKEEPER_AGENT_REGISTRY` flag still need verification. Switching +build targets does not bypass network policy or recover an existing identity. +The prototype's owned-community listing also does not replace normal onboarding's +joined-community discovery and identity recovery. + +For standalone physical testing, use Xcode's **Profile** configuration. Debug +Flutter builds require a debugger and cannot validate normal Home/Camera launch. diff --git a/mobile/lib/prototype/owned_communities.dart b/mobile/lib/prototype/owned_communities.dart index 0457210ae8..a28afc80e7 100644 --- a/mobile/lib/prototype/owned_communities.dart +++ b/mobile/lib/prototype/owned_communities.dart @@ -9,7 +9,10 @@ import '../shared/hosted/hosted_environment.dart'; Future> loadPrototypeOwnedCommunities( BuilderlabAccountClient account, ) async { - if (HostedEnvironment.origin.host != 'app.builderlabstaging.xyz') { + if (!{ + 'app.builderlabstaging.xyz', + 'test.blockstaging.build', + }.contains(HostedEnvironment.origin.host)) { throw StateError('Owned-community prototype requires staging.'); } final response = await account.post('/v1/buzz/communities/list', {}); diff --git a/mobile/lib/prototype/remote_agent_controller.dart b/mobile/lib/prototype/remote_agent_controller.dart index 3b0619d23a..31d94ddb6d 100644 --- a/mobile/lib/prototype/remote_agent_controller.dart +++ b/mobile/lib/prototype/remote_agent_controller.dart @@ -171,7 +171,10 @@ class RemoteAgentController extends ChangeNotifier { Future initialize() => _initialization ??= _initialize(); Future _initialize() async { - if (HostedEnvironment.origin.host != 'app.builderlabstaging.xyz') { + if (!{ + 'app.builderlabstaging.xyz', + 'test.blockstaging.build', + }.contains(HostedEnvironment.origin.host)) { throw StateError( 'This prototype requires the staging build configuration.', ); diff --git a/mobile/lib/shared/hosted/hosted_environment.dart b/mobile/lib/shared/hosted/hosted_environment.dart index a3ca701e53..d574b65f46 100644 --- a/mobile/lib/shared/hosted/hosted_environment.dart +++ b/mobile/lib/shared/hosted/hosted_environment.dart @@ -8,6 +8,7 @@ abstract final class HostedEnvironment { 'communities.buzz.xyz': 'https://app.builderlab.xyz', 'communities.public.builderlabstaging.xyz': 'https://app.builderlabstaging.xyz', + 'communities.test.blockstaging.build': 'https://test.blockstaging.build', }; /// Creation and admission use the same trusted build-specific host mapping. @@ -57,6 +58,10 @@ abstract final class HostedEnvironment { return null; } final host = relay.host.toLowerCase(); + // The existing bb-block staging community predates community subdomains. + if (host == 'buzz.test.blockstaging.build') { + return Uri.parse('https://test.blockstaging.build'); + } for (final entry in _communityAccounts.entries) { if (host.endsWith('.${entry.key}')) return Uri.parse(entry.value); } @@ -75,6 +80,7 @@ abstract final class HostedEnvironment { if (!{ 'https://app.builderlab.xyz', 'https://app.builderlabstaging.xyz', + 'https://test.blockstaging.build', }.contains(configured)) { throw StateError('Unsupported hosted account environment'); } diff --git a/mobile/test/shared/hosted/hosted_environment_test.dart b/mobile/test/shared/hosted/hosted_environment_test.dart index 8afc89f813..296936cb71 100644 --- a/mobile/test/shared/hosted/hosted_environment_test.dart +++ b/mobile/test/shared/hosted/hosted_environment_test.dart @@ -15,7 +15,26 @@ void main() { ), Uri.parse('https://app.builderlabstaging.xyz'), ); + for (final host in [ + 'team.communities.test.blockstaging.build', + 'buzz.test.blockstaging.build', + ]) { + expect( + HostedEnvironment.accountOriginFor(Uri.parse('wss://$host')), + Uri.parse('https://test.blockstaging.build'), + ); + } for (final value in [ + 'wss://test.blockstaging.build', + 'wss://buzz-push.test.blockstaging.build', + 'wss://other.test.blockstaging.build', + 'wss://buzz.test.blockstaging.build.attacker.test', + 'wss://buzz.test.blockstaging.build.', + 'wss://buzz.test.blockstaging.build:8443', + 'wss://user@buzz.test.blockstaging.build', + 'wss://buzz.test.blockstaging.build/path', + 'wss://buzz.test.blockstaging.build?account=other', + 'wss://communities.test.blockstaging.build', 'wss://team.communities.public.builderlabstaging.xyz.attacker.test', 'wss://communities.public.builderlabstaging.xyz', 'wss://team.communities.public.builderlabstaging.xyz.', @@ -35,6 +54,26 @@ void main() { } }); + test('selected build never accepts another account realm', () { + for (final (relay, account) in [ + ('wss://a.communities.buzz.xyz', 'https://app.builderlab.xyz'), + ( + 'wss://a.communities.public.builderlabstaging.xyz', + 'https://app.builderlabstaging.xyz', + ), + ( + 'wss://a.communities.test.blockstaging.build', + 'https://test.blockstaging.build', + ), + ('wss://buzz.test.blockstaging.build', 'https://test.blockstaging.build'), + ]) { + expect( + HostedEnvironment.supportsRelay(Uri.parse(relay)), + HostedEnvironment.origin == Uri.parse(account), + ); + } + }); + test('authentication follows only configured DNS domain boundaries', () { expect(HostedEnvironment.enabled, HostedEnvironment.authDomains.isNotEmpty); for (final domain in HostedEnvironment.authDomains) {