Files

7.0 KiB

Data Model: Safety, Reliability & Idiomatic Refactor

Feature Branch: 001-safety-reliability-refactor
Date: 2026-03-03

Entities

HostsFile

Represents the system hosts file as parsed content sections.

Field Type Description Constraints
Path string Absolute path to the hosts file OS-specific; determined by platform package
OriginalContent []string Raw lines from the file as read Immutable after read; used for change detection
PrefixContent []string Lines before the managed block start marker Preserved verbatim on write
ManagedContent []string Lines between start and end markers (exclusive) Modified by add/delete operations
PostfixContent []string Lines after the managed block end marker Preserved verbatim on write
HasManagedBlock bool Whether a valid managed block was found true if both start and end markers present in order

Validation Rules:

  • If start marker is present, end marker must also be present (and vice versa).
  • End marker must appear after start marker.
  • Duplicate start or end markers are not permitted.
  • Start marker at line index 0 is a valid position (must use sentinel, not zero-check).

State Transitions:

[File Read] → Parse → [No Managed Block] → Add entries → [Has Managed Block] → Write
                    → [Has Managed Block] → Add/Delete entries → [Updated Managed Block] → Write
                    → [Corrupt Block] → ERROR (refuse to modify)

DNSEntry

A single IP-to-hostname mapping in the hosts file.

Field Type Description Constraints
IP string IPv4 address Must be a valid IPv4 address; result of DNS resolution
Hostname string Fully qualified domain name As provided by user (without trailing dot)

String Representation: <IP>\t<Hostname> (tab-separated, one per line in hosts file)

Validation Rules:

  • IP must be non-empty and a valid IPv4 address.
  • Hostname must be non-empty and not contain whitespace.
  • Entries are deduplicated by their full string representation (IP + hostname pair).

ManagedBlock

The delimited section of the hosts file owned by this tool.

Field Type Description Constraints
StartMarker string Comment line marking block start # DNSHelper <<-> START CONFIG (constant)
EndMarker string Comment line marking block end # DNSHelper <->> END CONFIG (constant)
Entries []DNSEntry DNS entries within the block Zero or more; deduplicated

Rules:

  • The managed block is only written if there are entries to include.
  • When all entries are removed (delete all), the entire block (markers included) is removed.
  • The block is always written as: start marker, entries (one per line), end marker.

ResolveResult

The outcome of resolving a single hostname against a DNS server.

Field Type Description Constraints
Hostname string The hostname that was queried As provided by user
IPs []string Resolved IPv4 addresses Deduplicated; empty if resolution failed
Error error Resolution error, if any Non-nil on failure (timeout, NXDOMAIN, etc.)

State Transitions:

[Query Sent] → A records returned → [Success: IPs populated]
             → CNAME returned → Follow chain (max 10 depth) → [Success or Error]
             → NXDOMAIN / timeout / error → [Failure: Error populated]

BackupFile

A temporary safety copy of the hosts file created before modification.

Field Type Description Constraints
Path string Absolute path to backup file In executable's directory
SourcePath string Path of the file that was backed up The hosts file path
Timestamp string Date stamp portion of filename Format: YYYYMMDD
RandomSuffix string Short random string for uniqueness 4 hex characters (e.g., a7f3)

Naming Convention: hosts.bak.<Timestamp>-<RandomSuffix> (e.g., hosts.bak.20260303-a7f3)

Lifecycle:

[Pre-Write] → Create backup copy → [Write Success] → Delete backup
                                  → [Write Failure] → Original intact, clean up backup

LockFile

A file-based mutex for serializing concurrent access.

Field Type Description Constraints
Path string Absolute path to lock file In executable's directory; .dns-helper.lock
PID int Process ID of the lock holder Written for debugging; not used for staleness
CreatedAt time.Time When the lock was acquired Approximated by file ModTime

Parameters:

  • Stale timeout: 2 minutes
  • Retry interval: 200ms
  • Max wait: 2 seconds (10 retries)

Lifecycle:

[Pre-Lock] → Attempt create (O_EXCL) → [Acquired] → Hold during R/M/W → [Release: delete]
                                      → [Exists, not stale] → Retry (up to 2s) → [Acquired or Error]
                                      → [Exists, stale] → Break stale → Retry → [Acquired or Error]

Relationships

CLI (main.go)
 ├── validates input (host, server, subcommand)
 ├── calls Resolver to get ResolveResult[] (before lock)
 ├── acquires LockFile
 ├── reads HostsFile → parses ManagedBlock
 ├── modifies ManagedBlock (add DNSEntry[] / delete by hostname)
 ├── creates BackupFile
 ├── writes HostsFile atomically (temp + rename)
 ├── deletes BackupFile on success
 └── releases LockFile

Operation Flows

Add Flow

  1. Validate: -host and -server required and non-empty.
  2. Resolve: For each hostname, query DNS server → collect ResolveResult[].
  3. Lock: Acquire lock file.
  4. Read: Read hosts file → parse into sections.
  5. Modify: Remove existing entries for the specified hostnames from managed content. Add new DNSEntry[] from resolve results.
  6. Deduplicate: Remove duplicate entries by full line content.
  7. Backup: Copy current hosts file to backup location.
  8. Write: Assemble full content (prefix + managed block + postfix) → write to temp file → rename over hosts file.
  9. Cleanup: Delete backup on success. Release lock.
  10. Report: Print summary (entries added/updated). Exit non-zero if any hostname failed to resolve.

Delete Flow

  1. Validate: -host required and non-empty, OR all keyword.
  2. Lock: Acquire lock file.
  3. Read: Read hosts file → parse into sections.
  4. Modify: If specific hosts — remove matching lines from managed content. If all — clear managed content entirely (block markers will not be written).
  5. Backup: Copy current hosts file to backup location.
  6. Write: Assemble full content → write to temp file → rename.
  7. Cleanup: Delete backup on success. Release lock.
  8. Report: Print summary (entries removed). Report if no entries were found.