|
|
|
DNS isolation for the editor.land private network.
Development environments that communicate over the public internet expose services to unnecessary risk. DNS resolution for local services goes through external resolvers, leaking information about the development setup.
"Nothing leaks to the public internet. A clean network boundary between the editor and the outside world."
Mist provides DNS isolation and private network resolution for the Land
Code Editor. It creates a secure DNS sandbox that resolves all *.editor.land
domains locally to 127.0.0.1, ensuring that all private network communication
remains local and secure. External DNS queries are restricted to a strict
allowlist, preventing sidecars from accessing arbitrary external hosts.
Editor components need to discover each other on the private network, but
standard DNS resolution leaks queries to external resolvers. Mist solves this by
running a local authoritative DNS server for the editor.land zone - every
query stays on the machine, and nothing escapes to the public internet.
Mist is engineered to:
- Provide Private DNS Resolution - Operate a local DNS server authoritative
for the
editor.landzone, resolving all subdomains to127.0.0.1for secure local communication. - Enforce Forward Security - Implement a forward allowlist that only
permits DNS resolution to specific, trusted external domains (e.g.,
update.editor.land). - Bridge Sky 🌌 and
Cocoon 🦋 Directly - Run a local-first
JSON-RPCWebSockettransport (Source/WebSocket.rs) with shared-secret authentication, removing the Tauri-invoke +gRPCdouble hop for high-frequency extension-API traffic. - Enable Sidecar Isolation - Allow
Node.jssidecars (like Cocoon 🦋) to use the local DNS server via a custom DNS override, ensuring they cannot access arbitrary external hosts.
Private DNS Zone - Authoritative zone for *.editor.land domains. All
subdomains resolve to 127.0.0.1, creating a fully isolated private network for
the editor's internal services. No DNS queries ever leave the machine.
Forward Security - Allowlist-based DNS forwarding prevents sidecars from
reaching arbitrary external hosts. Only explicitly trusted domains (such as
update.editor.land) can be resolved externally. All other queries are refused
by default.
Dynamic Port Allocation - Automatically finds available ports using
portpicker, avoiding port conflicts with other services. Prefers a
configurable starting port and falls back to system-assigned ports when needed.
WebSocket Transport - Source/WebSocket.rs implements a local-first
JSON-RPC channel for the direct Sky↔Cocoon path, replacing the Tauri-invoke +
Mountain ⛰️ gRPC double hop for the ~95% of IPC traffic that
is extension-API calls. Every spawn gets a random 32-byte shared secret,
presented by clients via the X-Land-Secret header, a ?secret= query
parameter, or a Sec-WebSocket-Protocol entry (browsers cannot set custom
upgrade headers); connections presenting none of the three are rejected with
403 Forbidden. Reconnect uses exponential backoff (100ms → 5s cap, 30s
give-up).
Loopback Binding - The DNS server binds exclusively to 127.0.0.1, ensuring
no external host can query the private DNS server. Combined with the forward
allowlist, this creates a complete network boundary.
| Principle | Description | Key Components |
|---|---|---|
| Network Isolation | All editor.land DNS resolution stays local. The server binds to 127.0.0.1 only, and external queries require explicit allowlisting. |
Server, ForwardSecurity |
| Authenticated Transport | The WebSocket JSON-RPC channel requires a per-spawn shared secret on every upgrade request; connections without it are refused before any message is read. |
WebSocket, SharedSecret |
| Minimal Surface | A flat module structure with no unnecessary abstractions. Each module has a single, well-defined responsibility with clear public APIs. | Library, Server, Zone, Resolver |
| Composability | Independent DNS resolver for use by other Land components. Any consumer can create a resolver pointed at the local DNS server without additional configuration. | Resolver, LandDnsResolver |
graph LR
classDef mist fill:#e0f0ff,stroke:#2471a3,stroke-width:2px,color:#001030;
classDef zone fill:#d4f5d4,stroke:#27ae60,stroke-width:1px,color:#0a3a0a;
classDef forward fill:#fff3c0,stroke:#f39c12,stroke-width:1px,stroke-dasharray:5 5,color:#5a3e00;
classDef consumer fill:#f0d0ff,stroke:#9b59b6,stroke-width:1px,color:#2c0050;
classDef external fill:#ebebeb,stroke:#888,stroke-width:1px,stroke-dasharray:5 5,color:#333;
subgraph CONSUMERS["Land Components - DNS + WebSocket Clients"]
Mountain["Mountain ⛰️\nstarts Mist, reads DnsPort"]:::consumer
Cocoon["Cocoon 🦋\nNode.js sidecar (DNS override + WebSocket peer)"]:::consumer
Air["Air 🪁\nHTTP client with custom DNS"]:::consumer
Sky["Sky 🌌\nbrowser workbench (WebSocket peer)"]:::consumer
end
subgraph MIST["Mist 🌫️ - Local DNS + WebSocket Server (127.0.0.1:PORT)"]
direction TB
Server["Server.rs - Hickory DNS\nUDP + TCP listeners"]:::mist
Zone["Zone.rs - Authoritative Zone\n*.editor.land → 127.0.0.1\nSOA/NS/A records"]:::zone
Forward["ForwardSecurity.rs - Allowlist\nupdate.editor.land only"]:::forward
Resolver["Resolver.rs - LandDnsResolver"]:::mist
WSTransport["WebSocket.rs - Sky↔Cocoon JSON-RPC\nshared-secret auth"]:::mist
Server --> Zone
Server --> Forward
Server --> Resolver
end
subgraph INTERNET["External ☁️"]
UpdateServer["update.editor.land\nallowlisted only"]:::external
end
Mountain -- spawns + DnsPort --> Server
Cocoon -- DNS queries --> Server
Air -- DNS queries --> Resolver
Cocoon <--> WSTransport
Sky <--> WSTransport
Forward -- forwards allowed --> UpdateServer
Connection paths:
| Path | Protocol | Use Case |
|---|---|---|
| Mountain ⛰️ → Mist 🌫️ | Process spawn + port handoff | Application initialization, reads DnsPort managed state |
| Cocoon 🦋 → Mist 🌫️ | DNS over UDP/TCP to 127.0.0.1 |
Node.js sidecar DNS resolution for editor.land domains |
| Air 🪁 → Mist 🌫️ | LandDnsResolver (Hickory client) |
HTTP client DNS configured to use local resolver |
| Cocoon 🦋 ↔ Mist 🌫️ | JSON-RPC over WebSocket |
Direct Sky↔Cocoon extension-API traffic, shared-secret auth |
| Sky 🌌 ↔ Mist 🌫️ | JSON-RPC over WebSocket |
Browser workbench side of the same direct transport |
| Mist 🌫️ → External | UDP DNS (allowlisted only) | Forwarding queries for update.editor.land |
| Component | Path | Description |
|---|---|---|
| Library Entry | Source/Library.rs |
Main library entry point, exports public API and manages DNS server state. |
| DNS Server | Source/Server.rs |
DNS server implementation using Hickory, handles UDP/TCP listeners and catalog management. |
| Zone Configuration | Source/Zone.rs |
DNS zone configuration for editor.land, including record (SOA/NS/A) definitions and authority creation. |
| DNS Resolver | Source/Resolver.rs |
LandDnsResolver for reqwest DNS override, routing *.editor.land to loopback; TokioResolver is a stub. |
| Forward Security | Source/ForwardSecurity.rs |
Forward allowlist management, restricts which external domains can be resolved. |
| WebSocket Transport | Source/WebSocket.rs |
JSON-RPC WebSocket server/client for the direct Sky↔Cocoon path, with shared-secret auth and reconnect. |
Mist/
├── Source/
│ ├── Library.rs # Library root, DNS server lifecycle
│ ├── Server.rs # Hickory DNS server (UDP/TCP listeners)
│ ├── Zone.rs # Authoritative editor.land zone (SOA/NS/A)
│ ├── Resolver.rs # LandDnsResolver for consumer use
│ ├── ForwardSecurity.rs # Allowlist-based forward DNS
│ └── WebSocket.rs # JSON-RPC over WebSocket transport (Sky↔Cocoon)
├── tests/
│ └── integration.rs # Integration test suite
├── Documentation/
│ ├── GitHub/
│ │ ├── Architecture.md # Internal module design
│ │ └── DeepDive.md # In-depth technical details
│ └── Rust/
│ └── doc/ # Cargo doc output
└── Cargo.toml
Mist provides the DNS isolation that secures the Land private network. All
*.editor.land domains resolve to 127.0.0.1, preventing external network
leakage. External DNS queries are restricted to a strict allowlist.
Mist is part of the networking/IPC connectivity stack alongside
Air 🪁 (background daemon, uses Mist's DNS resolver for its HTTP
client) and Vine 🌿 (gRPC protocol layer).
| Consumer | How Mist is Used |
|---|---|
| Mountain ⛰️ | Starts the DNS server during application initialization and provides the port to other components via the DnsPort managed state. |
| Air 🪁 | Uses the DNS server for secure HTTP requests, configuring HTTP clients to use the local DNS resolver. |
| SideCar 🚃 | Spawns Node.js sidecars with DNS override configuration, ensuring all DNS queries go through the local server. |
| Cocoon 🦋 | The Node.js extension host can resolve editor.land domains via the local DNS server for gRPC communication with Mountain. |
Authoritative Zone: editor.land - All subdomains of editor.land resolve
to 127.0.0.1:
code.editor.land→127.0.0.1api.editor.land→127.0.0.1*.editor.land→127.0.0.1
Forward Allowlist - Only allowlisted external domains can be resolved:
update.editor.land- For application updates
All other external queries are refused by default.
WebSocket Path - Source/WebSocket.rs runs a JSON-RPC channel alongside
the DNS server for the direct Sky↔Cocoon path:
- Every spawn generates a random 32-byte
SharedSecret - Clients authenticate via
X-Land-Secretheader,?secret=query parameter, orSec-WebSocket-Protocolentry - Connections presenting none of the three are rejected with
403 Forbidden - Reconnect uses exponential backoff (100ms → 5s cap, gives up after 30s)
- Rust 1.75 or later
- No system DNS configuration required - Mist binds to
127.0.0.1only
cd Element/Mist
cargo build --release# Run all tests
cargo test
# Run integration tests
cargo test --test integration
# Run with logging
RUST_LOG=debug cargo testuse Mist::start;
// Start on preferred port 5380
let Port = Mist::start(5380)?;
// Or let the system select an available port
let Port = Mist::start(0)?;
println!("DNS server running on 127.0.0.1:{}", Port);use Mist::resolver::{land_resolver, LandDnsResolver};
// Simple resolver
let Port = Mist::dns_port();
let Resolver = land_resolver(Port);
// Or with explicit interface
let Resolver = LandDnsResolver::new(Port);| Crate | Purpose |
|---|---|
hickory-server |
DNS server implementation (dnssec-ring feature enabled) |
hickory-proto |
DNS protocol implementation (dnssec-ring feature enabled) |
hickory-client |
DNS client for resolvers |
ring |
Cryptographic primitives (available for future DNSSEC signing) |
tokio |
Async runtime |
tokio-tungstenite |
WebSocket protocol for the Sky↔Cocoon transport |
futures-util |
Stream/sink combinators for the WebSocket server |
hex |
Hex encoding for the WebSocket shared secret |
anyhow |
Error handling |
tracing |
Logging and instrumentation |
once_cell |
Thread-safe lazy initialization |
portpicker |
Random port selection |
async-trait |
Async trait support |
reqwest |
HTTP client with DNS integration |
rand |
Shared-secret generation |
Common |
Shared workspace types (workspace-internal crate) |
Mist enforces security at multiple layers:
| Layer | Mechanism |
|---|---|
| Network isolation | All editor.land domains resolve to 127.0.0.1, preventing any external network access for private services. |
| Forward allowlist | External DNS queries are restricted to a trusted allowlist, preventing sidecars from accessing arbitrary external hosts. |
| Loopback binding | Both the DNS server and the WebSocket server only bind to 127.0.0.1/loopback, preventing external access. |
| WebSocket auth | Every spawn's SharedSecret (32 random bytes) is required on the upgrade request; unauthenticated connections receive 403 Forbidden before any RPC message is read. |
Mist is designed to be compatible with:
| Target | Integration |
|---|---|
| Mountain ⛰️ | Starts the DNS server at initialization and distributes DnsPort via managed state |
| Air 🪁 | Uses LandDnsResolver as reqwest DNS override for secure HTTP requests |
| Cocoon 🦋 | Resolves editor.land domains through the local DNS server for gRPC IPC; also a WebSocket JSON-RPC peer |
| Sky 🌌 | Browser workbench peer on the direct WebSocket JSON-RPC path |
| SideCar 🚃 | Spawns Node.js sidecars with DNS override pointing at the local server |
- Architecture Overview
- Internal module structure
- Deep Dive
- In-depth technical details
- Land Documentation - Complete documentation index
- Air 🪁 - Background daemon that consumes Mist for HTTP client DNS - GitHub
- Vine 🌿 -
gRPCprotocol layer - GitHub - Mountain ⛰️ - Main application process - GitHub
- CHANGELOG
- Version history
This project is released into the public domain under the Creative Commons CC0
Universal license. You are free to use, modify, distribute, and build upon
this work for any purpose, without any restrictions. For the full legal text,
see the LICENSE
file.
Stay updated with our progress! See
CHANGELOG.md
for a history of changes.
Land 🏞️ is proud to be an open-source endeavor. Our journey is significantly supported by the organizations and projects that believe in the future of open-source software.
This project is funded through NGI0 Commons Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet program. Learn more at the NLnet project page.
| Land | PlayForm | NLnet | NGI0 Commons Fund |
|---|---|---|---|
Project Maintainers: Source Open (Source/Open@editor.land) | GitHub Repository | Report an Issue | Security Policy