What's inside the asd binary, what lives in your project, and the four moving parts that turn your YAML into a running stack.
asd is one binary that wraps four cooperating components: a manifest
parser, the Caddy reverse proxy, a tunnel daemon manager, and a
runtime registry. This page is the operator-level view — enough to
debug, not so much it requires you to read the source.
For the source-contributor view, see extending.
| Part | Lives in | Owns |
|---|---|---|
| Manifest parser | asd binary | Reading asd.yaml + every net.manifest.yaml, expanding ${{ env.X }} / ${{ macro.Y() }}, validating against the Zod schemas. |
| Registry | .asd/workspace/network/registry.json |
The runtime source of truth: which services exist, what port each binds, what hostname each routes to, whether the tunnel for it is alive. |
| Caddy | asd binary (embedded) | Active reverse-proxy routes + TLS certificates. Stateful. |
| Tunnel daemon | asd binary (one process per service that opts in to tunnel.public: true) |
Holds the reverse-SSH connection to the tunnel server. Multiplexes subdomains per connection. |
A single ~100 MB executable that bundles:
asd help surface).asd terminal start).asd inspect start).Everything is statically linked / bundled where possible. The
binary is intentionally fat — there's no apt/yum/npm dependency
chain at install time. curl install.sh | bash and you're done.
my-project/
├── asd.yaml # project-level config
├── tpl.env # env template (per-mode keys)
├── .env / .env.<mode> # rendered env values
├── packages/
│ ├── api/
│ │ └── net.manifest.yaml # one service
│ └── frontend/
│ └── net.manifest.yaml # another service
└── .asd/ # asd's runtime state (gitignored)
├── workspace/
│ ├── network/
│ │ └── registry.json # runtime source of truth
│ ├── caddy/
│ │ └── caddy-state.json # only when Caddy is stopped
│ └── …
└── state/
└── active_mode # current asd mode
packages/<svc>/ is yours to organise. asd cares about
net.manifest.yaml at the path it walks; other files in the
package are unrestricted. .asd/ is asd's runtime state — never
edit, never commit.
asd distinguishes two kinds of extension:
| Module | Plugin | |
|---|---|---|
| Location | modules/<name>/ inside the asd-cli source tree |
modules/plugin/<name>/ in the asd-cli source tree |
| Compiled into the binary? | Yes (mostly) | No — the template is read at runtime |
| Adds CLI commands? | Yes (asd <name> <subcommand>) |
No (declares hooks into existing commands) |
| User-extensible? | Requires asd-cli contribution | Same (but the path is shorter) |
| Examples | caddy, auth, net, vault, code |
angular, supabase |
| Use case | New command surface for a new subsystem | Bundle automation hooks + service declarations for a stack |
The user-facing extension story is plugins; modules are the
internal subsystems that make up the binary. See
learn/07-build-a-module for the
plugin-author path and extending for
the module-author path.
Worth knowing if you're trying to predict asd's behaviour:
Manifests are the source of truth. Everything else
(registry.json, Caddy config, tunnel daemons) is derived. If
it disagrees with the manifest, the manifest wins on next apply.
apply is idempotent and diff-based. Re-running converges
to the same state. Failures don't leave you half-applied —
the next apply finishes the job.
Macros expand at apply time, not parse time. ${{ env.X }}
and ${{ macro.Y() }} resolve when asd net apply runs, with
the current .env and runtime values. Editing .env and
re-applying gives you the new values; no re-parse needed.
Caddy is stateful, the registry is durable. Caddy holds
routes in memory; stop it carelessly and you lose them. The
registry is on disk; it survives. See
caddy-state and
registry.
Tunnels multiplex. One SSH connection per tunnel-server
destination carries traffic for every service whose
tunnel.public is true. Not one connection per service.
registry — registry.json shape and lifecycle.caddy-state — why Caddy can't be killed.tunnels — the SSH → sish path.credential-model — where credentials come from.extending — adding modules / plugins via PR.