Using the Relay

The relay enables async sync when teammates aren't online at the same time. Messages are stored encrypted and delivered when peers come online.

When You Need a Relay

envctl works peer-to-peer by default. The relay is optional but useful when:

  • Team members are in different time zones — Peers rarely online simultaneously
  • Working remotely — Direct P2P connections blocked by firewalls/NAT
  • Guaranteed delivery — Changes must reach everyone, even if they're offline
  • Onboarding new members — They need to sync before meeting other peers

You don't need the relay if:

  • Team is on the same local network (mDNS discovery works)
  • Team is small and online at similar times
  • You prefer fully P2P operation
  • You can run an always-on node (see below)

Alternative: Always-On Node

If your team shares a network (physical LAN or VPN), you can run an always-on node instead of using a relay. This is a machine that runs the envctl daemon continuously, acting as a persistent peer that's always available for sync.

When to Use an Always-On Node

  • Team shares a physical network or VPN
  • You have an always-on machine (server, NAS, Raspberry Pi, cloud VM)
  • You want to avoid external infrastructure entirely
  • You prefer a free, self-hosted solution

Always-On Node vs Relay

Feature Always-On Node Relay Server
Cost Free (your hardware) Free tier / Paid plans
Network requirement Shared network or VPN Internet access only
Setup complexity Run daemon on a server Single command
Works across internet Only via VPN Yes
External dependency None Relay service
Metadata visibility You control everything Relay sees metadata
Maintenance You maintain the node Managed for you

Setting Up an Always-On Node

Any machine that can run envctl can be an always-on node. Common choices:

  • Office server or NAS
  • Raspberry Pi on the network
  • Cloud VM (if using a VPN)
  • Any always-on workstation

1. Install envctl on the node

$ curl -fsSL https://raw.githubusercontent.com/uradical/envctl/main/install.sh | sh

2. Create an identity for the node

$ envctl init --name sync-server --keychain
 Identity created
  Name: sync-server
  Fingerprint: sha256:9a8b7c6d...

Using --keychain stores the passphrase so the daemon can start without manual intervention.

3. Join the project

Invite the node to your project like any team member:

# On your machine:
$ envctl project invite sync-server --pubkey 9a8b7c6d... --env dev,staging,prod

# On the always-on node:
$ envctl join eyJwcm9qZWN0Ijoi...

4. Install as a system service

Configure the daemon to start automatically on boot. The recommended approach depends on your platform.

Linux (systemd)

Create a systemd service file:

$ sudo nano /etc/systemd/system/envctl.service

Add the following configuration:

[Unit]
Description=envctl P2P secrets sync daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=envctl
Group=envctl
ExecStart=/usr/local/bin/envctl daemon run
Restart=on-failure
RestartSec=5
Environment=HOME=/home/envctl

[Install]
WantedBy=multi-user.target

If running as a dedicated user, create one first:

$ sudo useradd -r -m -s /bin/false envctl

Then set up the identity and project for that user before enabling the service. Alternatively, change User= and Group= to your own user account.

Enable and start the service:

$ sudo systemctl daemon-reload
$ sudo systemctl enable envctl
$ sudo systemctl start envctl

Check the service status:

$ sudo systemctl status envctl
● envctl.service - envctl P2P secrets sync daemon
     Active: active (running) since Mon 2024-03-01 10:00:00 UTC
   Main PID: 12345 (envctl)
     Memory: 24.0M
        CPU: 1.234s
     CGroup: /system.slice/envctl.service
             └─12345 /usr/local/bin/envctl daemon run

View logs with journalctl:

$ sudo journalctl -u envctl -f
macOS (launchd)

The built-in installer creates a launchd plist:

$ envctl daemon install
 Installed as system service

$ launchctl load ~/Library/LaunchAgents/io.envctl.daemon.plist

To start on boot, the plist is automatically loaded on login. Check status with:

$ launchctl list | grep envctl

5. Verify it's running

$ envctl daemon status
 Daemon running (PID 12345)
  P2P port: 7834
  Uptime: 3 days, 2 hours

How It Works

Once the always-on node is running:

  1. When you push changes, the node receives them (it's always online)
  2. When teammates come online, they discover the node via mDNS or saved peers
  3. They sync with the node, receiving any changes they missed

The node acts as a hub that's always available, solving the "nobody online" problem without external infrastructure.

VPN Considerations

If your team uses a VPN to create a shared network:

  • The always-on node should be connected to the VPN
  • mDNS may not work across some VPNs—add peers manually:
    $ envctl peers add 10.8.0.1:7834
  • Consider running the node on your VPN server itself

Best of both worlds

You can use both an always-on node and a relay. The node handles sync within your network, while the relay provides backup for team members who can't connect to the VPN.

Setting Up the Relay

Configure a relay for your project:

$ envctl project relay set relay.envctl.dev
 Relay configured for myapp
  URL: wss://relay.envctl.dev/ws

This change is recorded in your project's membership chain and automatically synced to all team members.

Using the Public Relay

The public relay at relay.envctl.dev is available for all envctl users:

Tier Retention Rate Limits
Free 7 days Yes
Pro 30 days No
Enterprise Unlimited No

Self-Hosting

For Enterprise users or those who prefer self-hosting, you can run your own relay:

$ envctl project relay set relay.yourcompany.com
 Relay configured for myapp
  URL: wss://relay.yourcompany.com/ws

Contact sales@envctl.dev for self-hosting documentation.

How the Relay Works

Message Flow

  1. You make a change (e.g., envctl env var set API_KEY=...)
  2. The daemon encrypts the operation for each team member
  3. Online peers receive it directly via P2P
  4. For offline peers, the message is sent to the relay
  5. The relay stores the encrypted message
  6. When peers come online, they retrieve their messages

What the Relay Sees

Data Visible to Relay?
Secret values No (encrypted)
Variable names No (encrypted)
Project name Yes (for routing)
Sender/recipient IDs Yes (for routing)
Message timestamps Yes
Message sizes Yes
IP addresses Yes (connection logs)

End-to-end encryption

The relay is a store-and-forward service. It can see metadata but never the contents of your secrets. Messages are encrypted with ML-KEM before leaving your machine.

Checking Relay Status

$ envctl project relay status
Relay Status for myapp:

  URL: wss://relay.envctl.dev/ws
  Status: connected
  Last message: 2 minutes ago
  Pending messages: 0

The daemon maintains a persistent WebSocket connection to the relay.

Removing the Relay

To disable relay sync and return to P2P-only:

$ envctl project relay set ""
 Relay disabled for myapp

Troubleshooting

Connection Issues

If the relay shows as disconnected:

  1. Check network connectivity:
    $ curl -I https://relay.envctl.dev
    HTTP/2 200
  2. Check daemon status:
    $ envctl daemon status
     Daemon running (PID 12345)
  3. Restart the daemon:
    $ envctl daemon stop
    $ envctl daemon start

Messages Not Syncing

If changes aren't reaching teammates:

  1. Verify relay is configured for all team members:
    $ envctl project relay status
  2. Check that both parties have the daemon running
  3. Verify the recipient's fingerprint matches their actual identity

Rate Limiting (Free Tier)

The free tier has rate limits. If you see "rate limited" errors:

  • Wait a few minutes and try again
  • Upgrade to Pro for unlimited usage
  • Batch your changes (fewer, larger updates)

Privacy Considerations

While your secrets are encrypted, the relay does see some metadata:

  • Traffic patterns — When you sync, how often, message sizes
  • Team structure — Who communicates with whom
  • IP addresses — Where you're connecting from

If this metadata is sensitive for your use case:

  • Self-host a relay on your own infrastructure
  • Use a VPN to mask IP addresses
  • Stick to P2P-only (no relay)

Related