Native Shell
Native Shell은 모든 플랫폼의 앱 안에서 만들어지는 AI UI입니다
Windows Server용 시각적 DNS 정책 관리
Visual DNS policy management for Windows Server. 98 cmdlets, 8 wizard scenarios, 31 MCP tools for AI agents, zero-trust local architecture.
개발 도구TypeScript · PowerShellGitHub @joshuatownsend온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Browser-based GUI for Windows Server DNS — policies, zones, records, DNSSEC, DNS over HTTPS, and blocklists. Next.js frontend + PowerShell bridge, 8 scenario wizards, and an MCP server for AI agents. Works offline as a PowerShell command generator.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A browser-based GUI for Windows Server DNS — policies, zones, records, DNSSEC, DNS over HTTPS, and blocklists. Built with Next.js, TypeScript, and shadcn/ui, it connects to a live DNS server through the included PowerShell bridge for real-time management, and works offline as a PowerShell command generator. Includes 8 scenario wizards and an MCP server for AI agent integration (Claude Code, Cursor, VS Code).
netsh SSL binding, firewall rule, Set-DnsServerEncryptionProtocol, and DNS service restart).nvmrc all target. Node 20 reached
end-of-life on 2026-04-30 and Node 22 is in maintenance until 2027-04-30, so 24 is the current
Active LTS. With nvm, run nvm use in the repo root to pick it up.DnsServer PowerShell module127.0.0.1 by default — never exposed to the networkpowershell -ExecutionPolicy Bypass -File Start-DNSPolicyManager.ps1
This starts both the PowerShell bridge (port 8650) and the Next.js dev server (port 10010), then opens your browser. Frontend dependencies are installed automatically on first run.
# Terminal 1: Start the bridge
powershell -ExecutionPolicy Bypass -File server/bridge.ps1
# Terminal 2: Start the frontend
cd dns-manager
npm install # first time only
npm run dev
Then open http://localhost:10010.
If the bridge isn't running, the app falls back to command generation only — build policies visually and copy the generated PowerShell commands.
The MCP server lets AI agents query your DNS servers through 31 read-only tools via the Model Context Protocol. It connects to the same PowerShell bridge used by the web UI.
cd mcp-server
npm install
npm run build
Or use the launcher:
powershell -ExecutionPolicy Bypass -File Start-DNSPolicyManager.ps1 -MCP
claude mcp add dns-policy-manager -- node /path/to/mcp-server/dist/index.js
With environment variables for a specific server:
claude mcp add dns-policy-manager \
-e BRIDGE_URL=http://127.0.0.1:8650 \
-e DNS_DEFAULT_SERVER=dc01.contoso.com \
-e DNS_CREDENTIAL_MODE=currentUser \
-- node /path/to/mcp-server/dist/index.js
All configuration is via environment variables:
| Variable | Default | Description |
|---|---|---|
BRIDGE_URL | http://127.0.0.1:8650 | PowerShell bridge URL |
BRIDGE_TOKEN | (read from the token file) | Bridge bearer token. Only needed when the MCP server runs as a different user or machine than the bridge |
BRIDGE_TOKEN_FILE | %LOCALAPPDATA%\DNSPolicyManager\bridge.token | Where to read the token the bridge writes on start |
DNS_DEFAULT_SERVER | (none) | Default DNS server hostname |
DNS_SERVER_ID | (none) | Server ID for credential lookup |
DNS_CREDENTIAL_MODE | currentUser | Auth mode: currentUser or savedCredential |
| Category | Tools |
|---|---|
| Zones | dns_list_zones, dns_get_zone_details, dns_get_zone_records, dns_get_zone_aging, dns_get_zone_delegations |
| Policies | dns_list_policies, dns_list_transfer_policies |
| Server Config | dns_get_server_settings, dns_get_forwarders, dns_get_cache_settings, dns_get_recursion_settings, dns_get_blocklist, dns_get_diagnostics, dns_get_statistics, dns_get_rrl, dns_get_rrl_exceptions, dns_get_scavenging, dns_get_root_hints, dns_get_edns, dns_get_encryption, dns_get_global_name_zone, dns_export_server_config |
| DNS Objects | dns_list_subnets, dns_list_zone_scopes, dns_list_recursion_scopes |
| DNSSEC | dns_get_dnssec_settings, dns_get_signing_keys, dns_get_trust_anchors, dns_get_trust_points |
| Offline | dns_generate_policy_commands (works without bridge) |
| Health | dns_check_health |
The MCP server supports two credential modes:
currentUser (default) — uses Kerberos/NTLM from the logged-in user. No setup needed.savedCredential — uses DPAPI-encrypted credentials stored via the web UI. Set DNS_SERVER_ID to the server ID configured in the GUI.Session credentials (username/password) are intentionally not supported in the MCP server — AI agents should not handle raw passwords.
/api/execute endpoint (arbitrary PowerShell) is not exposed through MCPdns_generate_policy_commands tool) go through one shared helper (dns-manager/src/lib/ps-escape.ts, mirrored in mcp-server/src/lib/):
`, $, " and the curly double quotes, with line breaks flattened.The frontend runs in a standard Linux container. The bridge requires Windows PowerShell with the DnsServer module, so it runs on the host machine (or a Windows container).
This is the recommended Docker setup. The frontend container proxies API calls to the bridge running on your Windows machine. The container reaches the host through Docker's network, so the bridge has to listen beyond loopback, and both sides share a token.
Step 1 — Pick a token and put it in a .env file next to docker-compose.yml:
BRIDGE_TOKEN=<a long random string, e.g. from: openssl rand -base64 32>
Step 2 — Start the bridge on the host with the same token:
$env:DNS_BRIDGE_TOKEN = '<the same string>'
powershell -ExecutionPolicy Bypass -File server/bridge.ps1 -BindAddress 0.0.0.0 -AllowRemote
Step 3 — Start the frontend container:
docker compose up -d
Or build and run manually:
docker build -t dns-manager ./dns-manager
docker run -d -p 10010:10010 -e BRIDGE_TOKEN='<the same string>' dns-manager
Open http://localhost:10010 and paste the token on the sign-in page (once per browser).
Compose publishes the port on 127.0.0.1 only. To serve other machines:
10010:10010 instead.ALLOWED_HOSTS. The proxy refuses unknown Host headers so that a DNS-rebinding page can't drive it.By default, the frontend's /api proxy forwards to http://host.docker.internal:8650 (Docker Desktop's host gateway). BRIDGE_URL is read at runtime, so you can point at a different bridge without rebuilding:
docker run -d -p 10010:10010 -e BRIDGE_URL=http://192.168.1.50:8650 -e BRIDGE_TOKEN='<token>' dns-manager
If the bridge runs on a different machine, bind it to a network interface. The bridge refuses to do that unless you pass -AllowRemote:
powershell -ExecutionPolicy Bypass -File server/bridge.ps1 -BindAddress 0.0.0.0 -AllowRemote
Security note: on a network interface, credentials entered in the Server tab and every DNS change cross the network. Plain HTTP sends them unencrypted. Use
-UseHttps(after binding a certificate to the port withnetsh http add sslcert ipport=0.0.0.0:8650 certhash=<thumbprint> appid={<any GUID>}), a TLS-terminating reverse proxy, or an SSH tunnel on anything but a trusted, isolated network. The token stops other clients from using the bridge, but it does not encrypt traffic.
Every bridge endpoint except /api/health requires Authorization: Bearer <token>:
-Token, else DNS_BRIDGE_TOKEN, else the token file from a previous start, else generates a new random one. -RotateToken forces a new one, which also signs every browser out.%LOCALAPPDATA%\DNSPolicyManager\bridge.token, readable only by your account, SYSTEM and Administrators./api proxy and the MCP server read that file automatically. Set BRIDGE_TOKEN when they run as another user or on another machine.The browser never sees the token: it only talks to the frontend's own origin.
To use the proxy, a browser signs in once:
Start-DNSPolicyManager.ps1 signs the browser in automatically. It writes a single-use code (valid 5 minutes) to a user-only file and opens /auth?code=..., so the token itself never appears in a URL or browser history./login page.Sign-in sets an HttpOnly, SameSite=Strict session cookie derived from the token. That cookie is what authenticates calls to /api/*. The Host, Origin and fetch-metadata checks only harden the proxy against cross-site browser attacks. The dev and production frontends listen on 127.0.0.1 only.
docker compose up -d # Start frontend (bridge on host)
docker compose down # Stop
docker compose up -d --build # Rebuild after code changes
docker compose logs -f frontend # View logs
Browser ──same-origin /api──► Next.js (:10010) PowerShell Bridge (:8650)
───────────────── ──────────────────────────
/api proxy ──HTTP + bearer token──► [System.Net.HttpListener]
│
AI Agent (stdio) DNS cmdlets (splatted)
──────────────── Get/Add/Remove/Set-DnsServer*
MCP Server ──HTTP──► │
31 read-only tools Returns JSON
Frontend (dns-manager/):
/api/[...path] route handler adds the bridge token and forwards to the bridge (Host/Origin/fetch-metadata checks guard it)Bridge (server/bridge.ps1):
HttpListener with regex-based routing and runspace pool for concurrent request handling/api/health; saved and session credentials are bound to the hostname they were entered for| Method | Path | Purpose |
|---|---|---|
| GET | /api/health | Ping + DNS module check |
| POST | /api/connect | Test DNS server, list zones |
| GET | /api/zones | List DNS zones |
| GET | /api/zones/{name} | Zone details |
| GET | /api/zones/{name}/records | List zone records |
| POST | /api/zones/{name}/records | Add record |
| PUT | /api/zones/{name}/records | Update record |
| DELETE | /api/zones/{name}/records | Delete record |
| PUT | /api/zones/{name}/settings | Edit zone settings |
| GET | /api/policies | List policies |
| POST | /api/policies | Create policy |
| DELETE | /api/policies/{name} | Remove policy |
| PUT | /api/policies/{name}/state | Enable/disable policy |
| POST | /api/policies/multi | Create on multiple servers |
| POST | /api/policies/copy | Copy policies between servers |
| GET | /api/transferpolicies | List zone transfer policies |
| POST | /api/transferpolicies | Create zone transfer policy |
| DELETE | /api/transferpolicies/{name} | Remove zone transfer policy |
| GET | /api/subnets | List client subnets |
| POST | /api/subnets | Create client subnet |
| DELETE | /api/subnets/{name} | Delete client subnet |
| GET | /api/zonescopes |
Start-DNSPolicyManager.ps1 Launcher: starts bridge + frontend (+ MCP with -MCP flag)
docker-compose.yml Docker Compose for containerized deployment
server/
bridge.ps1 PowerShell HTTP bridge (localhost:8650)
start.bat Double-click launcher for bridge only
mcp-server/ MCP server for AI agents
src/
index.ts Entry point, stdio transport
bridge-client.ts HTTP client for bridge REST API
tools/
shared.ts Shared Zod schemas (ServerParamsSchema)
zones.ts Zone query tools (5)
policies.ts Policy query tools (2)
server-config.ts Server config query tools (15)
objects.ts DNS object query tools (3)
security.ts DNSSEC query tools (4)
command-gen.ts Offline PowerShell command generation (1)
dns-manager/ Next.js frontend
Dockerfile Multi-stage Alpine build (standalone output)
src/
app/ App Router pages (12 tabs + help)
server/page.tsx Server management + configuration dashboard
objects/page.tsx DNS Objects (subnets, scopes)
zones/page.tsx Zone browser + record CRUD + lifecycle + filters
policies/page.tsx Policy list
create/page.tsx Create Policy form
blocklists/page.tsx Blocklists (quick block, bulk import, GQBL)
wizards/page.tsx Scenario wizards (8 scenarios)
dnssec/page.tsx DNSSEC management
doh/page.tsx DNS over HTTPS (inbound DoH config + setup-script generator)
resolvers/page.tsx Resolvers (adapter DNS, forwarders, topology)
backup/page.tsx Backup & Import + zone/config export
powershell/page.tsx PowerShell output
help/[slug]/page.tsx Help documentation viewer
components/
app-shell.tsx Layout shell, header, bridge status
tab-nav.tsx Tab navigation
help-panel.tsx Slide-over help panel
ui/ shadcn/ui components
lib/
api.ts Typed API client (~80 methods)
store.ts Zustand store
types.ts TypeScript interfaces
help-mapping.ts Route-to-help-doc mapping
wizards/
scenarios.ts Scenario definitions
command-generator.ts PowerShell command generation
public/help/ Help documentation (Markdown)
docs/help/ Source help documentation
The app wraps ~98 of ~120 cmdlets in the DnsServer PowerShell module with dedicated UI. The remaining cmdlets are accessible through the PowerShell tab via the execute endpoint, which allows any DnsServer cmdlet.
Commands sent to the execute endpoint are checked against an allowlist before they run: DnsServer cmdlets (plus Resolve-DnsName, Test-NetConnection, Get-DnsClientServerAddress and Get-Service), optionally piped to Select-Object, Sort-Object, Format-Table, Format-List or Out-String. Arguments may be literals, arrays and hashtables; script blocks are accepted only as calculated properties or sort keys for those formatters, and the only variables are $_, $PSItem, $true, $false and $null. Method calls, casts, $( ) subexpressions, assignments, redirection and control flow are rejected.
Cmdlets without dedicated UI (reachable via execute):
| Group | Cmdlets | Reason |
|---|---|---|
| Type-specific record creation | Add-DnsServerResourceRecordA, ...AAAA, ...CName, ...MX, ...Ptr, ...DnsKey, ...DS | Already handled by the generic Add-DnsServerResourceRecord |
| Record aging | Set-DnsServerResourceRecordAging | Bulk operation, run via execute |
| Key rollover | Invoke-DnsServerSigningKeyRollover, Step-DnsServerSigningKeyRollover | One-off DNSSEC operations |
| Zone restore | Restore-DnsServerPrimaryZone, Restore-DnsServerSecondaryZone | Rarely used, forces reload from AD/file |
| Zone sync | Sync-DnsServerZone | Flushes memory to storage |
| Imports | Import-DnsServerRootHint, Import-DnsServerResourceRecordDS, Import-DnsServerTrustAnchor | File-based import operations |
| Key storage | Show-DnsServerKeyStorageProvider | Read-only diagnostic |
| Directory partitions | Get/Add/Remove/Register/Unregister-DnsServerDirectoryPartition | AD infrastructure, rarely changed |
| Virtualization | Get/Add/Set/Remove-DnsServerVirtualizationInstance | Server 2016+ niche feature |
Chrome, Firefox, and Edge (current versions).
| Service | Default Port | Override |
|---|---|---|
| Frontend (dev) | 10010 | Edit package.json dev script |
| Frontend (Docker) | 10010 | Change in docker-compose.yml ports |
| Bridge | 8650 | -Port parameter on bridge.ps1 |
Windows Hyper-V/Docker dynamically reserves port ranges (check netsh int ipv4 show excludedportrange) that can conflict with port 8650. If the bridge fails to bind, use -Port to specify an open one.
| List zone scopes |
| POST | /api/zonescopes | Create zone scope |
| DELETE | /api/zonescopes/{name} | Delete zone scope |
| POST | /api/zonescopes/records | Add record to zone scope |
| GET | /api/recursionscopes | List recursion scopes |
| POST | /api/recursionscopes | Create recursion scope |
| PUT | /api/recursionscopes/{name} | Update recursion scope |
| DELETE | /api/recursionscopes/{name} | Delete recursion scope |
| POST | /api/credentials/store | Store DPAPI credential |
| POST | /api/credentials/session | Store session credential |
| GET | /api/credentials/check | Check credential exists |
| DELETE | /api/credentials/{id} | Delete credential |
| POST | /api/backup | Export policies as JSON |
| POST | /api/server/resolvers | Start DNS client resolver discovery job |
| GET | /api/server/resolvers | Poll resolver discovery job result |
| GET | /api/export/serverconfig | Export full server config as JSON |
| POST | /api/export/allzones | Export all primary zones |
| POST | /api/execute | Run allowlisted DNS cmdlet |