Skip to content

hahnfeld/devtunnel-provider

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@hahnfeld/devtunnel-provider

Microsoft Dev Tunnels provider plugin for OpenACP — expose local ports to the internet via the devtunnel CLI.

Features

  • Dev Tunnels integration — Uses Microsoft's devtunnel host CLI to create tunnels
  • Auto-start — Automatically starts the tunnel when the OpenACP API server is ready
  • URL change detection — Monitors for URL changes after reconnects and emits events so other plugins automatically pick up the new URL
  • Interactive install wizard — Validates CLI installation and authentication during setup
  • Anonymous access — Configurable --allow-anonymous flag (default: on) for webhook endpoints and viewer links
  • Configurable — Protocol, expiration, cluster region, and port options
  • Security hardened — No shell interpolation, strict input validation, bounded buffers

Prerequisites

  • OpenACP CLI >= 2026.0.0
  • Node.js 18+
  • Microsoft Dev Tunnels CLI (devtunnel)
  • A Microsoft or GitHub account (for devtunnel user login)

Installation

Option A: OpenACP plugin install (recommended)

openacp plugin install @hahnfeld/devtunnel-provider

This launches an interactive wizard that validates your CLI installation and authentication.

Option B: Manual npm install

npm install @hahnfeld/devtunnel-provider
# or
pnpm add @hahnfeld/devtunnel-provider

Dev Tunnels CLI Setup

Before configuring the plugin you need the devtunnel CLI installed and authenticated.

  1. Install the CLI:

    # macOS
    brew install --cask devtunnel
    
    # Linux
    curl -sL https://aka.ms/DevTunnelCliInstall | bash
    
    # Windows
    winget install Microsoft.devtunnel
  2. Log in (required — anonymous hosting is not supported by the CLI):

    devtunnel user login

    Or with GitHub:

    devtunnel user login -g

    Or with device code (when interactive browser login isn't possible):

    devtunnel user login -d

For full details see the Dev Tunnels CLI reference.

Configuration

Interactive wizard

If you installed via openacp plugin install, the wizard runs automatically. To re-configure:

openacp plugin configure @hahnfeld/devtunnel-provider

The wizard guides you through:

  1. CLI presence and authentication validation
  2. Port selection (specific port or auto-detect from API server)
  3. Anonymous access toggle
  4. Local server protocol hint (auto, HTTP, HTTPS — see Protocol)
  5. Optional tunnel expiration

Manual configuration

Add the following to your plugin settings:

plugins:
  devtunnel-provider:
    enabled: true
    port: null                 # null = auto-detect from API server, or specify e.g. 3100
    allowAnonymous: true       # Required for webhooks/viewer links
    protocol: auto             # auto | http | https
    expiration: null           # e.g., '4h', '2d', or null for session-lived
    cluster: null              # e.g., 'usw2', or null for auto-select

Configuration reference

Field Type Default Description
enabled boolean true Enable the provider
port number | null null Local port to tunnel. Null = auto-detect from API server
allowAnonymous boolean true Allow unauthenticated access to tunnel URLs
protocol string "auto" Local server protocol hint: auto, http, or https (see Protocol below)
expiration string | null null Tunnel lifetime (e.g., 4h, 2d, max 30d). Null = deleted on exit
cluster string | null null Preferred Azure region cluster (e.g., usw2)

Protocol

The protocol setting tells the Dev Tunnels relay what protocol your local server speaks — it does not affect the security of the public tunnel URL.

Value Meaning
auto Let devtunnel detect the local protocol automatically (default)
http Your local server speaks plain HTTP (the common case — Express, Fastify, Flask, etc.)
https Your local server uses TLS locally (e.g., a self-signed cert on localhost)

The public-facing tunnel URL is always HTTPS regardless of this setting. Traffic flows like this:

Internet ──HTTPS──▸ devtunnels.ms relay ──tunnel──▸ localhost (http or https)

Choosing http does not expose unencrypted traffic to the internet. It only describes the last hop on your own machine between the tunnel client and your local server.

How It Works

  1. On startup, the plugin waits for the api-server:started event
  2. It spawns devtunnel host -p <port> --allow-anonymous as a child process
  3. It parses the public URL from the CLI output (https://<id>-<port>.<cluster>.devtunnels.ms/)
  4. The URL is available via getPublicUrl() and the tunnel-provider:devtunnel service
  5. If the tunnel reconnects with a new URL, the plugin emits a devtunnel-provider:url-changed event

URL Change Detection

When a devtunnel host process reconnects after a network disruption, it may receive a new public URL. This plugin continuously monitors the tunnel output and:

  1. Detects the new URL via line-buffered stdout/stderr parsing
  2. Updates its internal state
  3. Emits a devtunnel-provider:url-changed event with { oldUrl, newUrl }

Other plugins can subscribe to this event to update their webhook registrations, bot endpoints, etc.

Events

Event Payload Description
devtunnel-provider:url-changed { oldUrl, newUrl } Tunnel URL changed after reconnect
devtunnel-provider:started { publicUrl, port } Tunnel started successfully

Known Limitations

No TunnelService.addTunnel() integration

This plugin operates independently of OpenACP's built-in TunnelService. It registers itself as a standalone service (tunnel-provider:devtunnel) and manages its own lifecycle rather than integrating with the central tunnel registry.

This means:

  • addTunnel / listTunnels / stopTunnel commands in the core tunnel system do not see Dev Tunnel instances
  • The plugin cannot be selected as a provider via the tunnel.provider config option
  • Tunnel retry, keepalive, and persistence are handled by the plugin itself, not by TunnelRegistry

Why: OpenACP's TunnelRegistry.createProvider() uses a closed factory — external providers cannot be registered into it. Adding support for plugin-provided tunnel providers would require a change to OpenACP core (e.g., a provider registration hook or a lookup into plugin services).

Planned: We'd like to contribute an extensible provider mechanism to OpenACP so that this plugin (and other third-party providers) can participate in the standard TunnelService lifecycle.

Security

OpenACP provides its own security layer

An important distinction: Dev Tunnels access control and OpenACP security are independent layers. Even when allowAnonymous: true (the default), your OpenACP instance is not unprotected. OpenACP enforces its own authentication and authorization on every incoming request — the tunnel is just a transport pipe.

Setting allowAnonymous: false adds a second layer of authentication at the Dev Tunnels relay (requiring a Microsoft/GitHub login to reach the URL at all), but this is not required for OpenACP to be secure. The default anonymous mode is the right choice for most users because:

  • Webhook providers (Teams, Slack, etc.) cannot authenticate with Microsoft to reach your tunnel
  • Viewer/share links need to work without requiring recipients to have a Microsoft account
  • OpenACP already validates and authorizes every request that arrives through the tunnel

If you want defense-in-depth or are tunneling a service that has no auth of its own (i.e., not OpenACP), then allowAnonymous: false adds a useful extra gate.

Plugin hardening

This plugin is designed with security as a priority:

  • No shell interpolation — All subprocess spawning uses spawn() with argv arrays, never shell: true or string interpolation
  • Strict input validation — Port, protocol, expiration, and cluster values are validated against strict patterns before being passed as CLI arguments
  • Binary path validation — Binary paths from which/where are verified to be absolute paths
  • Bounded buffers — Line buffers are capped at 1 MiB to prevent memory exhaustion from runaway subprocess output
  • No credential exposure — Dev Tunnels uses the system keychain for authentication; no secrets are passed via CLI args or environment variables

Slash Commands

Command Description
/devtunnel Show current tunnel status
/devtunnel auth Check devtunnel CLI authentication

Uninstalling

openacp plugin uninstall @hahnfeld/devtunnel-provider --purge

The --purge flag removes all saved settings. After uninstalling, you may also want to:

  1. devtunnel delete-all — Remove any persistent tunnels
  2. devtunnel user logout — Clear credentials
  3. brew uninstall --cask devtunnel — Remove the CLI (macOS)

Development

# Install dependencies
pnpm install

# Build
pnpm build

# Watch mode
pnpm dev

# Run tests
pnpm test

Architecture

src/
├── index.ts             # Plugin entry point & public exports
├── plugin.ts            # Plugin factory (install wizard, configure, setup/teardown)
├── provider.ts          # DevTunnelProvider (subprocess management, URL change detection)
├── types.ts             # DevTunnelConfig interface
└── __tests__/
    ├── provider.test.ts # Provider unit tests (URL parsing, change detection, security)
    └── plugin.test.ts   # Plugin shape conformance tests

Tech Stack

License

MIT

About

Microsoft Dev Tunnels provider plugin for OpenACP

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors