# ekdns Development Guidelines

## Project Overview

ekdns — a Go CLI tool that resolves hostnames against a specified DNS server and writes the resulting IP-to-hostname mappings into the local hosts file. It manages a clearly delimited block within the hosts file so it can cleanly add and remove only its own entries. Built as a single static binary with no runtime dependencies.

## Go Toolchain — CRITICAL

**Always use `go1.20` instead of `go` for ALL Go toolchain operations.**

The system default `go` command may be a newer version that is incompatible with this project.

```powershell
# CORRECT — use these commands:
go1.20 build .
go1.20 test ./...
go1.20 vet ./...
go1.20 fmt ./...

# WRONG — do NOT use bare 'go':
go build .       # May use incompatible Go version
go test ./...    # May use incompatible Go version
```

**Go 1.21+ features that MUST NOT be used:**
- `min`, `max`, `clear` builtins
- Range-over-int (`for i := range 10`)
- `slices`, `maps`, `cmp` packages
- `log/slog` package
- Any other Go 1.21+ standard library additions

## Building

```powershell
# Standard debug build for Windows
.\build.ps1

# Optimized release build for all platforms
.\build.ps1 -Release

# Clean build
.\build.ps1 -Clean

# Cross-compile for all platforms
.\build.ps1 -CrossCompile

# Create release packages (requires binaries already built)
.\build.ps1 -Release -Package
```

Output: `build\ekdns.exe` (default), `build\ekdns-<os>-<arch>[.exe]` (cross-compile).
Release packages: `releases\ekdns-<version>-<os>-<arch>.zip` / `.tar.gz`

## Running ekdns

ekdns has two commands: `add` and `delete` (with short aliases `a` and `d`/`del`).

```powershell
# Add one or more hostnames (resolves via the given DNS server, writes to hosts file)
.\build\ekdns.exe add -host hostname.example.com -server dns.example.com
.\build\ekdns.exe a   -host hostname.example.com -server dns.example.com

# Add multiple hostnames at once (comma-separated)
.\build\ekdns.exe add -host "host1.example.com,host2.example.com" -server dns.example.com

# Delete a specific hostname from the managed block
.\build\ekdns.exe delete -host hostname.example.com
.\build\ekdns.exe del    -host hostname.example.com
.\build\ekdns.exe d      -host hostname.example.com

# Delete multiple hostnames (comma-separated)
.\build\ekdns.exe delete -host "host1.example.com,host2.example.com"

# Delete ALL entries added by ekdns
.\build\ekdns.exe delete all
.\build\ekdns.exe delete a
```

### Flag Reference

| Command | Flag | Required | Description |
|---------|------|----------|-------------|
| `add` | `-host` | Yes | Comma-separated list of hostnames to resolve and add |
| `add` | `-server` | Yes | DNS resolver to query (e.g. `dns.example.com`) |
| `delete` | `-host` | Yes (unless `all`) | Comma-separated list of hostnames to remove |
| `delete all` | — | — | Removes every entry ekdns has written |

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Error or partial failure (e.g. one hostname failed to resolve) |

### Hosts File Management

ekdns wraps its entries between two marker lines:

```
# DNSHelper <<-> START CONFIG
192.0.2.1	hostname.example.com
# DNSHelper <->> END CONFIG
```

Only lines within this managed block are ever modified or removed. The rest of the hosts file is preserved exactly. A backup of the hosts file is written to the executable's directory before every write.

## Testing

```powershell
# Run all tests
go1.20 test ./...

# Run with coverage
go1.20 test -cover ./...

# Run a specific package
go1.20 test ./hostfile/...
go1.20 test ./resolver/...
go1.20 test ./lockfile/...
go1.20 test ./platform/...

# Vet all code
go1.20 vet ./...
```

## Project Structure

```
main.go           CLI entry point (add / delete commands, flag parsing)
hostfile/
  hostfile.go     Hosts file parsing, DNSEntry type, add/remove helpers
  managed.go      Managed block read/write logic
  filesystem.go   FileSystem interface + OSFileSystem implementation
  hostfile_test.go
  managed_test.go
lockfile/
  lockfile.go     File-based mutex to prevent concurrent modifications
  lockfile_test.go
platform/
  platform.go         Platform interface — GetHostsFilePath()
  platform_windows.go Windows implementation
  platform_darwin.go  macOS implementation
  platform_linux.go   Linux implementation
  platform_test.go
resolver/
  resolver.go     DNS resolution via golang.org/x/net/dns/dnsmessage
  resolver_test.go
```

## Code Style

- **Go 1.20 only** — no Go 1.21+ features
- **Standard library first** — only `golang.org/x/*` dependencies allowed; no `github.com/*` third-party packages
- **Error wrapping**: `fmt.Errorf("context: %w", err)`
- **CGO disabled**: `CGO_ENABLED=0` for static binaries
- **Table-driven tests** preferred

## Dependencies

From `go.mod`:
- `golang.org/x/net` — DNS message parsing (`golang.org/x/net/dns/dnsmessage`)

## After Every Implementation

After completing any code change, validate in this order:

1. Run tests for changed packages:
   ```powershell
   go1.20 test ./hostfile/... ./resolver/...   # example
   ```
2. Run the full suite:
   ```powershell
   go1.20 test ./...
   ```
3. Build the project:
   ```powershell
   .\build.ps1
   ```
4. **Update `README.md`** — Review the root `README.md` and update it with any information an admin or end-user would need to know as a result of the changes. This includes but is not limited to:
   - New or changed CLI commands, flags, or aliases
   - New or changed exit codes
   - New dependencies
   - Changes to build steps or requirements
   - New environment variables or configuration
   - Changes to supported platforms
   - Changes to the project structure (new packages, renamed files)
   - Any new prerequisites or setup steps
   - Security-relevant changes (e.g. new permissions required)
5. Report test pass/fail counts and build outcome before considering the task complete.
