A lightweight proxy that combines your frontend and backend into one link for easy development and sharing.
Project description
Dev Linker
Dev Linker starts your local development stack and routes frontend and backend traffic through one proxy URL, with optional LAN and public sharing.
โก Quick Start (2 Minutes)
Install:
pip install devlinker
Run your apps:
# Backend (example)
uvicorn main:app --reload
# Frontend (example)
npm run dev
Run DevLinker:
devlinker
Open:
http://localhost:8001
Done โ
๐ง How DevLinker Works
Request flow:
Browser -> DevLinker Proxy -> Frontend or Backend
Routing rules:
- / routes to frontend (React/Vite)
- /api/* routes to backend (FastAPI/Flask/Node)
DevLinker acts as a smart proxy and optional tunnel layer for local, LAN, and public development links.
Architecture diagram:
flowchart LR
B[Browser / Mobile] --> P[DevLinker Proxy]
P --> F[Frontend Dev Server\nVite/React]
P --> A[Backend API\nFastAPI/Flask/Node]
P --> T[Optional Tunnel\nCloudflare/ngrok]
๐ฏ Use Cases
- Test APIs and UI flows on mobile devices over WLAN
- Share local work instantly with teammates using one public URL
- Debug frontend-backend integration from a single entrypoint
- Reduce CORS/preflight issues during development
๐ผ๏ธ Demo & Screenshots
- Terminal startup output: add screenshot at docs/images/terminal-startup.png
- Browser app via proxy: add screenshot at docs/images/browser-proxy.png
- Public URL share demo: add screenshot at docs/images/public-url.png
Features
- ๐ Unified Dev Proxy: Combines frontend (Vite/React) and backend (FastAPI/Flask/Node/Docker) into a single local and public URL.
- ๐ Auto Detection: Detects frontend/backend ports, runtime, Docker containers, and Vite servers automatically.
- ๐ก Debug Request Logger: Live API traffic lines (method, path, status, latency) only in debug mode.
- ๐งฉ Backend-Only Mode: If no frontend is detected, DevLinker still runs and forwards all traffic to backend.
- ๐ Runtime API URL Injection: Injects runtime browser patching so hardcoded localhost API calls are rewritten to the active proxy/tunnel origin.
- ๐ก๏ธ Proxy CORS + Preflight: Handles common CORS/preflight behavior at the proxy layer, including credential-safe Origin handling.
- ๐ง Smart Detection & Doctor: Real-time request analysis, backend intelligence, log analyzer, and
devlinker doctorfor instant diagnostics. - ๐ก๏ธ Auto-Fix Engine:
devlinker fixapplies safe fixes (like VITE_API_URL) and suggests code changes. - ๐ Public Sharing: Share your local dev environment instantly with the
--urlflag at startup. - ๐ก WLAN Sharing: Prints LAN URL for same-network device access.
- ๐ Secure Token Linking: Optional token gate for LAN/public access with
DEVLINKER_LINK_TOKEN. - ๐ Browser API Logs Dashboard: Open
/__devlinker/dashboardfor lightweight live API visibility. - ๐งโ๐ป Interactive CLI: Modern, colorized, emoji-rich terminal UX for all commands.
- ๐งฉ Zero Config: Works out-of-the-box for most FastAPI, Flask, Vite, and Docker projects.
- ๐งช Runtime Smoke Test: Built-in test for end-to-end proxy validation.
- ๐ ๏ธ Extensible: Modular architecture for future SaaS, dashboard, and team features.
Support DevLinker
If DevLinker helps you ship faster, consider supporting the project:
- ๐ UPI: devlinker@upi
- ๐ UPI link: upi://pay?pa=devlinker@upi&pn=DevLinker&cu=INR&tn=Support%20DevLinker%20Project%20๐
CLI Commands & Options
devlinkerโ Start proxy (local only, fast)devlinker supportโ Show UPI support QR code in terminaldevlinker --urlโ Start with public tunnel (Cloudflare/ngrok)devlinker doctorโ Diagnose issues, see categorized problems and fixesdevlinker fixโ Auto-fix common issues (env, API paths, config)devlinker --frontend 5173 --backend 5000โ Override detected portsdevlinker --dockerโ Auto-start Docker backenddevlinker --no-tunnelโ Explicitly force local/WLAN-only mode (no public tunnel)devlinker --no-lanโ Hide WLAN sharing URLdevlinker --interactive-backendโ Prompt to choose backend if multiple founddevlinker --proxy-port 18000โ Use custom proxy portdevlinker --debugโ Enable debug mode (turns on live API request logger)devlinker --versionโ Show version
Security token (optional):
set DEVLINKER_LINK_TOKEN=your-secret-token
devlinker --url
When enabled, LAN/public requests must include one of:
- query param
dl_token=... - header
X-DevLinker-Token: ... - header
Authorization: Bearer ...
Built-in API logs dashboard:
http://localhost:<proxy-port>/__devlinker/dashboard
JSON stream endpoint used by the dashboard:
http://localhost:<proxy-port>/__devlinker/logs
Project Structure
devlinker/
โโโ devlinker/
โ โโโ __init__.py
โ โโโ main.py
โ โโโ runner.py
โ โโโ detector.py
โ โโโ proxy.py
โ โโโ tunnel.py
โโโ setup.py
โโโ README.md
โโโ requirements.txt
Install
For local development:
pip install .
For editable local development:
pip install -e .
After publishing to PyPI:
pip install devlinker
Run
devlinker
Direct module run (without installing entrypoint script):
python -m devlinker.main
Typical startup output (TTY with Rich available):
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ โพ๏ธ DevLinker v1.4.5 โ
โ Smart Local Dev Environment โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โ Detecting project...
โณ Booting local services...
โญโโโโโโโโโ DevLinker Ready โโโโโโโโโโฎ
โ Proxy http://localhost:8001 โ
โ WLAN http://192.168.1.3:8001 โ
โ Public disabled (use --url) โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โจ Ready in 5.5s
Powered by DevLinker ๐
Enable debug mode with live API request logging:
devlinker --debug
Debug mode request logger sample:
๐ Debug mode enabled: live API request logger is ON
๐ก Requests (Live)
GET /api/users 200 45ms
POST /api/login 401 120ms
Version check:
devlinker --version
Optional overrides:
devlinker --frontend 5173 --backend 5000
Backend override alias:
devlinker --backend-port 3001
Enable Docker auto-start explicitly:
devlinker --docker
Tunnel and Sharing Modes
By default, DevLinker starts fast local proxy only (no tunnel). It prints a LAN URL when it can detect a local network interface, and you can share that link with devices on the same Wi-Fi/LAN.
LAN-only quick start:
devlinker
You can also pass --no-tunnel to explicitly enforce no public tunnel startup:
devlinker --no-tunnel
--no-tunnel does not disable frontend/backend linking. DevLinker still starts the local proxy and routes frontend + backend through the same local entry URL.
For access from another network, start with a public tunnel using the --url flag:
devlinker --url
This starts the proxy and opens a public tunnel (Cloudflare or ngrok). The output will show:
๐ Enabling public tunnel...
โ Tunnel provider: Cloudflare
โ Public URL:
https://xxxx.trycloudflare.com
โน Tip: Ctrl+Click to open link
โน Share this link with collaborators.
If your friend is on the same Wi-Fi/LAN, use the printed LAN URL like http://192.168.x.x:<proxy-port>. If they are outside your network, use the public tunnel URL instead.
To force tunnel off (even if --url is passed):
devlinker --no-tunnel
When running without --url, youโll see local/WLAN output with public disabled:
Proxy: http://localhost:8000
WLAN: http://192.168.1.5:8000
Public: disabled (use --url)
WLAN usage notes:
localhostworks only on the machine running DevLinker.- Use the printed WLAN URL on phones/laptops connected to the same Wi-Fi/LAN.
WLAN troubleshooting checklist:
- Ensure both devices are on the same Wi-Fi/subnet.
- Allow Python/DevLinker through firewall prompts (Private networks).
- Keep Universal Mode enabled (default) so localhost API calls are rewritten safely for LAN/public links.
Disable WLAN URL output:
devlinker --no-lan
Smart Detection & Auto-Fix System
DevLinker now includes an AI-powered detection and auto-fix engine:
- Request Inspector: Real-time analysis of proxy traffic for common mistakes (missing
/apiprefix, 404s, CORS risks, upstream failures) - Backend Intelligence: Probes backend endpoints and type at startup for smarter routing and hints
- Log Analyzer: Converts error messages (CORS, 404, connection refused) into human-readable explanations and actionable fixes
- Smart Warning Engine: Prints clean CLI warnings and suggestions, e.g.:
โ ๏ธ Detected direct backend call (localhost:5000)
๐ Use /api/* instead of direct URL
All detection and fixes are modular, async-compatible, and production-ready. See devlinker/proxy.py, devlinker/detector_ai.py, and devlinker/logger.py for implementation.
Interactive backend selection (when local and Docker are both detected):
devlinker --interactive-backend
Disable interactive backend selection (keeps local-first behavior):
devlinker --no-interactive-backend
If port 8000 is already in use:
devlinker --frontend 5173 --backend 5000 --proxy-port 18000
Default behavior also tries fallback ports automatically when 8000 is busy:
โ Port 8000 in use
โน Using proxy port: 8001
By default it scans the next 10 ports after the requested one, then tries 18000.
Frontend detection behavior:
- Scans Vite defaults and fallback ports (
5173through5190) - Also checks common alternatives (
3000,4173,8080) - Retries during startup to catch slow boot cases
- Performs readiness gating before proxy startup (waits until frontend looks like Vite and backend responds)
Important Frontend Rule
Frontend requests must use relative API paths:
fetch("/api/endpoint")
Do not hardcode backend host URLs in frontend code.
DevLinker injects runtime config and request patching in proxied HTML so frontend API calls are routed through the active proxy/tunnel origin without requiring source edits or frontend rebuilds.
Use the proxy URL as your single entry point during development:
http://localhost:<proxy-port>
Avoid direct backend calls like http://localhost:5000 from browser-facing code.
Configuration File
DevLinker loads config from the first file found in this order:
devlinker.yamldevlinker.ymldevlinker.json
Example:
frontend: 5173
backend: 5000
proxy_port: 8001
backend_entry: main.py
api_prefix: /api
strip_prefix: true
Config key notes:
backend_entry: Optional Python backend startup file override (for examplemain.py)api_prefix: Prefix DevLinker treats as API traffic (default:/api)strip_prefix: Whentrue, strips configuredapi_prefixbefore forwarding to backend- Public tunnel is opt-in at runtime using
--url; local/WLAN mode works without ngrok/cloudflared.
Backend Auto-Detection
Backend port detection runs in this order:
- Check localhost port 5000
- If not found, query Docker via Docker SDK (
docker.from_env()) for published host-to-container port mappings - Prioritize containers using labels when present (
devlinker.role=backend, optionaldevlinker.port=<container-port>) - Otherwise rank containers by likely backend identity (name hints like backend/api plus project-name hints)
- Use the best mapped host port automatically, even when internal port is not 5000
- If nothing is found, print next-step guidance and exit
Python backend entry detection supports automatic discovery of:
app.pymain.pyserver.pyrun.pymanage.py
If backend_entry is set in config, DevLinker uses it first and falls back to the discovery list.
If Docker SDK is unavailable, Dev Linker falls back to Docker CLI parsing as a compatibility path.
When both Local and Docker backends are available, Dev Linker prompts you to choose one (TTY mode) unless --no-interactive-backend is used.
If backend detection fails, Dev Linker prints a clear checklist showing what it checked and how to recover.
Detection messages include source labels, for example:
[OK] Backend detected (Local) -> port 5000
Example Docker dynamic-port message:
[WARN] Backend not found on port 5000
[INFO] Checking Docker containers...
[OK] Backend detected (Docker) -> port 32768
Dev Linker checks backend runtime in this order:
- Docker Compose (
backend/docker-compose.yml,docker-compose.yaml,compose.yml, orcompose.yaml) - Docker (
backend/Dockerfile) - Node (
backend/package.json) - Python (
backend/requirements.txtor discovered entry file such asapp.py/main.py)
Backend startup commands:
- Docker Compose (default): manual run
docker compose up --buildinbackend/ - Dockerfile (default): manual run
docker build -t devlinker-backend .thendocker run --rm -p 5000:5000 devlinker-backend - Docker Compose/Dockerfile with
--docker: Dev Linker runs those Docker commands for you - Node:
npm run dev(ornpm startwhendevis missing) - Python:
python <discovered-entry>(auto:app.py,main.py,server.py,run.py,manage.py, or configuredbackend_entry)
For containerized Flask backends, ensure:
- App binds to all interfaces:
app.run(host="0.0.0.0", port=5000) - Port mapping is present:
-p 5000:5000
Notes
- runner.py expects frontend project in
frontend/and backend project inbackend/, with automatic Python entry discovery. - If those paths do not exist, Dev Linker skips launch and only tries to detect already-running services.
- If frontend is missing but backend is available, DevLinker continues in backend-only mode.
- Tunnel selection order is: cloudflared (TryCloudflare), then ngrok.
- If cloudflared is unavailable and ngrok is not configured, Dev Linker prints one-time setup guidance.
- You may need to set ngrok auth once on your machine using ngrok config add-authtoken .
- Dev Linker prints a public URL with
ngrok-skip-browser-warning=trueonly when ngrok is used. - Startup output includes selected tunnel provider (
cloudflareorngrok). - Proxy layer now supports WebSocket upgrades, including Vite HMR over shared links.
- Proxy handles common CORS/preflight behavior and adds camera/mic permissions policy headers.
- Live API request logging is disabled by default and only enabled with
devlinker --debug. - Proxy listens on
0.0.0.0and can print a WLAN URL for same-network sharing. - If WLAN access fails on Windows, allow the proxy port in firewall and confirm devices are on the same network.
Runtime Smoke Test
Run this test to validate proxy behavior end-to-end (frontend HTTP route, backend API forwarding, and WebSocket pass-through):
python -m unittest tests.test_proxy_runtime
The test spins up lightweight local frontend and backend apps, starts Dev Linker proxy, and verifies:
GET /is routed to frontendPOST /api/loginis routed to backendws://.../hmrround-trip works through proxy
Troubleshooting Links
If local or shared links show blank pages, connection refused errors, or 404s, check these common causes:
- Docker backend binding
- Symptom:
http://localhost:<backend-port>refuses connection. - Cause: backend process inside container is bound to
127.0.0.1instead of0.0.0.0. - Fix: run backend with host
0.0.0.0(example FastAPI/Uvicorn:uvicorn app.main:app --host 0.0.0.0 --port 8000).
- API prefix mismatch
- Symptom: frontend loads through Dev Linker but API calls return 404.
- Cause: frontend calls
/api/..., but backend routes are mounted without/apiprefix. - Fix: expose backend routes under
/api(or adjust frontend paths to match backend routes).
- Vite host restrictions
- Symptom: direct Vite URL works, Dev Linker proxy URL is blank or blocked.
- Cause: Vite host protections reject proxied host/port.
- Fix: set Vite
server.hostandserver.allowedHoststo allow proxy use.
Quick isolate sequence:
- Open
http://localhost:<backend-port>/docs(or/health) directly. - Open Dev Linker local proxy URL and verify UI loads.
- Use browser network tab to check API status codes for
/api/*requests.
Real-Time Development
- Run
devlinkerto share one combined frontend/backend URL. - Vite HMR and other WebSocket flows are proxied end-to-end through Dev Linker.
- Keep using relative frontend API paths (for example,
/api/endpoint) so routing stays consistent locally and over tunnel.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file devlinker-1.4.6.tar.gz.
File metadata
- Download URL: devlinker-1.4.6.tar.gz
- Upload date:
- Size: 50.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f853c3f5aedaf846e9d18f62131aca6cc6c87ef1ad6f28227e9631e42aed7843
|
|
| MD5 |
95037f917b18335a5bbf365886d8c278
|
|
| BLAKE2b-256 |
e46a3bf7a6a3f2235a133abc3ecb48b08f36f181c10560280e84fce6fedbcf0e
|
Provenance
The following attestation bundles were made for devlinker-1.4.6.tar.gz:
Publisher:
publish.yml on mani1028/devlinker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
devlinker-1.4.6.tar.gz -
Subject digest:
f853c3f5aedaf846e9d18f62131aca6cc6c87ef1ad6f28227e9631e42aed7843 - Sigstore transparency entry: 1243610981
- Sigstore integration time:
-
Permalink:
mani1028/devlinker@2b675e49e50a9a58427709220371f5d1e8e76e38 -
Branch / Tag:
refs/tags/v1.4.6 - Owner: https://github.com/mani1028
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2b675e49e50a9a58427709220371f5d1e8e76e38 -
Trigger Event:
push
-
Statement type:
File details
Details for the file devlinker-1.4.6-py3-none-any.whl.
File metadata
- Download URL: devlinker-1.4.6-py3-none-any.whl
- Upload date:
- Size: 49.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5faafad2746ae8edbb041a2556d9d3c63fb713a0837ce94e044c06326068dcfc
|
|
| MD5 |
0f56ed60aa189d1af9e2985663e2e36a
|
|
| BLAKE2b-256 |
33ad5a1f4e7d1733316e74f2f204bc5f6efbfe36e2b71bc41f4889760a214c02
|
Provenance
The following attestation bundles were made for devlinker-1.4.6-py3-none-any.whl:
Publisher:
publish.yml on mani1028/devlinker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
devlinker-1.4.6-py3-none-any.whl -
Subject digest:
5faafad2746ae8edbb041a2556d9d3c63fb713a0837ce94e044c06326068dcfc - Sigstore transparency entry: 1243610997
- Sigstore integration time:
-
Permalink:
mani1028/devlinker@2b675e49e50a9a58427709220371f5d1e8e76e38 -
Branch / Tag:
refs/tags/v1.4.6 - Owner: https://github.com/mani1028
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2b675e49e50a9a58427709220371f5d1e8e76e38 -
Trigger Event:
push
-
Statement type: