SSyncropel Docs

Exposing the Instance Securely

Make `spl serve` reachable from other machines without putting an unauthenticated SQLite-backed HTTP server on the public internet. Tailscale, reverse proxy with TLS, CORS, and what never to do.

This page is for people running their own workspace or using the API. If you use a workspace created at syncropel.com, start with Your hosted workspace.

By default spl serve binds 127.0.0.1 and is unreachable from anywhere else on your network. You'll want to change that when:

  • You're pairing a phone that lives on the same home network.
  • You're running a peer workspace that needs to sync with another host.
  • You want to use the CLI from a second machine.
  • You're hosting a shared team instance.

Three safe paths, and one you should never take.

Preferred: Tailscale (or any WireGuard-based private network)

Tailscale gives each machine an address in the 100.x.y.z space. The addresses are only routable between machines you've explicitly joined to your tailnet. No public IP is exposed. Access control is built in.

Install Tailscale on the instance host and any device that needs to reach it. Then:

# Find the host's tailnet address
tailscale ip -4

# Bind the instance to that address
spl stop
spl serve --host 100.x.y.z

Devices on the same tailnet reach the instance at http://100.x.y.z:9100. That becomes the --url you pass to spl pair.

Tailscale's ACLs further narrow which devices can reach the instance. Default-allow across your own tailnet is fine for personal use; tighten for shared machines.

Alternative: reverse proxy with TLS

A reverse proxy (Caddy, nginx, or Traefik) terminates TLS on a public hostname and forwards to the instance on loopback. This is the right answer when you need a public URL (say, https://syncropel.your-domain.com) for a peer workspace or a web dashboard hosted elsewhere.

Keep the instance bound to loopback:

spl serve                                 # stays on 127.0.0.1:9100

Caddy example

syncropel.your-domain.com {
  reverse_proxy 127.0.0.1:9100
}

Caddy handles certificate issuance via Let's Encrypt automatically.

nginx example

server {
  listen 443 ssl;
  server_name syncropel.your-domain.com;
  ssl_certificate     /etc/letsencrypt/live/syncropel.your-domain.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/syncropel.your-domain.com/privkey.pem;

  location / {
    proxy_pass http://127.0.0.1:9100;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # Server-Sent Events for /v1/sync/subscribe
    proxy_buffering off;
    proxy_read_timeout 3600s;
  }
}

Instance-side hardening when behind a proxy

When a proxy terminates connections, the instance sees 127.0.0.1 as the client IP for everything. Two config flags harden against header spoofing in this mode:

# strict impersonation enforcement has a dedicated verb:
spl config impersonation-strict-enable

strict_actor_header has no dedicated verb yet; enable it by writing a strict_actor_header config record (a LEARN on th_engine_config with enabled: true, the standard config-record convention). Both take effect on the next request: no restart needed.

  • strict_actor_header rejects requests that claim a DID via header without a matching token.
  • strict_impersonation_enforcement blocks admin tokens from acting as arbitrary actors unless an explicit allowlist is set.

Alternative: an outbound tunnel

A variant of the reverse-proxy pattern that doesn't require opening a port on your firewall. The instance stays on loopback; a tunnel agent establishes an outbound connection to a tunnel service, which terminates TLS on a public hostname and forwards traffic back down the tunnel to http://127.0.0.1:9100.

Several tunnel services offer this. Follow your provider's setup, point the tunnel's ingress at http://127.0.0.1:9100, and the same hardening flags apply (strict_actor_header, strict_impersonation_enforcement).

Do not: public internet without TLS

# NEVER
spl serve --host 0.0.0.0

On a cloud VM, --host 0.0.0.0 exposes the instance on port 9100 to the entire internet. Even with auth on, you are one config mistake away from a token leak that compromises the whole record store. Even with perfect auth hygiene, you're paying every bot on the internet to probe your instance's surface.

If you find yourself wanting --host 0.0.0.0, pick one of the three safer alternatives above. The only legitimate use of 0.0.0.0 is on a host that is itself behind a firewall, reverse proxy, or tunnel, and in that case, --host 127.0.0.1 would also work and is simpler.

CORS: when a browser calls your instance

The instance's HTTP API rejects cross-origin requests by default. To let a browser at https://syncropel.com (or any other origin) call your locally-running instance, add the origin to the allowlist:

spl config auth-set-cors-origins https://syncropel.com

Multiple origins:

spl config auth-set-cors-origins https://syncropel.com,https://app.example.com

The setting is a runtime record: it takes effect immediately and survives instance restarts.

Typical setups:

  • syncropel.com calling your instance. Add https://syncropel.com.
  • A private web app calling a shared team instance. Add the app's origin.
  • Developer localhost (rare). Add http://localhost:<port>.

Verifying CORS

From the browser's devtools network tab, the instance's responses should include Access-Control-Allow-Origin matching the request's Origin. If they don't, the request is either coming from an origin not on the allowlist, or you haven't set the allowlist at all. spl config show lists current settings.

Pairing with another workspace

Once the instance is reachable on a stable URL, another workspace can pair with it. The pairing flow creates reciprocal federation:manage-scoped service accounts, exchanges public keys, and persists a pair record on both sides. See the workspace pairing guide for the canonical spl federation pair <peer-url> walkthrough and the Discovery guide for auto-discovery via DNS or mDNS.

Decision matrix

Use caseRecommended path
Phone on same home Wi-FiBind to LAN IP, or use Tailscale
CLI from a second personal machineTailscale
Browser on syncropel.comLoopback instance + CORS allowlist
Peer workspace on a cloud hostReverse proxy with TLS or an outbound tunnel
Team-shared instanceReverse proxy with TLS on a private network
Single developer laptopLoopback only: don't expose at all

See also

On this page