@hahnfeld/devtunnel-provider
v1.0.3
Published
Microsoft Dev Tunnels provider plugin for OpenACP
Maintainers
Readme
@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 hostCLI 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-anonymousflag (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-providerThis 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-providerDev Tunnels CLI Setup
Before configuring the plugin you need the devtunnel CLI installed and authenticated.
Install the CLI:
# macOS brew install --cask devtunnel # Linux curl -sL https://aka.ms/DevTunnelCliInstall | bash # Windows winget install Microsoft.devtunnelLog in (required — anonymous hosting is not supported by the CLI):
devtunnel user loginOr with GitHub:
devtunnel user login -gOr 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-providerThe wizard guides you through:
- CLI presence and authentication validation
- Port selection (specific port or auto-detect from API server)
- Anonymous access toggle
- Protocol selection (auto, HTTP, HTTPS)
- 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-selectConfiguration 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" | Port protocol: auto, http, or https |
| 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) |
How It Works
- On startup, the plugin waits for the
api-server:startedevent - It spawns
devtunnel host -p <port> --allow-anonymousas a child process - It parses the public URL from the CLI output (
https://<id>-<port>.<cluster>.devtunnels.ms/) - The URL is available via
getPublicUrl()and thetunnel-provider:devtunnelservice - If the tunnel reconnects with a new URL, the plugin emits a
devtunnel-provider:url-changedevent
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:
- Detects the new URL via line-buffered stdout/stderr parsing
- Updates its internal state
- Emits a
devtunnel-provider:url-changedevent 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/stopTunnelcommands in the core tunnel system do not see Dev Tunnel instances- The plugin cannot be selected as a provider via the
tunnel.providerconfig 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
This plugin is designed with security as a priority:
- No shell interpolation — All subprocess spawning uses
spawn()with argv arrays, nevershell: trueor 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/whereare 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
- Anonymous access warning — When
allowAnonymousis enabled, a conspicuous warning is logged at startup
Anonymous Access Warning
When allowAnonymous: true (the default), the tunnel URL is publicly accessible without authentication. This is required for OpenACP webhook endpoints and viewer links, but means anyone with the URL can access your local service. If your service is not intended to be public, set allowAnonymous: false.
Slash Commands
| Command | Description |
|---------|-------------|
| /devtunnel | Show current tunnel status |
| /devtunnel auth | Check devtunnel CLI authentication |
Uninstalling
openacp plugin uninstall @hahnfeld/devtunnel-provider --purgeThe --purge flag removes all saved settings. After uninstalling, you may also want to:
devtunnel delete-all— Remove any persistent tunnelsdevtunnel user logout— Clear credentialsbrew uninstall --cask devtunnel— Remove the CLI (macOS)
Development
# Install dependencies
pnpm install
# Build
pnpm build
# Watch mode
pnpm dev
# Run tests
pnpm testArchitecture
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 testsTech Stack
devtunnelCLI — Microsoft Dev Tunnels command-line tool@openacp/plugin-sdk— OpenACP plugin interface
License
MIT
