1. System Overview
DTech is an innovative Android application built with Flutter that transforms a mobile device into an active web server. It allows users to host and serve a static website (HTML, CSS, JS, Images, etc.) directly from their phone's local storage.
What makes DTech uniquely powerful is its dual-mode accessibility:
1. Local Wi-Fi Mode: Devices on the same Wi-Fi network can access the hosted website directly via the phone's local IP address.
2. Global Internet Tunneling Mode: Users anywhere in the world can access the phone's local web server through a public subdomain without the need for complex port forwarding, proxy servers, or centralized relay points (like Ngrok).
The Core Problem Solved:
Mobile networks often use Carrier-Grade NAT (CGNAT) and strict firewalls, making it impossible to expose a local port on a mobile phone to the public internet using traditional methods. Cloudflare tunnels often face Anycast data center isolation issues. DTech bypasses these limitations by establishing a direct Peer-to-Peer (P2P) WebRTC connection between the visitor's web browser and the Flutter application on the phone. This allows HTTP traffic to be tunneled seamlessly over WebRTC data channels, turning the browser itself into a proxy.
---
2. Architecture & How It Works
The architecture of DTech is divided into two major components: the Local Web Server (on the phone) and the WebRTC Tunnel (the bridge between the global internet and the phone).
A. The Local Web Server
When a user selects a folder and starts the server via the Flutter UI, the app utilizes the Dart shelf and shelf_static packages to bind an HTTP server to port 8080. This server listens for incoming HTTP requests (GET, POST, etc.) and serves the static files from the selected directory. This handles the core file-hosting functionality.
B. The WebRTC Tunnel (The Magic)
When global tunneling is enabled, the app establishes an internet gateway. This involves several coordinated moving parts:
1. Signaling via Firebase Realtime Database:
Because the visitor's browser and the Android phone do not initially know how to connect to each other across the internet, they use a Firebase Realtime Database as a "Signaling Server". They exchange Session Description Protocol (SDP) "Offers" and "Answers", as well as ICE Candidates (which map out potential network paths) through this central database. Once they find a valid path (often using public STUN servers provided by Google), the direct P2P connection is established, and Firebase steps out of the way.
2. The Cloudflare Worker App Shell (worker.js):
When a visitor navigates to the user's public subdomain (managed by Cloudflare), a Cloudflare Worker intercepts the request. Instead of serving the website directly (which it can't, because the files are on the phone), the Worker serves an "App Shell". This is a basic HTML file containing JavaScript.
- This App Shell connects to Firebase, initiates the WebRTC signaling process with the phone, and establishes a secure
RTCDataChannel. - Once connected, it registers a Service Worker (
sw.js). - It then renders a full-screen
<iframe>that reloads the requested URL.
3. The Service Worker Interceptor (sw.js):
The Service Worker runs in the background of the visitor's browser. Its job is to intercept *every single HTTP request* (images, CSS, API calls) made by the <iframe> (the actual website being viewed).
- Instead of letting the browser fetch the asset from the internet, the Service Worker pauses the request.
- It bundles the HTTP method, URL, and headers into a JSON object and sends it via
postMessageto the App Shell.
4. Data Serialization & Transport:
The App Shell receives the intercepted request. If there is a request body (e.g., a form upload), it base64 encodes it. It formats the request into a string (e.g., requestId|method|path|headersJSON|bodyStr) and sends it across the internet directly to the phone via the WebRTC RTCDataChannel.
5. The Flutter Proxy (WebRTCTunnelService):
Inside the Flutter app, the WebRTCTunnelService listens to the WebRTC Data Channel.
- It receives the custom formatted string from the browser.
- It deserializes the string back into an HTTP Request.
- It removes conflicting headers (like the
Hostheader) and executes the request against the phone's internalshelflocal server (http://127.0.0.1:8080). - The
shelfserver processes the request and returns the file/response. - Chunking: Because WebRTC data channels have a strict message size limit (typically 64KB), the Flutter app reads the HTTP response body in 64KB chunks.
- It sends the response headers back over WebRTC, followed by binary messages containing the file chunks, marked with flags to indicate the final chunk.
6. Reassembly and Browser Delivery:
The App Shell in the browser receives the chunks, stitches the binary file back together, and passes it to the Service Worker. The Service Worker then completes the intercepted request, handing the final file over to the browser's rendering engine. To the end-user, it feels exactly like browsing a normal website, entirely unaware that the assets are being streamed P2P from a mobile device.
---
3. Detailed File-by-File Analysis
The Global Gateway
#### worker.js (Cloudflare Worker)
This is the single entry point for external traffic. It serves two distinct roles based on the requested URL:
- Service Worker Generator: If the path is
/sw.js, it dynamically generates and serves the Service Worker script. This script utilizes theFetchEventto intercept all network requests, sending them viaclients.matchAll()back to the parent window (the App Shell). - App Shell Generator: For all other paths, it serves the main HTML file. This HTML file loads the Firebase JS SDK, manages the RTCPeerConnection, handles the complex logic of reassembling 64KB binary chunks received over WebRTC, and manages the invisible
<iframe>where the actual website lives.
The Flutter Core (lib/core/)
#### server_manager.dart
This is the central "brain" of the application state. It extends ChangeNotifier so the UI can react to state changes.
- Responsibilities: It initializes the
shelfHTTP server, binds it to all IPv4 interfaces (InternetAddress.anyIPv4) on port 8080, and configures the static file handler pointing to the user-selected folder. - Integration: It wires together the
VisitorTracker(adding a middleware pipeline toshelfto log requests) and initializes theWebRTCTunnelServiceif global access is enabled. It also interfaces withPreferencesServiceto load auto-start settings.
#### webrtc_tunnel_service.dart
The most complex and vital component of the app's backend.
- Signaling: It connects to the Firebase Realtime Database. It listens for incoming
offerevents from new visitors under the user's specific subdomain path. It replies withanswerevents and exchanges ICE candidates. - Proxy Logic: Once a WebRTC
RTCDataChannelis open, it listens to theonMessageevent. It parses the incoming stringrequestId|method|path|headersJSON|body, constructs an internalhttp.Request, and fires it at127.0.0.1:8080. - Binary Streaming: After getting the response from the local server, it implements a custom chunking protocol. It loops through the response byte array, extracting 64KB chunks, prepending a binary header (containing the Request ID length, the Request ID itself, and an
isLastboolean flag), and pushes it over the WebRTC channel.
#### network_utils.dart
A utility class dedicated to discovering the phone's Local Area Network (LAN) IP address. It iterates through the device's network interfaces (looking for wlan, en, or eth), ignoring loopback addresses, to find the IPv4 address so the UI can display instructions for local Wi-Fi access.
#### visitor_tracker.dart
A simple state management class (ChangeNotifier) that maintains a counter of unique visitors (currently basic, incrementing when / or index.html is requested) and keeps a rolling list of the last 50 HTTP requests (e.g., GET /style.css) to be displayed in the Dashboard UI logs.
#### preferences_service.dart
A wrapper around the shared_preferences package. It provides asynchronous getter and setter methods to persist user configurations across app restarts, such as the selected website folder path, the chosen subdomain, and the auto-start/tunnel toggles.
The Application UI
#### lib/main.dart
The entry point of the Flutter application.
- Setup: It ensures Flutter bindings are initialized, initializes Firebase (
Firebase.initializeApp()), and loads local preferences. - State Provisioning: It sets up the
MultiProviderarchitecture, injectingVisitorTrackerandServerManagerinto the widget tree so they can be accessed anywhere. - Theming: It defines the global Material 3 design language, setting the dark theme, the Deep Blue primary color (
#1565C0), and the Cyan secondary accent (#00BCD4).
#### lib/features/dashboard/dashboard_screen.dart
The primary and only screen of the application.
- Interactivity: It utilizes
file_pickerandpermission_handlerto allow the user to grant storage access and select the directory they wish to host. - Controls: It provides toggle switches to start/stop the server and enable/disable the Global Tunnel. It includes a text field to set the public subdomain.
- Data Visualization: It dynamically displays the local IP address, the public web address, server uptime, total visitor count, and a
ListView.builderthat renders the live scrolling log of incoming HTTP requests managed by theVisitorTracker. It also usesqr_flutterto generate a QR code for easy local access sharing.