Skip to content

File Layout

Tunnel Whisperer stores all runtime configuration, keys, and infrastructure state under a single platform-specific directory.

Platform Base directory
Linux /etc/tw/config/
macOS /etc/tw/config/
Windows C:\ProgramData\tw\config\
Override TW_CONFIG_DIR environment variable

Server file tree

A fully provisioned server with two users has the following layout:

/etc/tw/config/
├── config.yaml              # Main configuration file
├── authorized_keys          # SSH authorized keys (auto-generated from users)
├── ssh_host_ed25519_key     # SSH server host key (private)
├── ssh_host_ed25519_key.pub # SSH server host key (public)
├── ca.crt                   # Per-server CA certificate (PEM); public cert shipped to the relay trust pool
├── ca.key                   # Per-server CA private key (PEM); never leaves the server
├── client.crt               # Client certificate (PEM) presented to the relay's mutual-TLS gate
├── client.key               # Private key (PEM) for client.crt
├── relay/
│   ├── main.tf              # Terraform configuration for the relay
│   ├── cloud-init.yaml      # Cloud-init script (Caddy + Xray + SSH setup)
│   ├── terraform.tfvars     # Terraform variables (provider, domain, token)
│   └── terraform.tfstate    # Terraform state (tracks provisioned resources)
└── users/
    ├── alice/
    │   ├── config.yaml      # Client config pre-filled for this user
    │   ├── id_ed25519       # SSH private key
    │   └── id_ed25519.pub   # SSH public key
    └── bob/
        ├── config.yaml      # Client config pre-filled for this user
        ├── id_ed25519       # SSH private key
        └── id_ed25519.pub   # SSH public key

Client file tree

A client receives a config bundle from the server and places it in the config directory:

/etc/tw/config/
├── config.yaml              # Client configuration (mode, xray, tunnels)
├── id_ed25519               # SSH private key (received from server)
├── id_ed25519.pub           # SSH public key (received from server)
├── client.crt               # Per-server client certificate (received from server) for relay mTLS
└── client.key               # Private key for client.crt (received from server)

The client certificate is per-server, not per-user

client.crt/client.key admit the connection at the relay's mutual-TLS gate. Every user of the same server shares the same client certificate; individual identity is enforced later by the per-user SSH key. See Relay Authentication.


Per-user config bundle

When a user is created on the server, tw export user <name> (or the dashboard download button) produces a .zip file containing everything the client needs.

Zip contents:

<name>-tw-config.zip
├── config.yaml              # Complete client config
├── id_ed25519               # SSH private key for this user
├── id_ed25519.pub           # SSH public key for this user
├── client.crt               # Per-server client certificate for relay mTLS (shared by all users)
└── client.key               # Private key for client.crt

The config.yaml inside the bundle is pre-filled with:

  • mode: client
  • xray.uuid -- unique UUID for this user
  • xray.relay_host -- the server's relay domain
  • xray.relay_port and xray.path -- transport settings
  • client.ssh_user -- the user's name
  • client.server_ssh_port -- matching the server's SSH port
  • client.tunnels -- port mappings defined during user creation

The bundle also carries the server's client.crt/client.key — the same per-server certificate for every user — which the client presents to the relay's mutual-TLS gate. See Relay Authentication.

Deploying the bundle

Extract the zip into the client's config directory and start the client:

# Linux
sudo mkdir -p /etc/tw/config
sudo unzip alice-tw-config.zip -d /etc/tw/config/
sudo tw connect

Relay directory

The relay/ subdirectory contains all Terraform-managed infrastructure files. It is created during tw create relay-server and persists until tw destroy relay-server removes the cloud resources.

File Description
main.tf Terraform configuration defining the VPS, firewall rules, and DNS
cloud-init.yaml Cloud-init user data that installs Caddy, Xray, and configures SSH
terraform.tfvars Input variables: provider credentials, domain, region
terraform.tfstate Terraform state file tracking all provisioned cloud resources

Do not edit terraform.tfstate

The state file is managed by Terraform. Manual edits can cause resource drift or prevent clean destruction of the relay server.