MEHS / Knowledge base

MEHS HTTPS control server

The server implements the TLS 1.3 control API and a separate TLS 1.3 TCP data listener. It authenticates configured clients, applies transport policy, issues one-use UUIDv4 negotiation IDs that expire after 120 seconds, and finalizes a selected mode. The data listener binds the finalized epoch to the configured client credentials and forwards TCP flow-open requests to target sockets. Credential and short-lived session stores default to in-memory; restart invalidates sessions when using the default stores.

Configuration

Start from mehs-server.example.json, replace every placeholder secret, and point the certificate/key fields at a certificate valid for the server DNS name. The server configuration uses schema_version: 1; its accepted fields, defaults, validation, and evolution rules are documented in the authoritative configuration reference. TLS certificate verification remains the client's responsibility; the server never sends a key and treats certificate validation as proof of server identity.

Credential and session stores can be configured to use file-backed storage. The existing clients array seeds an empty credential store; once credentials are present in the store, the stored allowlist is loaded instead (the array may then be omitted). The file backend stores client secrets as plaintext, creates files with Unix mode 0600, and rejects credential files with broader Unix permissions. Protect the store directory and files accordingly. Finalized session metadata expires after 15 minutes; the file backend is intended for a single server process, not distributed coordination.

On Debian/Linux, protect the file before starting the service:

sudo install -o root -g root -m 600 mehs/config/mehs-server.example.json /etc/mehs/server.json
sudo MEHS_SERVER_CONFIG=/etc/mehs/server.json /usr/local/bin/mehs-server

Generate each client secret with openssl rand -base64 32 and edit the file under umask 077. The process refuses a config file readable by group or other users on Unix, and refuses client secrets shorter than 16 bytes or left as the example placeholder. Outbound flows to private, loopback, link-local, and other non-public destinations are blocked by default (outbound_block_private: true); set it to false explicitly only for deployments that need them, and prefer an outbound_allow_cidrs allowlist. The example is not secure until the client secret, listen address, and TLS paths are set for the deployment. Keep the private key in a root-readable location and provision certificates through the host's established certificate-management process.

The server binds the configured address and exposes:

Both POST endpoints require HTTPS/TLS 1.3 and are limited to 64 KiB request bodies. The data listener also requires TLS 1.3 and caps concurrent data connections (max_data_connections, default 32; every link of every client counts, including a client's standby link, so size it as clients × (pool size + 1)) and each session at max_flows_per_session concurrent flows (default 1024; closed flows free their slot). Credential values and negotiation tokens are not written to logs. Configure the firewall and outbound network policy: authenticated clients can request TCP or UDP connections to target IP addresses reachable from the server.

Operational metrics

GET /metrics is always enabled and returns Prometheus text format at the control listener address (for example, https://vpn.example.com:8443/metrics). It is served over the same TLS 1.3 certificate as the control API and does not require additional configuration. The endpoint has no authentication of its own; restrict network access to the control listener with the host firewall or deployment network policy, especially because it also serves authorization endpoints.

The metric names and semantics are stable and intentionally have no client, destination, profile, or other high-cardinality or secret-bearing labels:

MetricTypeMeaning
mehs_authorize_requests_totalcounterValid JSON requests reaching the authorize handler.
mehs_authorize_failures_totalcounterAuthorize requests rejected by policy, credentials, or an external auth failure.
mehs_finalize_requests_totalcounterValid JSON requests reaching the finalize handler.
mehs_finalize_failures_totalcounterFinalize requests rejected or failed.
mehs_data_auth_failures_totalcounterData authentication handshakes that fail before authentication succeeds.
mehs_outbound_connect_failures_totalcounterFlow-open attempts that fail to resolve, pass outbound policy, or connect before the timeout.
mehs_active_data_connectionsgaugeData connections currently holding a server connection slot, including handshakes in progress; bounded transport labels are tcp_tls and quic.
mehs_active_flowsgaugeSuccessfully opened target flows that have not yet closed.

For example, a Prometheus scrape can use the server CA file:

scrape_configs:
  - job_name: mehs
    scheme: https
    static_configs:
      - targets: ["vpn.example.com:8443"]
    tls_config:
      ca_file: /etc/prometheus/certs/mehs-ca.pem

Use standard Prometheus counter functions for rates, for example rate(mehs_authorize_requests_total[5m]) or rate(mehs_outbound_connect_failures_total[5m]).

The TLS/TCP and optional QUIC data paths carry TCP and UDP target flows (SOCKS5 UDP ASSOCIATE, TUN-mode UDP/DNS, and remote DNS are all served by the same flow machinery; the server needs no raw-IP gateway and no special privileges). Session state is in-memory by default, with optional file-backed stores. Outbound CIDR allowlists and private-address blocking are configurable. The wire protocol is not frozen between releases and no independent security audit has been completed. Every field is documented in the configuration reference.

On shutdown, both data transports stop accepting new sessions, send a session-level Close to active sessions, and wait up to shutdown_grace_secs for them to finish before aborting remaining tasks. A rejected data authentication closes that connection; a failed flow-open returns a failure result for that flow while leaving the authenticated session available.

Running as a service (systemd)

# /etc/systemd/system/mehs.service
[Unit]
Description=MEHS server
After=network-online.target
Wants=network-online.target

[Service]
Environment=MEHS_SERVER_CONFIG=/etc/mehs/server.json
ExecStart=/opt/mehs/mehs-server
Restart=always
RestartSec=2
LimitNOFILE=1048576
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

Open TCP 8443 (control) and 8444 (data), and UDP 8444 when quic_data_listen is set. The server runs unprivileged; it does not need root unless it binds a port below 1024. Raise LimitNOFILE for many concurrent connections. Give the server IPv6 connectivity if TUN clients should reach IPv6 destinations.