checkpoint: integrate authenticated transports

This commit is contained in:
h3cx committed 2026-09-15 10:57:11 +02:00
1 parent 313ab00765
commit 586ff8f223
42 files changed
+6277 -771

No files matched your search

+10 -6
View File
@@ -49,10 +49,12 @@ STARTING → RESOLVING → PROBING → AUTHENTICATING → CONNECTED
└→ PROBING
```
In `auto` mode the target order is direct UDP, QUIC DATAGRAM, HTTP/3
CONNECT-UDP, then TLS/TCP. Authentication rejection is a configuration error,
not permission to probe indefinitely. Network failures use bounded exponential
backoff with jitter.
In `auto` mode the configured order defaults to direct UDP, QUIC DATAGRAM,
HTTP/3 CONNECT-UDP, then TLS/TCP. Authentication rejection is a configuration
error, not permission to probe indefinitely. Network failures use bounded
exponential backoff with jitter (250 ms to 30 s), reset after a successful
connection. These transports are wired into the current runtime; the
acceptance checklist distinguishes implementation from completed v1 sign-off.
## Server isolation
@@ -69,5 +71,7 @@ Normal operation uses ordinary UDP/TCP/Unix sockets and needs no root,
reviewed deployment must bind a port below 1024. The control socket is local
and protected by filesystem ownership/mode; it is not a network listener.
This document describes the intended v1 architecture. Check the milestone
matrix before relying on a transport or control command in a particular build.
Configuration reload and certificate/key reload are not implemented. The
control `reload` command returns an explicit unsupported response. The
`reconnect` command applies to the client, while the server supports `status`,
`stats`, and graceful `shutdown`.
+95 -91
View File
@@ -1,109 +1,113 @@
# Development state
Checkpoint: 2026-09-14, end of day. Development is paused at a compilable
checkpoint; no commit was made.
End-of-day checkpoint: 2026-09-15. Branch: `main`. Baseline commit:
`313ab00` (`checkpoint: implement wgcloak v1 foundations`). Work after that
checkpoint remains uncommitted in the shared worktree. Several agents have
edited files in parallel; use the inventory below and review the complete diff
before committing.
## Branch and worktree
## Implemented behavior
- Branch: `main`.
- `git status --short` reports the project files as untracked, including
`Cargo.toml`, `Cargo.lock`, `src/`, `tests/`, documentation, examples, and
packaging files. There is no useful committed baseline to compare against in
this checkout.
- Multiple agents have been working in the shared directory. Review all diffs
and ownership boundaries before committing; in particular, do not assume an
untracked file was created by this task or that a concurrent edit is final.
- The package now has a reusable Rust library (`src/lib.rs`) and a thin binary
(`src/main.rs`). Integration tests import `wgcloak` instead of compiling
production modules through `#[path]`; module unit tests run once in the
library test target.
- Client and server runtime paths support authenticated direct UDP, QUIC
DATAGRAM, HTTP/3 CONNECT-UDP with HTTP Datagrams, and TLS/TCP length-framed
fallback. TLS client connections use normal certificate-chain and hostname
verification. For encrypted transports, shared-secret authentication occurs
before server backend-session activation.
- Client `auto` uses its configured transport order and bounded connection
probes, stops on authentication rejection, and reconnects with jittered
exponential backoff from 250 ms through 30 s. Successful connections reset
the backoff. The client control `reconnect` command forces another attempt.
- The server runs enabled listeners concurrently, rejects incompatible UDP
listener port conflicts, and aggregates listener/session statistics. Sessions
are isolated and use per-session backend UDP sockets. Admission limits cover
active sessions, per-IP sessions, pending handshakes, and authentication
attempts.
- The Unix control socket uses a private runtime directory and mode `0600`,
refuses an active socket, and removes only a verified stale socket. `status`,
`stats`, and `shutdown` are supported; `reconnect` is client-only. Config and
certificate/key reload are not implemented. The CLI/control reload request
returns an explicit unsupported response.
- The local client UDP listener currently tracks one most-recent application
peer; concurrent local source peers can replace the return target.
- Direct UDP keepalives are one-way. Silent peer loss may not be detected
immediately; a local transport error or explicit client reconnect is the
recovery path covered by tests.
## Implemented and wired behavior
## Verification checkpoint
The current tree contains the following work, subject to integration review:
Before the final config and client-ID edits, the full gates passed:
- Operational assets: MIT `LICENSE`, Linux-focused `README.md`, `.gitignore`,
client/server TOML examples, architecture/protocol/security/operations/
troubleshooting documents, an implementation checklist, and systemd client
and server templates.
- TLS/TCP transport primitives in `src/transport/tls_tcp.rs`: bounded
incremental/coalesced u16-big-endian framing, truncated/oversized-frame
handling, PEM/DER certificate and private-key loading, challenge-response
authentication, authenticated data/heartbeat/close framing, idle timeout,
and bounded shutdown.
- HTTP/3 support primitives in `src/transport/http3.rs`: CONNECT-UDP request
validation, QUIC varints, RFC 9297 HTTP Datagram context encoding, capsule
encoding, and project authentication messages. The module does not fake
CONNECT-UDP with POST or ordinary streams.
- Runtime orchestration in `src/transport/mod.rs`, `src/client/mod.rs`, and
`src/server/mod.rs` instantiates direct UDP, QUIC, and TLS/TCP connectors or
listeners. TLS backend creation occurs after the authenticated session is
established. HTTP/3 currently returns an explicit `Unsupported` result from
runtime selection because the actual h3 datagram adapter is not complete.
- The client and server start the Unix control server. Client reconnect and
reload commands currently cause a reconnect attempt; server shutdown is
handled by the encrypted listener paths, while direct-UDP/control lifecycle
still needs review.
- `h3-quinn` was enabled with its `datagram` feature; `tokio-rustls` and
`rustls-native-certs` were added for TLS runtime integration.
- `cargo fmt --all -- --check`
- `cargo check --all-targets`
- `cargo clippy --all-targets -- -D warnings`
- `cargo test --all-targets --quiet` — 76 tests passed.
## Files changed in this work
After those edits, the config-focused suite passed 12/12, the client-ID focused
transport tests passed, and the current `cargo check` passes. Rerun the full
gates after these edits before treating the tree as audited. The broader
encrypted daemon-runtime end-to-end proof described below is not yet created or
complete; focused transport and HTTP/3 runtime tests do not replace it.
Primary files touched by the operations/integration work are:
## Remaining work, prioritized
- `Cargo.toml`, `Cargo.lock`
- `src/transport/mod.rs`
- `src/transport/tls_tcp.rs`
- `src/transport/http3.rs`
- `src/client/mod.rs`
- `src/server/mod.rs`
- `tests/tls_tcp.rs`, `tests/http3.rs`, `tests/integration_transports.rs`
- `.gitignore`, `LICENSE`, `README.md`
- `examples/*`, `packaging/systemd/*`, and the other files under `docs/`
1. Add and complete encrypted daemon-runtime end-to-end proof: local UDP
application traffic through client runtime, encrypted transport, server
runtime, and UDP echo backend, including auth rejection, session isolation,
status/stats, and bounded shutdown. Exercise each encrypted mode where
practical; do not mistake lower-level transport tests for daemon proof.
2. Rerun formatting, all-target check, strict all-target Clippy, and the full
suite after the final config/client-ID edits; then review the complete diff
and reconcile acceptance evidence with `docs/IMPLEMENTATION_CHECKLIST.md`.
3. Add fuzz and benchmark targets for parsers, framing, protocol/auth paths,
and transport overhead; record the environment and baseline.
4. Decide and document a practical maximum datagram-size cap, with config and
boundary tests tied to transport overhead and MTU constraints.
5. Confirm whether configuration and certificate/key reload are required by
the spec/product scope. Until implemented and tested, they remain explicitly
unsupported; do not imply hot reload in CLI or deployment docs.
`src/transport/quic.rs` was edited by another concurrent agent and is outside
the TLS/integration ownership listed above. Preserve that ownership boundary
when resolving conflicts.
## First recovery tasks
## Verification and current failures
1. Inspect current client/server orchestration and transport test APIs, then
create the encrypted daemon-runtime echo test without duplicating transport
internals.
2. Re-run the four full gates above against the current tree; resolve any
compile, lint, or test regressions before adding further features.
3. Review spec milestones and the 17 completion criteria, and record evidence
for each criterion in the coordinator checklist. Keep fuzzing, benchmarks,
datagram-cap decision, and reload scope visible as open items.
- TOML examples were parsed successfully with Python `tomllib`.
- TLS/TCP focused tests passed: `cargo test --test tls_tcp --quiet` (7 tests).
- HTTP/3 focused tests passed: `cargo test --test http3 --quiet` (4 tests).
- `cargo check` passed after the transport integration edits.
- The TLS loopback integration test compiles but currently panics while
constructing a rustls configuration because no process-level crypto provider
is installed:
## Current worktree inventory
`Could not automatically determine process-level CryptoProvider from Rustls crate features.`
Captured with `git status --short` for this checkpoint. These are shared-tree
changes; ownership is not implied by this list.
The runtime/test setup must explicitly install the `ring` provider (or enable
exactly one provider feature) before building rustls configurations.
- The supervisor's latest failure reprobe reports that the QUIC focused tests
now pass. Run the complete suite again after fixing the rustls provider; the
earlier complete-suite run had QUIC handshake-timeout failures before the
concurrent QUIC-agent fixes landed.
- Clippy has not reached a clean result. Existing included test/source modules
produce many dead-code and lint diagnostics, and integration code also needs
a final lint pass after the provider fix.
Modified files:
## Remaining gaps, prioritized
- `Cargo.lock`, `Cargo.toml`
- `README.md`
- `docs/ARCHITECTURE.md`, `docs/DEVELOPMENT_STATE.md`,
`docs/IMPLEMENTATION_CHECKLIST.md`, `docs/OPERATIONS.md`, `docs/PROTOCOL.md`,
`docs/TROUBLESHOOTING.md`
- `examples/client.advanced.toml`, `examples/server.advanced.toml`,
`examples/server.minimal.toml`
- `packaging/systemd/wgcloak-client.service`,
`packaging/systemd/wgcloak-server.service`
- `src/cli.rs`, `src/client/mod.rs`, `src/client/supervisor.rs`, `src/config.rs`,
`src/control.rs`, `src/main.rs`, `src/protocol/auth.rs`,
`src/protocol/direct.rs`, `src/server/mod.rs`, `src/stats.rs`,
`src/transport/direct_udp.rs`, `src/transport/http3.rs`,
`src/transport/mod.rs`, `src/transport/quic.rs`
- `tests/control.rs`, `tests/direct_udp.rs`, `tests/http3.rs`,
`tests/integration_transports.rs`, `tests/quic.rs`, `tests/supervisor.rs`,
`tests/tls_tcp.rs`
1. Fix and verify explicit rustls crypto-provider initialization, then run the
TLS loopback test and the complete test suite.
2. Finish a real h3 0.0.8/h3-quinn 0.0.10 HTTP/3 CONNECT-UDP datagram adapter,
or retain the explicit `Unsupported` path with a precise documented
dependency/API blocker. Do not substitute POST, capsules-only streams, or
ordinary HTTP request bodies for HTTP Datagrams.
3. Review runtime lifecycle semantics: direct-UDP control shutdown wiring,
actual file-backed config reload, client TLS heartbeat scheduling, and
authentication-error mapping so auto fallback stops on rejected credentials.
4. Run repository-wide formatting, tests, and clippy; inspect the shared
untracked tree and reconcile concurrent `quic.rs` changes before any commit.
## First three tasks tomorrow
1. Install/select the rustls `ring` provider once at runtime and in the
integration test setup; rerun TLS loopback plus `cargo test`.
2. Inspect the enabled h3-quinn datagram API and implement/test the smallest
standards-correct CONNECT-UDP client/server adapter; otherwise record the
exact unsupported API/dependency condition in the public docs.
3. Perform an end-to-end lifecycle review and repository-wide `fmt`, check,
test, and clippy pass, resolving only changes within each agent's ownership.
Untracked files:
- `src/lib.rs`, `src/server/limits.rs`
- `tests/http3_integration.rs`, `tests/http3_runtime.rs`, `tests/limits.rs`,
`tests/reconnect.rs`, `tests/runtime_lifecycle.rs`
+11 -9
View File
@@ -1,9 +1,10 @@
# Implementation and acceptance checklist
This is a coordinator checklist, not a claim that the items are complete.
Mark an item only with code evidence, tests, and an integration result. Record
the commit/test command or issue beside each checked item. A feature marked
planned in the other documents must remain visibly planned here until proven.
This is a coordinator checklist, not a claim that v1 is signed off. The current
runtime has code paths for all four carriers, configured auto selection,
client reconnect backoff, and local control commands. Mark an acceptance item
only with code evidence, tests, and an integration result; record the test
command or issue beside each checked item.
## Milestones 1–6
@@ -14,17 +15,18 @@ planned in the other documents must remain visibly planned here until proven.
- [ ] **2 — QUIC DATAGRAM:** certificate loading and hostname verification;
client challenge/response; datagrams; keepalive/MTU; reconnect; multi-client
handling without core API redesign.
- [ ] **3 — automatic selection:** `mode = "auto"`; direct UDP then QUIC;
bounded probes/retries; auth rejection stops probing; jittered reconnect;
status reports selected transport.
- [ ] **3 — automatic selection:** `mode = "auto"`; configured order across
direct UDP, QUIC, HTTP/3, and TLS/TCP; bounded probes/retries; auth rejection
stops probing; jittered reconnect; status reports selected transport.
- [ ] **4 — HTTP/3 CONNECT-UDP:** standard CONNECT-UDP/HTTP Datagram semantics;
authentication; backend mapping; reuse QUIC infrastructure where practical.
- [ ] **5 — TLS/TCP fallback:** TLS listener; client authentication; bounded
big-endian length framing; bidirectional datagram reconstruction; fallback
integration; expected head-of-line behavior documented.
- [ ] **6 — operational polish:** Unix control socket; status/stats/reconnect
commands; config validation; systemd units; capability guidance; certificate
and configuration reload; benchmarks; fuzzing; installation docs.
commands; config validation; systemd units; capability guidance; benchmarks;
fuzzing; installation docs. Config and certificate/key reload are not
implemented; the control command explicitly reports reload as unsupported.
## v1 completion criteria (spec §89)
+65 -51
View File
@@ -1,79 +1,91 @@
# Operations
This guide assumes a dedicated `wgcloak` system user and `/etc/wgcloak/` for
configuration. Adjust paths for a user service or a different packaging
layout.
This guide assumes a dedicated `wgcloak` service account and configuration
under `/etc/wgcloak/`. Adjust paths to match the reviewed deployment.
## First installation
## Build and install
```bash
cargo test --all-targets
cargo build --release
sudo install -Dm755 target/release/wgcloak /usr/bin/wgcloak
sudo install -d -o wgcloak -g wgcloak -m0750 /etc/wgcloak/clients
sudo install -o wgcloak -g wgcloak -m0600 client.secret /etc/wgcloak/client.secret
```
Create server client secrets separately and transfer them through a protected
channel. Do not put real secret contents in the repository. Validate a config
before starting a service when `check-config` is available in the current
build:
Store each client secret in a separate file readable only by the service
account (normally owner `wgcloak`, mode `0600`). Protect the parent directory
from untrusted writes. The server needs its TLS certificate and private key
readable by its service account when QUIC, HTTP/3, or TLS/TCP is enabled.
Validate configuration before starting a daemon:
```bash
sudo -u wgcloak wgcloak check-config --config /etc/wgcloak/client.toml
sudo -u wgcloak wgcloak check-config --config /etc/wgcloak/server.toml
```
The command is a target v1 CLI and is delivered in the operational-polish
milestone; confirm with `wgcloak --help`.
## Start, stop, and reconnect
## Starting and stopping
Foreground operation is useful for first-run diagnostics:
Foreground operation is useful during initial setup:
```bash
wgcloak client --config /etc/wgcloak/client.toml
wgcloak server --config /etc/wgcloak/server.toml
```
SIGTERM and SIGINT should stop accepting new sessions, stop local ingestion,
close backend sockets, flush useful metrics, and exit within a bounded timeout.
A transport failure should reconnect with jitter; it should not terminate the
daemon. The systemd templates set restart-on-failure for this reason.
On Linux, SIGINT, SIGTERM, or the control `shutdown` command initiates bounded
shutdown. The server stops accepting sessions and gives listeners/sessions a
bounded cleanup window before abort fallback. A client reconnects after
transport failures using exponential backoff with jitter from 250 ms to 30 s;
the sequence resets after a successful connection. Authentication rejection
stops automatic probing rather than trying other transports.
## Control socket (intended interface)
Direct UDP keepalives are one-way and do not acknowledge peer liveness. A
silent direct-UDP server restart may therefore not be detected immediately;
use `wgcloak reconnect` on the client to force a fresh handshake. The tested
restart path waits through normal reconnect backoff while the server is
unavailable, then resumes when it returns.
The daemon should expose a Unix-domain control socket at
`$XDG_RUNTIME_DIR/wgcloak.sock` (for a system service, the template maps this
to its private runtime directory). Filesystem permissions, not a TCP port,
authorize access. The initial control protocol may be newline-delimited JSON.
## Local control socket
Target commands are:
By default the socket is `$XDG_RUNTIME_DIR/wgcloak.sock` when that runtime
directory is owned by the current user and has mode `0700`. Otherwise the
daemon uses `/run/user/<uid>/wgcloak/wgcloak.sock` when that private user
runtime directory exists, or `$HOME/.local/run/wgcloak/wgcloak.sock` as a
per-user fallback. The socket's parent is created with mode `0700`; the socket
itself is mode `0600`. An active socket path is never unlinked; a stale socket
is removed only after its type and identity are checked. Use the same runtime
directory for the CLI that is used by the daemon, or pass `--socket`
explicitly (its parent directory must be owned by you and mode `0700`):
```text
status current role/state, selected transport, endpoint, MTU and uptime
stats counters for traffic, drops, auth failures, reconnects and sessions
reconnect ask the client to leave the current transport and probe again
reload re-read supported configuration (not full hot reload in early builds)
shutdown request graceful process shutdown
```bash
wgcloak status
wgcloak stats
wgcloak reconnect
wgcloak reload
wgcloak shutdown
```
The exact request/response field names are implementation-owned until the
control protocol lands. A client should never send credentials or payloads
through this socket. `status` and `stats` are read-only; `reconnect`, `reload`,
and `shutdown` are state-changing and must remain local-admin operations.
`status` and `stats` report the running state. `reconnect` asks a client to
leave its current session and reconnect; the server rejects this client-only
command. `reload` currently returns “configuration reload is not supported”.
Neither configuration hot reload nor certificate/key reload is implemented.
`shutdown` requests graceful process shutdown. The control protocol is local
newline-delimited JSON; do not send credentials or application payloads over
it.
For a non-default control path, set `XDG_RUNTIME_DIR` consistently for both
daemon and CLI, or use the CLI's `--socket PATH` option.
## systemd deployment
Review and copy the units under `packaging/systemd/`. They assume:
- binary `/usr/bin/wgcloak`;
- user/group `wgcloak`;
- configuration `/etc/wgcloak/client.toml` or `server.toml`;
- runtime socket under a private `/run` directory;
- secret and certificate paths readable by the service user.
After review:
Review and copy the units under `packaging/systemd/`. They assume binary
`/usr/bin/wgcloak`, user/group `wgcloak`, and role configuration under
`/etc/wgcloak/`. Each unit creates a private runtime directory and sets
`XDG_RUNTIME_DIR` there.
```bash
sudo install -Dm0644 packaging/systemd/wgcloak-client.service /etc/systemd/system/wgcloak-client.service
sudo install -Dm0644 packaging/systemd/wgcloak-client.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now wgcloak-client.service
systemctl status wgcloak-client.service
@@ -81,13 +93,15 @@ journalctl -u wgcloak-client.service -f
```
For a low port such as 443, uncomment `AmbientCapabilities=CAP_NET_BIND_SERVICE`
and its bounding-set companion in the server unit only after confirming the
unit still starts. Direct UDP examples use an unprivileged port.
and its matching bounding-set entry in the server unit only if required. Normal
high-port operation needs no capability. Keep `CAP_NET_ADMIN` and
`CAP_SYS_ADMIN` disabled.
## Safe rollout
Start with direct UDP and one test client. Check local listener binding,
server authentication, backend reachability, return-peer behavior, counters,
and clean shutdown. Add one transport milestone at a time. Keep an independent
path to the host while changing a public listener and retain the previous
configuration until a restart test succeeds.
Start with one enabled transport and one test client. Verify authentication,
backend reachability, reply delivery, status/statistics, and clean shutdown.
Add transports one at a time. Direct UDP, QUIC, and HTTP/3 are UDP listeners;
QUIC and HTTP/3 cannot bind the same address/port in the current architecture.
TLS/TCP may use the same numeric port because it uses TCP. Keep an independent
management path available when changing public listener addresses.
+23 -13
View File
@@ -49,20 +49,30 @@ starting limit is 1200 bytes.
## Encapsulated transports
- **Direct UDP:** lowest overhead and native datagrams; uses the authenticated
protocol above and provides integrity but not outer confidentiality.
- **QUIC DATAGRAM:** TLS 1.3 protects the transport; client shared-secret
challenge/response still authenticates the configured client.
- **HTTP/3 CONNECT-UDP:** uses legitimate MASQUE CONNECT-UDP and HTTP Datagram
semantics over QUIC. It is for compatibility, not fingerprint spoofing.
- **TLS/TCP:** uses the same client authentication model. Each datagram is
framed as a big-endian `u16` payload length followed by up to 65535 bytes;
the configured lower limit should normally apply. TCP head-of-line blocking
is expected.
- **Direct UDP:** native datagrams; uses the authenticated protocol above and
provides integrity but not outer confidentiality.
- **QUIC DATAGRAM:** TLS 1.3 protects the connection; a client shared-secret
challenge/response authenticates the configured client. Application
datagrams use QUIC DATAGRAM frames.
- **HTTP/3 CONNECT-UDP:** uses extended CONNECT with `:protocol=connect-udp`
and HTTP Datagrams with the RFC 9297 context ID. The request path is
configurable. Capsules are used for the authentication exchange, not as the
application-data carrier.
- **TLS/TCP:** uses the same client authentication model. Each application
datagram is framed by a big-endian `u16` payload length and opaque payload;
the configured maximum applies. TCP head-of-line blocking is expected.
QUIC, HTTP/3, and TLS/TCP are planned by milestones 2, 4, and 5 respectively.
The direct packet details are a protocol target; confirm the implementation and
integration tests before treating them as interoperable guarantees.
All four carriers are wired into the current client/server runtime. Client
`mode = "auto"` probes its configured order, retries bounded probe attempts,
and stops on authentication rejection. Reconnection after transport loss uses
bounded exponential backoff with jitter; successful connection resets the
backoff. This describes the current code path, not a claim that every v1
acceptance criterion has passed.
The control `reload` command is currently unsupported, including certificate
or key reload. See the checklist for acceptance work that remains before a v1
release. The direct packet details and the encapsulated transport APIs are
project-specific; interoperability requires matching this protocol.
## Authentication and resource order
+18 -12
View File
@@ -8,15 +8,16 @@ Check the simplest layer that can explain the symptom, then move outward.
error.
- Check that every secret/certificate path exists and is readable by the
service account, but not by unrelated users.
- Run `wgcloak --help` and, where implemented, `wgcloak check-config` to spot a
command or schema mismatch.
- Run `wgcloak --help` and `wgcloak check-config` to spot a command or schema
mismatch.
- Confirm listener addresses are valid and no other process owns the port.
## Client cannot connect
- Verify DNS resolution and the configured server name/port.
- Confirm the selected transport is implemented in this build; an `auto` list
may mention future transports.
- Confirm the selected server listener is enabled and that the matching
transport/port appears in its `status` output. In auto mode, check the
configured client order and `retry_ms` status while it probes.
- For direct UDP, check both directions in the host firewall and allow the
server's UDP listener.
- For TLS transports, verify the hostname matches the certificate and do not
@@ -44,17 +45,22 @@ fallback probing rather than generate traffic across every transport.
## Session disappears
The target idle timeout is 120 seconds, refreshed by authenticated data or
keepalive. Check keepalive settings, NAT expiry, server resource limits, and
whether the client is reconnecting after a transport failure. A valid direct
UDP packet can update the peer address after NAT rebinding; a spoofed packet
without a valid MAC cannot.
The server's configured idle timeout applies per session. Check transport
keepalive behavior, NAT expiry, server resource limits, and whether the client
is reconnecting after a transport failure. A valid direct UDP packet can
update the peer address after NAT rebinding; a spoofed packet without a valid
MAC cannot.
## Control socket unavailable
Check the daemon's `XDG_RUNTIME_DIR`, socket ownership/mode, and whether a
stale socket path remains after an unclean exit. The control interface is local
only and may be absent until the operational-polish milestone is implemented.
Check the daemon and CLI use the same `XDG_RUNTIME_DIR`, and verify the
directory is private and the socket mode is `0600`. If `XDG_RUNTIME_DIR` is
unset or is not current-user-owned mode `0700`, the daemon uses
`/run/user/<uid>/wgcloak/` when available, or `$HOME/.local/run/wgcloak/`.
An active socket is never removed; a stale socket can be replaced only after
its type and identity are checked.
The `reload` command is present but currently returns an unsupported response;
neither configuration nor certificate hot reload is implemented.
## Host hardening prevents startup