Configuration

Configuration Reference (asd.yaml)

Overview

The asd.yaml file at your project root is the single source of truth for how ASD manages your development environment. It contains four distinct areas:

SectionPurposeDocumentation
network:Service definitions, tunnels, Caddy routingThis page + Caddy
automation:Task runner, background services, health checksAutomation
hub:Dashboard layout, widgets, themesDashboard
features:Auto-start flags, binary management, behavior togglesThis page

Create it with:

asd init

Or write one by hand. The only required fields are version and project.name.

Minimal Example

version: 1
project:
  name: "my-app"

network:
  services:
    my-app:
      dial: "127.0.0.1:3000"
      host: "app.localhost"
      paths: ["/"]

This defines a single service running on port 3000, routable locally at http://app.localhost. That is enough to get started.

Project Settings

The project block identifies your project and configures global behavior:

project:
  name: "my-project"              # Required. Project identifier.
  domain: "localhost"              # Base domain for local service routing.
  description: "My application"   # Optional human-readable description.
  plugins: [supabase]             # List of enabled plugins.
  project_root: "frontend"        # Service ID that owns the root path "/".
  package_manifests_dir: "apps"   # Directory for monorepo package manifests.

The name is used as a namespace for service IDs and appears in log output. The plugins array activates integrations like the Supabase plugin, which auto-discovers and registers Supabase services.

Section 1: Networking and Tunnels

The network: block defines your services and how they are reached locally and publicly.

Service Definitions

Each service maps a local address to routable URLs (local Caddy routes and optional public tunnel URLs):

network:
  services:
    frontend:
      dial: "127.0.0.1:5173"
      host: "app.localhost"
      paths: ["/"]
      public: true
      subdomain: "frontend"

    api:
      dial: "127.0.0.1:8080"
      paths: ["/api"]
      stripPrefix: true

    webhook-receiver:
      dial: "127.0.0.1:9000"
      public: true
      subdomain: "webhook"

All Service Properties

PropertyTypeDescription
dialstringLocal address and port to forward traffic to (e.g., 127.0.0.1:3000). This is the upstream your service listens on. Required for your own services.
namestringOptional display name for the TUI and logs.
hoststringHostname for host-based routing (e.g., app.localhost).
pathsstring[]URL path prefixes for path-based routing (e.g., ["/api"]).
stripPrefixbooleanRemove the matched path prefix before forwarding to the upstream.
publicbooleanEnable a public HTTPS tunnel for this service.
subdomainstringSubdomain prefix for the tunnel URL (e.g., myapp becomes myapp-<id>.eu1.tn.asd.engineer).
publishPreferred"host" or "path" or "both"Which routing strategy to prefer for tunnel URLs.
prioritynumberRoute ordering. Higher values match first.
descriptionstringHuman-readable description shown in the TUI.
typestringService type label (added as X-ASD-Service-Type header).
securityHeadersobjectSecurity header configuration. See Caddy.
iframeOriginstring or nullOrigin allowed for iframe embedding.
deleteResponseHeadersstring[]Response headers to strip from the upstream.
ingressTagstring or nullPer-service ingress identifier for monitoring.
basic_authobjectPer-service basic auth override. See Caddy.
securitySensitivebooleanFlag service as requiring authentication by default.
envobjectPer-service environment variables (template-expanded).
processobjectAuto-start process definition (cmd, cwd).

Tunnel Configuration

Per-service tunnel settings:

network:
  services:
    api:
      dial: "127.0.0.1:8080"
      public: true                # Create a public tunnel
      subdomain: "api"            # URL prefix
      tunnelPort: 8080            # Override tunnel port (default: from dial)
      tunnelProtocol: "http"      # "http" or "tcp"

Project-level tunnel defaults:

project:
  tunnel:
    preferred: false              # Enable tunnels by default for all services
    prefix: "app"                 # Default subdomain prefix
    protocol: "http"              # Default protocol

Service IDs

Every service has a unique identifier — the key name in your YAML:

network:
  services:
    my-frontend:        # service-id = "my-frontend"
      dial: "127.0.0.1:3000"

    api-server:         # service-id = "api-server"
      dial: "127.0.0.1:8080"

Use service IDs in CLI commands:

asd net expose start my-frontend
asd net stop api-server
asd net open my-frontend

Plugin services use namespaced IDs like supabase:studio and supabase:kong.

Network Inspection (Alpha)

The network inspector (asd inspect) is an alpha feature that uses mitmproxy for HTTP traffic debugging. A planned integration will add per-service inspection toggles directly in asd.yaml:

network:
  services:
    api:
      dial: "127.0.0.1:8080"
      inspect: true               # Route traffic through mitmproxy (planned)

When fully integrated, asd net will show an “Inspect Traffic” action per service, and Caddy will chain requests through the mitmproxy proxy transparently. Today, you configure your application to use localhost:8080 as its HTTP proxy manually.

Section 2: Caddy Configuration

The network.caddy block controls the local reverse proxy. See Caddy Reverse Proxy for the full reference.

network:
  caddy:
    enable: true
    tls:
      enabled: true
      auto: true

    basic_auth:
      enabled: true
      realm: "My Project"
      routes: ["host", "path", "tunnel"]

Caddy handles request routing, TLS termination, HTTP Basic Authentication, security headers, compression, and health check endpoints.

Section 3: Automation

The automation: block defines tasks you run with asd run <task>. See Automation for the full reference.

automation:
  dev:
    - run: "pnpm dev"
      background: true
      skipWhen:
        port: 5173
    - waitFor: "http://127.0.0.1:5173"

  test:
    - run: "pnpm test"
      expect: "passed"

  build:
    - run: "pnpm build"
      environment:
        NODE_ENV: "production"

When you run asd up without arguments, it looks for tasks named up, dev, or start in that order.

Section 4: Dashboard

The hub: block configures the widget-based dashboard layout. See Dashboard for the full reference.

hub:
  globalSettings:
    theme: "dark"
    hideBoardControl: true

  boards:
    - name: "Development"
      order: 1
      views:
        - name: "Services"
          widgets:
            - type: "service"
              serviceId: "frontend"
              columns: 4
              rows: 4

Feature Flags

The features block toggles automatic behaviors:

features:
  # Service discovery
  auto_detect_services: true       # Scan for running services on asd net apply
  auto_onboard_detected: true      # Automatically register discovered services

  # Binary management
  auto_install_binaries: true      # Download Caddy, ttyd, etc. on asd init
  skip_binaries: ["codeserver"]    # Skip specific module binaries

  # Auto-start (what starts on asd net apply)
  auto_start_caddy: false          # Start Caddy reverse proxy
  auto_start_tunnel: false         # Start tunnels for public services
  auto_start_ttyd: false           # Start web terminal
  auto_start_codeserver: false     # Start VS Code server
  auto_start_mitmproxy: false      # Start network inspector
  auto_start_dbgate: false         # Start database UI

  # Security and networking
  enable_restricted_ports: false   # Allow binding to ports 1-1024 (requires setcap)
  disable_authentication: false    # Disable HTTP Basic Auth globally
  show_auth_in_urls: true          # Show credentials in displayed URLs
  ingress_header: true             # Add X-ASD-Ingress response header

  # Dashboard
  hub_visibility: "both"           # "both", "public", "private", or false

For most projects, the defaults work well. Enable auto_start_caddy and auto_start_tunnel if you want asd net apply to bring everything up in one command.

Plugins

Plugins extend ASD with network services from external tools.

Enabling Plugins

project:
  name: "my-app"
  plugins: [supabase]

Available Plugins

PluginDescriptionServices Provided
supabaseLocal Supabase development integrationsupabase:studio, supabase:kong, supabase:mailpit

Plugin Service Overlays

Plugin services appear automatically. Customize their routing without redefining the service:

network:
  services:
    supabase:studio:
      paths: ["/studio"]
      public: true
      priority: 40

The difference between an overlay and a full service: overlays lack a dial property. They customize existing plugin services; full services define new ones.

Plugin Commands

asd plugin:supabase:bootstrap   # Start Supabase + extract env vars to .env
asd plugin:supabase:start       # Start Supabase services
asd plugin:supabase:stop        # Stop Supabase services
asd plugin:supabase:extract     # Extract env vars only

Template Macros

Service definitions, plugin manifests, and automation steps support ${{ }} template expressions. These are expanded at configuration time before routes are applied.

Environment Variables

network:
  services:
    my-app:
      dial: "127.0.0.1:${{ env.APP_PORT }}"
      host: "${{ env.APP_HOST }}"

Tunnel Credential Macros

These macros read from the credential registry. They work across all authentication types (SSH keys, tokens, ephemeral) and server types (shared, dedicated, self-hosted):

MacroReturnsExample Value
${{ macro.tunnelHost("app") }}Full tunnel hostnameapp-fkmc.eu1.tn.asd.engineer
${{ macro.tunnelClientId() }}Short client IDfkmc
${{ macro.tunnelEndpoint() }}Server FQDNeu1.tn.asd.engineer
${{ macro.exposedOrigin("app") }}Full HTTPS originhttps://app-fkmc.eu1.tn.asd.engineer
${{ macro.exposedOriginWithAuth("app") }}Origin with credentialshttps://user:pass@app-fkmc...

When no tunnel credentials exist, these resolve to empty strings. Empty hosts are filtered out automatically.

Utility Macros

MacroDescription
${{ macro.getRandomPort() }}Allocate a free port
${{ macro.getRandomString(length=32) }}Random alphanumeric string
${{ macro.bcrypt(password="...", cost=12) }}Generate a bcrypt hash
${{ core.isDockerAvailable() }}Returns true if Docker is running

Network Package Manifests

For monorepo projects, ASD auto-discovers services from subdirectories using net.manifest.yaml files. Each package defines its own services, and ASD merges them into the network at discovery time.

my-monorepo/
  asd.yaml                    # Root config with project settings
  packages/
    api/
      net.manifest.yaml        # API service definition
    web/
      net.manifest.yaml        # Frontend service definition
    admin/
      net.manifest.yaml        # Admin panel service definition

Manifest Format

A net.manifest.yaml follows the same service schema as network.services in your root asd.yaml:

# packages/api/net.manifest.yaml
services:
  api:
    dial: "127.0.0.1:${{ env.API_PORT }}"
    paths: ["/api"]
    public: true
    subdomain: "api"
    description: "REST API server"

Template macros work in manifests. Environment variables and tunnel credential macros are expanded at discovery time.

Configure the Manifest Directory

project:
  name: "my-monorepo"
  package_manifests_dir: "apps"   # Look in apps/ instead of default packages/

Or set via environment variable: ASD_PACKAGE_MANIFESTS_DIR=services.

How Discovery Works

When you run asd net apply, ASD:

  1. Reads the root asd.yaml
  2. Scans package_manifests_dir for subdirectories containing net.manifest.yaml
  3. Expands template macros in each manifest
  4. Merges all discovered services into the network registry
  5. Generates Caddy routes for the combined service set

Services from manifests can be overridden by the root asd.yaml. If both define the same service ID, the root config wins.

Full Example

A comprehensive asd.yaml demonstrating all four sections:

version: 1
project:
  name: "my-saas"
  domain: "localhost"
  plugins: [supabase]
  package_manifests_dir: "apps"

features:
  auto_detect_services: true
  auto_install_binaries: true
  auto_start_caddy: true
  auto_start_tunnel: false

network:
  caddy:
    enable: true
    basic_auth:
      enabled: true
      realm: "My SaaS Dev"

  services:
    frontend:
      dial: "127.0.0.1:5173"
      host: "app.localhost"
      paths: ["/"]
      public: true
      subdomain: "app"
      priority: 10
      basic_auth:
        enabled: false

    api:
      dial: "127.0.0.1:8080"
      paths: ["/api"]
      stripPrefix: true
      public: true
      subdomain: "api"
      securityHeaders:
        enableHsts: true
        enableCompression: true

    admin:
      dial: "127.0.0.1:3001"
      host: "admin.localhost"
      basic_auth:
        enabled: true
        realm: "Admin Only"

    supabase:studio:
      paths: ["/studio"]
      public: true
      priority: 40

hub:
  globalSettings:
    theme: "dark"
  boards:
    - name: "Dev"
      order: 1
      views:
        - name: "Services"
          widgets:
            - type: "service"
              serviceId: "frontend"
              columns: 4
              rows: 4

automation:
  dev:
    - run: "pnpm dev"
      background: true
      skipWhen:
        port: 5173
    - waitFor: "http://localhost:5173"

  build:
    - run: "pnpm build && pnpm preview"
      background: true

Related Guides