···11+# Binaries
22+plcdns
33+*.exe
44+*.dll
55+*.so
66+*.dylib
77+88+# Test binary
99+*.test
1010+1111+# Output of the go coverage tool
1212+*.out
1313+coverage.html
1414+1515+# Dependency directories
1616+vendor/
1717+1818+# Go workspace file
1919+go.work
2020+2121+# IDE
2222+.vscode/
2323+.idea/
2424+*.swp
2525+*.swo
2626+*~
2727+2828+# OS
2929+.DS_Store
3030+Thumbs.db
+21
LICENSE
···11+MIT License
22+33+Copyright (c) 2025 tree
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+307
README.md
···11+# plcdns
22+33+**PLC Directory over DNS** - A DNS server that resolves Bluesky/AT Protocol DID PLC identifiers to their associated metadata via DNS TXT records.
44+55+## Features
66+77+- 🔍 **Handle Resolution** - Resolve DIDs to their AT Protocol handles
88+- 🌐 **PDS Discovery** - Find Personal Data Server endpoints
99+- 🔧 **Service Resolution** - Resolve any service from DID documents (PDS, labelers, etc.)
1010+- 🔑 **Public Key Lookup** - Retrieve verification public keys
1111+- ⚡ **Caching** - 5-minute cache to reduce PLC directory load
1212+- 🐳 **Docker Support** - Easy deployment with Docker
1313+- ✅ **Comprehensive Tests** - Full test coverage
1414+1515+## Installation
1616+1717+### From Source
1818+1919+```bash
2020+# Clone the repository
2121+git clone https://github.com/yourusername/plcdns.git
2222+cd plcdns
2323+2424+# Install dependencies
2525+go mod download
2626+2727+# Build
2828+go build -o plcdns
2929+3030+# Run
3131+./plcdns -port 8053
3232+```
3333+3434+### Using Docker
3535+3636+```bash
3737+# Build
3838+docker build -t plcdns .
3939+4040+# Run
4141+docker run -p 8053:8053 -e DNS_PORT=8053 plcdns
4242+```
4343+4444+### Using Go Install
4545+4646+```bash
4747+go install github.com/yourusername/plcdns@latest
4848+```
4949+5050+## Usage
5151+5252+### Starting the Server
5353+5454+```bash
5555+# Default port (8053)
5656+./plcdns
5757+5858+# Custom port via flag
5959+./plcdns -port 9053
6060+6161+# Custom port via environment variable
6262+DNS_PORT=9053 ./plcdns
6363+6464+# Custom PLC directory
6565+./plcdns -port 8053 -plc https://plc.directory
6666+```
6767+6868+### Query Formats
6969+7070+The server supports four types of queries using different subdomain prefixes:
7171+7272+| Query Type | Format | Returns | Example |
7373+|:-----------|:-------|:--------|:--------|
7474+| Handle | `_handle.<did>.plc.atscan.net` | AT Protocol handle | `test.bsky.social` |
7575+| PDS | `_pds.<did>.plc.atscan.net` | PDS endpoint URL | `https://bsky.social` |
7676+| Labeler | `_labeler.<did>.plc.atscan.net` | Labeler service URL | `https://mod.bsky.app` |
7777+| Public Key | `_pubkey.<did>.plc.atscan.net` | Public key (multibase) | `zQ3sh...` |
7878+7979+### Query Examples
8080+8181+```bash
8282+# Using dig
8383+dig @localhost -p 8053 _handle.z72i7hdynmk6r22z27h6tvur.plc.atscan.net TXT
8484+dig @localhost -p 8053 _pds.z72i7hdynmk6r22z27h6tvur.plc.atscan.net TXT
8585+dig @localhost -p 8053 _labeler.ar7c4by46qjdydhdevvrndac.plc.atscan.net TXT
8686+dig @localhost -p 8053 _pubkey.z72i7hdynmk6r22z27h6tvur.plc.atscan.net TXT
8787+8888+# Using nslookup
8989+nslookup -type=TXT _handle.z72i7hdynmk6r22z27h6tvur.plc.atscan.net localhost -port=8053
9090+9191+# Using host
9292+host -t TXT _pds.z72i7hdynmk6r22z27h6tvur.plc.atscan.net localhost -p 8053
9393+```
9494+9595+### Example Response
9696+9797+```bash
9898+$ dig @localhost -p 8053 _handle.z72i7hdynmk6r22z27h6tvur.plc.atscan.net TXT +short
9999+"bsky.app"
100100+101101+$ dig @localhost -p 8053 _pds.z72i7hdynmk6r22z27h6tvur.plc.atscan.net TXT +short
102102+"https://bsky.social"
103103+```
104104+105105+## Configuration
106106+107107+### Command Line Flags
108108+109109+| Flag | Default | Description |
110110+|:-----|:--------|:------------|
111111+| `-port` | `8053` | DNS server port |
112112+| `-plc` | `https://plc.directory` | PLC directory URL |
113113+114114+### Environment Variables
115115+116116+| Variable | Description |
117117+|:---------|:------------|
118118+| `DNS_PORT` | Override default port (8053) |
119119+120120+### Cache Settings
121121+122122+- **TTL**: 300 seconds (5 minutes) for DNS records
123123+- **Cache Duration**: 5 minutes for DID documents
124124+- **Cache Type**: In-memory map
125125+126126+## Testing
127127+128128+```bash
129129+# Run all tests
130130+go test -v
131131+132132+# Run with coverage
133133+go test -v -cover
134134+135135+# Generate coverage report
136136+go test -coverprofile=coverage.out
137137+go tool cover -html=coverage.out
138138+139139+# Run with race detector
140140+go test -v -race
141141+```
142142+143143+## API Reference
144144+145145+### DID Document Structure
146146+147147+The server fetches DID documents from the PLC directory with the following structure:
148148+149149+```json
150150+{
151151+ "@context": ["https://www.w3.org/ns/did/v1"],
152152+ "id": "did:plc:z72i7hdynmk6r22z27h6tvur",
153153+ "alsoKnownAs": ["at://bsky.app"],
154154+ "verificationMethod": [{
155155+ "id": "did:plc:z72i7hdynmk6r22z27h6tvur#atproto",
156156+ "type": "Multikey",
157157+ "controller": "did:plc:z72i7hdynmk6r22z27h6tvur",
158158+ "publicKeyMultibase": "zQ3sh..."
159159+ }],
160160+ "service": [{
161161+ "id": "#atproto_pds",
162162+ "type": "AtprotoPersonalDataServer",
163163+ "serviceEndpoint": "https://bsky.social"
164164+ }]
165165+}
166166+```
167167+168168+## Deployment
169169+170170+### Systemd Service
171171+172172+Create `/etc/systemd/system/plcdns.service`:
173173+174174+```ini
175175+[Unit]
176176+Description=PLC Directory DNS Server
177177+After=network.target
178178+179179+[Service]
180180+Type=simple
181181+User=plcdns
182182+ExecStart=/usr/local/bin/plcdns -port 53
183183+Restart=always
184184+Environment="DNS_PORT=53"
185185+186186+[Install]
187187+WantedBy=multi-user.target
188188+```
189189+190190+Enable and start:
191191+192192+```bash
193193+sudo systemctl enable plcdns
194194+sudo systemctl start plcdns
195195+```
196196+197197+### Docker Compose
198198+199199+```yaml
200200+version: '3.8'
201201+202202+services:
203203+ plcdns:
204204+ build: .
205205+ ports:
206206+ - "53:53/udp"
207207+ - "53:53/tcp"
208208+ environment:
209209+ - DNS_PORT=53
210210+ restart: unless-stopped
211211+```
212212+213213+### Running on Port 53
214214+215215+To run on the standard DNS port (53), you need elevated privileges:
216216+217217+**Linux (with capabilities):**
218218+```bash
219219+sudo setcap 'cap_net_bind_service=+ep' ./plcdns
220220+./plcdns -port 53
221221+```
222222+223223+**Using sudo:**
224224+```bash
225225+sudo ./plcdns -port 53
226226+```
227227+228228+## Architecture
229229+230230+```
231231+┌─────────────┐
232232+│ DNS Client │
233233+└──────┬──────┘
234234+ │ Query: _handle.<did>.plc.atscan.net
235235+ ▼
236236+┌─────────────────┐
237237+│ DNS Server │
238238+│ (Port 8053) │
239239+└────────┬────────┘
240240+ │
241241+ ├─── Cache Check (5 min TTL)
242242+ │
243243+ ▼
244244+┌─────────────────┐
245245+│ PLC Directory │
246246+│ (plc.directory) │
247247+└─────────────────┘
248248+ │
249249+ ▼ DID Document
250250+┌─────────────────┐
251251+│ Parse & Return │
252252+│ TXT Record │
253253+└─────────────────┘
254254+```
255255+256256+## Performance
257257+258258+- **Cache Hit**: ~1ms response time
259259+- **Cache Miss**: ~50-200ms (depends on PLC directory)
260260+- **Concurrent Requests**: Supports thousands of concurrent queries
261261+- **Memory Usage**: ~10-50MB depending on cache size
262262+263263+## Contributing
264264+265265+Contributions are welcome! Please:
266266+267267+1. Fork the repository
268268+2. Create a feature branch (`git checkout -b feature/amazing-feature`)
269269+3. Commit your changes (`git commit -m 'Add amazing feature'`)
270270+4. Push to the branch (`git push origin feature/amazing-feature`)
271271+5. Open a Pull Request
272272+273273+### Code Style
274274+275275+- Follow standard Go conventions
276276+- Run `go fmt` before committing
277277+- Add tests for new features
278278+- Update documentation as needed
279279+280280+## License
281281+282282+MIT License - see [LICENSE](LICENSE) file for details
283283+284284+## Related Projects
285285+286286+- [AT Protocol](https://atproto.com/) - Authenticated Transfer Protocol
287287+- [Bluesky](https://bsky.app/) - Social network built on AT Protocol
288288+- [PLC Directory](https://plc.directory/) - DID PLC registry
289289+290290+## Acknowledgments
291291+292292+- Built with [miekg/dns](https://github.com/miekg/dns) - DNS library for Go
293293+- Inspired by the AT Protocol ecosystem
294294+295295+## Roadmap
296296+297297+- [ ] DNSSEC support
298298+- [ ] Prometheus metrics endpoint
299299+- [ ] Redis cache backend option
300300+- [ ] Rate limiting
301301+- [ ] Multiple PLC directory fallbacks
302302+- [ ] Web UI for testing queries
303303+- [ ] REST API endpoint
304304+305305+---
306306+307307+**Made with ❤️ for the AT Protocol community**