Files
dnshelper/.specify/memory/constitution.md
T

9.6 KiB

ekdns Constitution

Core Principles

I. System File Safety

The hosts file is a critical operating system resource. Corruption or data loss in this file disables all local name resolution and can render a machine unusable without manual intervention.

  • The tool MUST NOT truncate, zero-out, or partially overwrite the hosts file at any point during its operation.
  • All writes to the hosts file MUST use an atomic pattern: write complete content to a temporary file in the same directory, then rename over the original.
  • A backup of the current hosts file MUST be created before any modification is committed.
  • If any step in the write pipeline fails, the original hosts file MUST remain unmodified.

Rationale: This tool exists to solve DNS reliability problems. If it introduces a new failure mode — destroying the hosts file — it is worse than the problem it solves.

II. Standard Library First

The Go standard library MUST be the first choice for all functionality. External dependencies introduce supply-chain risk, version conflicts, and maintenance burden that are disproportionate for a small, single-purpose utility.

  • All functionality MUST be implemented using the Go standard library unless a documented technical justification proves it insufficient.
  • When an external dependency is genuinely required, it MUST come from one of the following approved sources:
    • golang.org/x/* (official Go extended libraries)
    • dev.emberkom.com/* (Emberkom internal libraries)
  • Any other external dependency MUST have a written justification in the code review or commit message explaining: (a) what standard library alternative was considered, (b) why it was insufficient, and (c) what the external dependency provides that the stdlib does not.
  • Dependencies MUST be audited when upgraded to ensure no new transitive dependencies from unapproved sources are introduced.

Rationale: A single-binary CLI tool that modifies a system file must have a minimal and auditable dependency tree. Every external package is an attack surface and a maintenance liability.

III. Go 1.20 Compatibility

This tool MUST compile and run correctly with Go 1.20. This version ceiling exists to maintain compatibility with legacy systems that cannot run newer Go runtimes.

  • The go.mod file MUST specify go 1.20 as the language version.
  • No language features, standard library APIs, or dependency versions exclusive to Go 1.21 or later are permitted.
  • Build and test pipelines MUST use Go 1.20 as the primary toolchain version to catch compatibility issues early.
  • If a dependency releases a version that requires Go 1.21+, the tool MUST pin to the last Go 1.20-compatible release of that dependency.

Rationale: The target deployment environments include older systems where upgrading the Go toolchain or OS is not feasible. Pinning the version ensures the tool remains deployable everywhere it is needed.

IV. Idiomatic Error Handling

Errors MUST flow upward through return values. Functions MUST NOT terminate the process, call os.Exit, or invoke global exit helpers except at the top-level main function.

  • Every function that can fail MUST return an error value.
  • Callers MUST inspect returned errors and either handle them, wrap them with additional context, or propagate them upward.
  • The main function is the only location permitted to call os.Exit or print a fatal error and terminate.
  • Platform-specific code (build-tagged files) MUST return errors to the caller rather than terminating directly.
  • Errors presented to the user MUST include enough context to understand what operation failed and why (e.g., file path, hostname, DNS server).

Rationale: Internal os.Exit calls make code untestable, prevent cleanup logic from running, and cause execution to continue past error points when deferred. Centralizing exit decisions in main produces predictable, debuggable control flow.

V. Simplicity and Single Purpose

This tool does one thing: update the local hosts file with DNS entries resolved from a specified DNS server. Every design decision MUST favor the simplest approach that satisfies the requirement.

  • The tool MUST remain a single-binary CLI with no external configuration files, services, or daemons.
  • Features MUST be justified by a concrete user need, not speculative future use. YAGNI (You Aren't Gonna Need It) applies.
  • Dead code, unused exports, and speculative abstractions MUST be removed. If code is not called, it does not belong in the repository.
  • The CLI interface MUST remain straightforward: subcommand + flags. No interactive prompts, no TUI, no config file parsing.

Rationale: Complexity is the enemy of reliability. A tool that modifies a critical system file must be small enough to reason about completely and audit quickly.

VI. Test-Driven Development

All new and refactored code MUST be developed using a test-driven development (TDD) approach. Tests are not an afterthought — they are the specification that drives implementation.

  • Tests MUST be written before the implementation code they verify. The Red-Green-Refactor cycle is mandatory: write a failing test, write the minimum code to make it pass, then refactor.
  • Every exported function and every function with non-trivial logic MUST have corresponding test coverage.
  • Functions that interact with external resources (filesystem, DNS, network) MUST be designed to accept interfaces or parameters that enable testing with fakes, stubs, or in-memory implementations. Direct calls to OS-level I/O from business logic are prohibited.
  • Platform-specific code MUST have tests that verify the function contract (correct return values, correct error conditions) on each supported platform.
  • Test files MUST follow Go conventions: *_test.go in the same package as the code under test.
  • go test ./... MUST pass with zero failures before code is merged.

Rationale: This tool modifies a critical system file. Untested code is untrustworthy code. TDD ensures that every behavior is verified, every error path is exercised, and regressions are caught immediately. It also enforces the interface-based design required by Principle IV (idiomatic error handling) and makes the codebase safe to refactor.

Dependency Governance

This section codifies the external dependency policy from Principle II into an auditable process.

Source Status Condition
Go standard library Always permitted N/A
golang.org/x/* Permitted Must solve a problem stdlib cannot
dev.emberkom.com/* Permitted Must solve a problem stdlib cannot
All other sources Prohibited by default Requires written justification, code review approval, and an entry in this table

Currently Approved External Dependencies

None. All external functionality is provided by golang.org/x/net/dns/dnsmessage (covered by the golang.org/x/* blanket approval above).

Development Standards

  • Formatting: All Go source files MUST be formatted with gofmt. No exceptions.
  • Vet/Lint: go vet MUST pass with zero findings before code is merged. Additional linting (e.g., staticcheck) is encouraged.
  • Testing: All new and refactored code MUST have test coverage written before the implementation (TDD). Functions that interact with the filesystem or DNS MUST be designed to accept interfaces or parameters that allow testing with fakes/stubs. go test ./... MUST pass with zero failures before merge. See Principle VI for the full TDD policy.
  • Build tags: Platform-specific code MUST use Go build tags (//go:build windows, etc.) and MUST implement identical function signatures across all supported platforms.
  • Commit messages: Follow conventional commit format (e.g., fix:, feat:, refactor:, docs:).

Governance

This constitution is the authoritative source of project standards for ekdns. It supersedes informal conventions, ad-hoc decisions, and prior practices that conflict with its contents.

  • Amendment process: Any change to this constitution MUST be documented with a version bump, a rationale for the change, and an update to the Sync Impact Report at the top of this file.
  • Versioning policy: This constitution follows semantic versioning. MAJOR for principle removals or redefinitions, MINOR for new principles or materially expanded guidance, PATCH for clarifications and non-semantic refinements.
  • Compliance review: All pull requests and code reviews MUST verify that changes comply with the principles defined here. Violations MUST be resolved before merge, or the constitution MUST be amended first.
  • Dependency audits: When dependencies are added or upgraded, the reviewer MUST verify compliance with Principle II and the Dependency Governance table above.

Version: 1.1.1 | Ratified: 2026-03-03 | Last Amended: 2026-03-04