# Duckeighty Init

> Bootstrap a host for duckeighty: clone the duckeighty repository locally if it is missing, trust the mkcert local CA, install the macOS /etc/resolver entry for *.duckeighty.test, and bring up the shared traefik + dnsmasq ingress stack. After one invocation per host, every app repo only needs the duckeighty-integrate skill to generate its docker-compose.local.yaml. TRIGGER: the user is new to duckeighty, has never run ./duckeighty.sh up, asks to "set up duckeighty" / "install the local HTTPS ingress", or the duckeighty-doctor skill reported several failing checks at once. SKIP: the user already has a working duckeighty checkout and a running ingress (run the duckeighty-doctor skill first if unsure).

- Skill: `mackee/duckeighty-init` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mackee/duckeighty-init`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mackee/duckeighty-init/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: mackee (https://skillmd.com/u/mackee)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mackee/duckeighty-init

---


# duckeighty: init (one-time per-host ingress bootstrap)

This skill walks the user through the four steps needed to make
`https://*.duckeighty.test` resolvable and TLS-trusted on their machine.
The ingress stack is host-wide: one install per developer, shared by
every app repo they work in.

## Confirm prerequisites before starting

1. macOS. The current duckeighty resolver step writes to `/etc/resolver/`,
   which is macOS-specific.
2. Docker daemon is running (`docker version` succeeds).
3. `mkcert` is installed. If not:

   ```
   brew install mkcert nss
   ```

   Install it and confirm before proceeding.

## Step 1 — decide where duckeighty lives (always ask the user)

Do not hardcode a path. Developers use different checkout layouts and the
skill has no business assuming which one. The only auto-accepted value
is an explicit `DUCKEIGHTY_CHECKOUT` env var the user has set previously.

### 1a. Honor an explicit override

```
if [ -n "${DUCKEIGHTY_CHECKOUT:-}" ] && [ -d "$DUCKEIGHTY_CHECKOUT/.git" ]; then
  CHECKOUT="$DUCKEIGHTY_CHECKOUT"
fi
```

If set and valid, confirm with the user ("Use the clone at `$CHECKOUT`?").
If yes, skip to step 2.

### 1b. Otherwise, present the choice

Always present these options to the user and let them pick. Do not guess
based on filesystem layout or tool availability.

| # | Path                                         | When it fits                                            |
|---|----------------------------------------------|---------------------------------------------------------|
| 1 | `~/.duckeighty`                              | No existing convention. Shortest path, skill-owned.     |
| 2 | `~/src/github.com/mackee/duckeighty`         | You keep repos under `~/src/<host>/<owner>/<repo>`.     |
| 3 | A custom path the user types                 | Any other layout the user prefers.                      |

If the user picks option 3, ask them for the absolute path.

Once the path is chosen:

```
CHECKOUT="<chosen>"
mkdir -p "$(dirname "$CHECKOUT")"
git clone https://github.com/mackee/duckeighty "$CHECKOUT"
```

If `$CHECKOUT` exists but is not a git clone, stop and ask the user. Do
not overwrite.

### 1c. Persist the choice (optional but recommended)

Suggest the user add this to their shell rc so later `init` / `doctor`
runs skip the prompt:

```
export DUCKEIGHTY_CHECKOUT="$CHECKOUT"
```

All subsequent steps run from `$CHECKOUT`. Surface the final value in
the summary to the user.

## Step 2 — install local CA and wildcard certificate

```
./duckeighty.sh setup-ca
```

This runs `mkcert -install` (installs the mkcert root CA into the system
keychain) and writes `infra/certs/duckeighty.test.pem` +
`infra/certs/duckeighty.test-key.pem`. Do not re-run on every init; only
run when certs are missing or the user explicitly asks to re-issue.

If `mkcert -install` asks for a password, that is the normal macOS
keychain prompt — proceed.

## Step 3 — install the macOS resolver entry (sudo)

```
./duckeighty.sh setup-resolver
```

This writes `/etc/resolver/duckeighty.test` pointing at `127.0.0.1:5354`.
It needs `sudo`. Tell the user the command and what it writes before
running it — do not silently escalate.

Browser restart is usually not required, but if the user opened
`https://*.duckeighty.test` before setup the browser may cache the
failure; suggest a tab reload.

## Step 4 — bring up the ingress stack

```
./duckeighty.sh up
```

This creates the external `shared_ingress` network (if missing) and runs
`docker compose -f infra/compose.yaml up -d`. The compose file declares
`name: duckeighty`, so `docker compose ls` lists it as the `duckeighty`
project. After startup, two containers should be running with
`restart: unless-stopped`:

- `duckeighty-traefik-1` — reverse proxy + TLS terminator on 127.0.0.1:80 / 443
- `duckeighty-dnsmasq-1` — DNS for `*.duckeighty.test` on 127.0.0.1:5354

Both survive reboots as long as Docker restarts.

## Step 5 — verify

Suggest running the duckeighty-doctor skill next, or manually:

```
"$CHECKOUT"/duckeighty.sh check probe.duckeighty.test
```

Expected output:

```
127.0.0.1
HTTP 404
```

The 404 means TLS + DNS + Traefik all work and the hostname simply has no
router yet — which is correct before any app is onboarded.

## After init

From any application repo with a `compose.yaml`, use the duckeighty-integrate skill
to generate `docker-compose.local.yaml`. The ingress stack stays running
in the background; no per-app changes under `infra/` are ever needed.

## Do not

- Do not re-run `setup-ca` on every init call. Re-issuing the CA forces
  the user to re-trust it in every browser. Only re-run when the certs
  are actually missing or expired.
- Do not run these commands from inside a random application repo — they
  must run in the duckeighty checkout (`$CHECKOUT`).
- Do not copy `infra/` into the user's app repo. The ingress is host-wide
  and shared; duplication breaks the port 80/443 assumption.
- Do not silently run `sudo`. Surface the command, explain what it writes,
  ask before proceeding.

