Skip to content

Commit b70c345

Browse files
committed
feat: Microsoft Dev Tunnels
1 parent 455fe1a commit b70c345

10 files changed

Lines changed: 346 additions & 56 deletions

File tree

README.md

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
1010
[![Monthly Downloads](https://pepy.tech/badge/flaredantic/month)](https://pepy.tech/project/flaredantic)
1111

12-
Flaredantic is a Python library that simplifies the process of creating tunnels to expose your local services to the internet. It supports both Cloudflare and Serveo tunneling services, making it a user-friendly alternative to ngrok, localtunnel, and similar tools.
12+
Flaredantic is a Python library that simplifies the process of creating tunnels to expose your local services to the internet. It supports Cloudflare, Serveo, and Microsoft Dev Tunnel services, making it a user-friendly alternative to ngrok, localtunnel, and similar tools.
1313

1414
</div>
1515

@@ -20,7 +20,7 @@ Flaredantic is a Python library that simplifies the process of creating tunnels
2020
- 🚀 Easy-to-use Python API
2121
- 💻 Command-line interface (CLI)
2222
- 📦 Automatic binary management
23-
- 🔄 Multiple tunnel providers (Cloudflare, Serveo)
23+
- 🔄 Multiple tunnel providers (Cloudflare, Serveo, Microsoft DevTunnel)
2424
- 🌐 TCP forwarding support (Serveo)
2525
- 🎯 Cross-platform support (Windows, macOS, Linux)
2626
- 📱 Android support via Termux
@@ -60,6 +60,9 @@ flare --port 8080 -v
6060
# Use Serveo tunnel instead
6161
flare --port 8080 --tunnel serveo
6262

63+
# Use Microsoft DevTunnel
64+
flare --port 8080 --tunnel microsoft
65+
6366
# TCP forwarding with Serveo
6467
flare --port 5432 --tcp
6568
```
@@ -69,7 +72,7 @@ CLI Options:
6972
-p, --port Local port to expose (required)
7073
-t, --timeout Tunnel start timeout in seconds (default: 30)
7174
-v, --verbose Show detailed progress output
72-
--tunnel Tunnel provider to use [cloudflare, serveo] (default: cloudflare)
75+
--tunnel Tunnel provider to use [cloudflare, serveo, microsoft] (default: cloudflare)
7376
--tcp Use Serveo with TCP forwarding (overrides --tunnel)
7477
```
7578

@@ -101,6 +104,19 @@ with ServeoTunnel(config) as tunnel:
101104
input("Press Enter to stop the tunnel...")
102105
```
103106

107+
#### Basic Usage with Microsoft DevTunnel
108+
109+
```python
110+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
111+
112+
# Create a tunnel using Microsoft DevTunnel
113+
config = MicrosoftConfig(port=8080)
114+
with MicrosoftTunnel(config) as tunnel:
115+
print(f"Your service is available at: {tunnel.tunnel_url}")
116+
# Your application code here
117+
input("Press Enter to stop the tunnel...")
118+
```
119+
104120
#### TCP Forwarding with Serveo
105121

106122
```python
@@ -119,6 +135,7 @@ with ServeoTunnel(config) as tunnel:
119135
```python
120136
from flaredantic import FlareTunnel, FlareConfig
121137
from flaredantic import ServeoTunnel, ServeoConfig
138+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
122139
from pathlib import Path
123140

124141
# Configure Cloudflare tunnel with custom settings
@@ -137,8 +154,18 @@ serveo_config = ServeoConfig(
137154
verbose=True # Enable detailed logging
138155
)
139156

157+
# Configure Microsoft DevTunnel with custom settings
158+
microsoft_config = MicrosoftConfig(
159+
port=8080,
160+
bin_dir=Path.home() / ".my-tunnels",
161+
timeout=60,
162+
verbose=True, # Enable detailed logging
163+
tunnel_id="flaredantic", # Custom tunnel ID
164+
device_login=True # Use device login flow
165+
)
166+
140167
# Create and start tunnel (choose one)
141-
with FlareTunnel(cloudflare_config) as tunnel:
168+
with MicrosoftTunnel(microsoft_config) as tunnel:
142169
print(f"Access your service at: {tunnel.tunnel_url}")
143170
input("Press Enter to stop the tunnel...")
144171
```
@@ -189,10 +216,23 @@ if __name__ == '__main__':
189216
| verbose | bool | False | Show detailed progress and debug output |
190217
| tcp | bool | False | Enable TCP forwarding instead of HTTP |
191218

219+
### Microsoft DevTunnel Options
220+
221+
| Option | Type | Default | Description |
222+
|--------|------|---------|-------------|
223+
| port | int | Required | Local port to expose |
224+
| bin_dir | Path | ~/.flaredantic | Directory for devtunnel binary |
225+
| timeout | int | 30 | Tunnel start timeout in seconds |
226+
| verbose | bool | False | Show detailed progress and debug output |
227+
| tunnel_id | str | "flaredantic" | Custom tunnel ID |
228+
| device_login | bool | True | Use device login flow |
229+
192230
## 📦 Requirements
193231

194232
- **Cloudflare tunnel**: No additional requirements (binary auto-downloaded)
195233
- **Serveo tunnel**: Requires SSH client to be installed
234+
- **Microsoft DevTunnel**: No additional requirements (binary auto-downloaded)
235+
- **Note**: Currently only supports Linux and macOS.
196236

197237
> **❗️Note:** Serveo servers might occasionally be unavailable as they are a free service. Flaredantic automatically detects when Serveo is down and provides a clear error message. Consider using Cloudflare tunnels if you need guaranteed availability.
198238
@@ -201,5 +241,6 @@ if __name__ == '__main__':
201241
For more detailed examples and use cases, check out our examples:
202242
- [Cloudflare Examples](docs/examples/Cloudflare.md) - HTTP Server, Django, FastAPI, Flask
203243
- [Serveo Examples](docs/examples/Serveo.md) - HTTP, TCP, SSH forwarding, database access
244+
- [Microsoft Examples](docs/examples/Microsoft.md) - HTTP Server, Custom Tunnel ID, Device Login
204245

205-
---
246+
---

docs/examples/Microsoft.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Flaredantic Microsoft DevTunnel Examples 📚
2+
3+
This document provides various examples of how to use Flaredantic with Microsoft DevTunnels in different scenarios.
4+
5+
> **⚠️ Note:** Microsoft DevTunnel currently only supports **Linux** and **macOS** platforms. Windows support is not yet available in this library.
6+
7+
## Basic Examples
8+
9+
### Simple HTTP Server
10+
```python
11+
from http.server import HTTPServer, SimpleHTTPRequestHandler
12+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
13+
import threading
14+
import time
15+
16+
# Create a basic HTTP server
17+
server = HTTPServer(('localhost', 8000), SimpleHTTPRequestHandler)
18+
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
19+
server_thread.start()
20+
21+
# Create and start tunnel
22+
config = MicrosoftConfig(port=8000)
23+
with MicrosoftTunnel(config) as tunnel:
24+
print(f"Server accessible at: {tunnel.tunnel_url}")
25+
try:
26+
while True:
27+
time.sleep(1)
28+
except KeyboardInterrupt:
29+
print("\nStopping server...")
30+
```
31+
32+
### Custom Tunnel ID
33+
You can reuse a specific tunnel ID if available:
34+
```python
35+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
36+
37+
config = MicrosoftConfig(
38+
port=8080,
39+
tunnel_id="my-custom-tunnel-id",
40+
verbose=True
41+
)
42+
43+
with MicrosoftTunnel(config) as tunnel:
44+
print(f"Tunnel URL: {tunnel.tunnel_url}")
45+
input("Press Enter to stop...")
46+
```
47+
48+
## Account Management
49+
50+
### Changing Accounts / Logging Out
51+
If you need to switch accounts or force a re-login, you can manually use the binary that `flaredantic` manages.
52+
53+
The binary is located in `~/.flaredantic/microsoft` (or your custom `bin_dir`).
54+
55+
**To logout:**
56+
```bash
57+
# Navigate to the binary directory
58+
cd ~/.flaredantic/
59+
60+
# Run the logout command
61+
./microsoft user logout
62+
```
63+
64+
After logging out, the next time you run your Python script with `MicrosoftTunnel`, it will prompt you for a new device login.
65+
66+
**To check current login status:**
67+
```bash
68+
cd ~/.flaredantic/
69+
./microsoft user show
70+
```
71+
72+
## Advanced Examples
73+
74+
### Device Login Flow
75+
Microsoft DevTunnels support device login authentication (enabled by default):
76+
```python
77+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
78+
79+
config = MicrosoftConfig(
80+
port=8080,
81+
device_login=True, # Enable device login flow
82+
verbose=True # Show login code and instructions
83+
)
84+
85+
# First run will prompt: "Browse to https://github.com/login/device and enter code: XXXX"
86+
with MicrosoftTunnel(config) as tunnel:
87+
print(f"Tunnel URL: {tunnel.tunnel_url}")
88+
input("Press Enter to stop...")
89+
```
90+
91+
### FastAPI Integration
92+
```python
93+
import uvicorn
94+
from fastapi import FastAPI
95+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
96+
import threading
97+
98+
app = FastAPI()
99+
100+
@app.get("/")
101+
def read_root():
102+
return {"status": "online", "provider": "Microsoft DevTunnel"}
103+
104+
def start_tunnel():
105+
config = MicrosoftConfig(port=8000)
106+
tunnel = MicrosoftTunnel(config)
107+
url = tunnel.start()
108+
print(f"FastAPI app available at: {url}")
109+
return tunnel
110+
111+
if __name__ == "__main__":
112+
# Start tunnel in background
113+
tunnel = start_tunnel()
114+
115+
try:
116+
uvicorn.run(app, host="127.0.0.1", port=8000)
117+
finally:
118+
tunnel.stop()
119+
```
120+
121+
### Django Integration
122+
```python
123+
import os
124+
import sys
125+
import time
126+
import threading
127+
from flaredantic import MicrosoftTunnel, MicrosoftConfig
128+
from django.core.management import execute_from_command_line
129+
130+
def run_tunnel():
131+
config = MicrosoftConfig(port=8000)
132+
with MicrosoftTunnel(config) as tunnel:
133+
print(f"Django site available at: {tunnel.tunnel_url}")
134+
# Keep tunnel alive
135+
try:
136+
while True:
137+
time.sleep(1)
138+
except KeyboardInterrupt:
139+
pass
140+
141+
if __name__ == "__main__":
142+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
143+
144+
# Start tunnel in background
145+
tunnel_thread = threading.Thread(target=run_tunnel, daemon=True)
146+
tunnel_thread.start()
147+
148+
execute_from_command_line(sys.argv)
149+
```
150+
151+
## Error Handling Examples
152+
153+
### Connection Retry Logic
154+
```python
155+
from flaredantic import MicrosoftTunnel, MicrosoftConfig, TunnelError
156+
import time
157+
158+
def create_tunnel_with_retry(port: int, max_retries: int = 3):
159+
config = MicrosoftConfig(port=port, verbose=True)
160+
161+
for attempt in range(max_retries):
162+
try:
163+
tunnel = MicrosoftTunnel(config)
164+
tunnel.start()
165+
return tunnel
166+
except TunnelError as e:
167+
if attempt == max_retries - 1:
168+
raise
169+
print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
170+
time.sleep(2)
171+
```
172+
173+
## Common Issues and Solutions
174+
175+
### Login Required
176+
If the tunnel fails to start with login errors:
177+
1. Ensure `verbose=True` is set to see the login code.
178+
2. Complete the device login flow at https://github.com/login/device.
179+
3. The token is cached locally, so subsequent runs won't require login.
180+
181+
### Port Conflicts
182+
If you see "hosting port" errors:
183+
- Ensure the local service is running on the specified port.
184+
- Check if another tunnel is already using the same `tunnel_id`.

flaredantic/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .tunnel.cloudflare import FlareTunnel, FlareConfig
22
from .tunnel.serveo import ServeoTunnel, ServeoConfig
3-
from .tunnel.devtunnel import DevTunnel, DevTunnelConfig
3+
from .tunnel.microsoft import MicrosoftTunnel, MicrosoftConfig
44
from .core.exceptions import (
55
CloudflaredError,
66
DownloadError,
@@ -23,9 +23,9 @@
2323
"ServeoTunnel",
2424
"ServeoConfig",
2525

26-
# DevTunnel provider
27-
"DevTunnel",
28-
"DevTunnelConfig",
26+
# Microsoft provider
27+
"MicrosoftTunnel",
28+
"MicrosoftConfig",
2929

3030
# Exceptions
3131
"CloudflaredError",
@@ -36,4 +36,4 @@
3636

3737
# Version
3838
"__version__",
39-
]
39+
]

flaredantic/cli.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44
from . import FlareTunnel, FlareConfig
55
from . import ServeoTunnel, ServeoConfig
6-
from . import DevTunnel, DevTunnelConfig
6+
from . import MicrosoftTunnel, MicrosoftConfig
77

88
def signal_handler(sig, frame):
99
sys.exit(0)
@@ -37,7 +37,7 @@ def main():
3737

3838
parser.add_argument(
3939
"--tunnel",
40-
choices=["cloudflare", "serveo", "devtunnel"],
40+
choices=["cloudflare", "serveo", "microsoft"],
4141
default="cloudflare",
4242
help="Tunnel provider to use"
4343
)
@@ -74,13 +74,13 @@ def main():
7474
tcp=args.tcp
7575
)
7676
tunnel = ServeoTunnel(config)
77-
else: # devtunnel
78-
config = DevTunnelConfig(
77+
else: # microsoft
78+
config = MicrosoftConfig(
7979
port=args.port,
8080
timeout=args.timeout,
8181
verbose=args.verbose
8282
)
83-
tunnel = DevTunnel(config)
83+
tunnel = MicrosoftTunnel(config)
8484

8585
try:
8686
with tunnel:
@@ -91,4 +91,4 @@ def main():
9191
sys.exit(1)
9292

9393
if __name__ == "__main__":
94-
main()
94+
main()

flaredantic/tunnel/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from .cloudflare import FlareTunnel, FlareConfig
22
from .serveo import ServeoTunnel, ServeoConfig
3-
from .devtunnel import DevTunnel, DevTunnelConfig
3+
from .microsoft import MicrosoftTunnel, MicrosoftConfig
44

55
__all__ = [
66
# Cloudflare
@@ -11,7 +11,7 @@
1111
"ServeoTunnel",
1212
"ServeoConfig",
1313

14-
# DevTunnel
15-
"DevTunnel",
16-
"DevTunnelConfig"
17-
]
14+
# Microsoft
15+
"MicrosoftTunnel",
16+
"MicrosoftConfig"
17+
]

0 commit comments

Comments
 (0)