7.0 KiB
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
- Validate:
-hostand-serverrequired and non-empty. - Resolve: For each hostname, query DNS server → collect
ResolveResult[]. - Lock: Acquire lock file.
- Read: Read hosts file → parse into sections.
- Modify: Remove existing entries for the specified hostnames from managed content. Add new
DNSEntry[]from resolve results. - Deduplicate: Remove duplicate entries by full line content.
- Backup: Copy current hosts file to backup location.
- Write: Assemble full content (prefix + managed block + postfix) → write to temp file → rename over hosts file.
- Cleanup: Delete backup on success. Release lock.
- Report: Print summary (entries added/updated). Exit non-zero if any hostname failed to resolve.
Delete Flow
- Validate:
-hostrequired and non-empty, ORallkeyword. - Lock: Acquire lock file.
- Read: Read hosts file → parse into sections.
- Modify: If specific hosts — remove matching lines from managed content. If
all— clear managed content entirely (block markers will not be written). - Backup: Copy current hosts file to backup location.
- Write: Assemble full content → write to temp file → rename.
- Cleanup: Delete backup on success. Release lock.
- Report: Print summary (entries removed). Report if no entries were found.