Reach any service.
From anywhere.

A private mesh for your services — not a flat network you join. You run it all; we run nothing.

Enroll your devices into a federation. Grant one service to one peer. It answers on localhost — over a post-quantum WireGuard mesh you own end to end. No account, no control plane we run, no telemetry. Built on sidehub, one open binary for every OS.

closed alpha · free · linux  macos  windows  ios  android  freebsd  openbsd

Three steps. One federation.

No accounts to create. No servers to provision. Run the binary, mint a code, paste it on the next device.

01  / run

Run the binary.

Drop the single sidehub executable on the device you want as the controller and run it. It opens a setup page on localhost — choose Controller. No daemon to install, no config file to hand-edit, no kernel module.

$ ./sidehub  # setup page on 127.0.0.1
02  / invite

Mint a bootstrap code.

In the controller’s dashboard, issue a one-time code bound to your federation. Hand it to whichever device should join — paste it, scan a QR, sneakernet it. Your call.

dashboard → new code  → bc_3f9…8a2
03  / join

Paste it to join.

Run sidehub on the new device and paste the code in its setup page. It pairs with the controller, builds an encrypted tunnel to every hub, and exposes the federation’s services on localhost.

$ ./sidehub + paste bc_…  # joined

Two devices that can't reach each other directly — behind a home router, a firewall, a carrier NAT — meet through a relay. The relay only forwards encrypted frames and sealed invitations; it never sees your traffic, your keys, or even which services exist. Because it can't read anything, it doesn't have to be yours: use our open relay (free, on request), a partner's, or run your own. It's never part of what you have to trust.

A service is one thing you share.

A service is one thing you expose — a database, a web app, an SSH port, a SOCKS exit, even your mailbox. The controller grants it to specific peers, and it shows up on a local address on their machine. Nothing else on your network is visible — there's no subnet to scan, no flat address space to firewall.

TCP

Databases, SSH, Git, registries — anything on a port.

localhost:5432
UDP

DNS, syslog, game servers, custom protocols.

localhost:5353
HTTP / HTTPS the web naming

Web apps and APIs answer at a name — no IP, no port to remember. Resolved on your own machine, never by public DNS.

wiki.home-lab.sideservices.localhost
SOCKS5

A proxy or exit on another node — route requests through where it sits.

localhost:1080
Mail

A private IMAP + SMTP mailbox the controller hosts for your federation — opt-in, mesh-only.

imap + smtp on localhost

Four ways people use it.

The same primitive — grant one service to one peer, deny everything else — covers problems that usually need four different products. Nothing below opens a port, publishes a DNS name, or joins two networks together.

// ot & industrial

Let a vendor reach one machine, not the plant.

Remote support for an HMI, a PLC or an instrument normally means a jump host, a VPN account and a route into the OT segment. Here the vendor is granted exactly one service on one device, for as long as you say.

vendor-laptop → hmi-3 · tcp → localhost:502
  • The device needs no softwarePlug it into the IoT appliance's protected port. It keeps its own address and its own firmware; the appliance is what joins the mesh.
  • The grant is the perimeterNothing else in the segment is reachable — there is no subnet to scan, because the vendor never receives a route, only a local port.
  • Time-boxed and revocableA service can carry an idle timeout and a deadline, and deleting it in the controller drops the live session on the device.
// partner exchange

Hand another company one feed.

Two organizations, two controllers, no merged network. A federation code grants a single service across the boundary — and neither side learns anything about the other's topology, hosts or other services.

partner-co → metrics · http → metrics.acme.sideservices.localhost
  • Scoped to one serviceCross-federation access is deny-by-default and per service. There is no shared address space and no site-to-site tunnel to decommission later.
  • Auditable on both endsEach side keeps its own usage record of the exchange: which peer, how many requests, how many bytes, over what period.
  • Withdrawn in one clickRevoke the service and the partner's local address stops answering — no ticket to a network team, no firewall change.
// ci/cd

Give a build runner an internal dependency.

A hosted runner needs your private registry, a package proxy or a seeded database. The usual answers are a public endpoint behind a password, or a VPN credential sitting in CI secrets.

runner-07 → registry · http → registry.acme.sideservices.localhost
  • Nothing is publishedThe registry stays on its own machine. It is never resolvable by public DNS, never listening on a public port, never fronted by a public certificate.
  • One identity per runnerEach runner is its own peer with its own grant, so a compromised runner can be revoked on its own — and its traffic is separable in the usage log.
  • Same address everywhereThe service answers on the same local address on every runner, so the pipeline configuration does not change between environments.
// private ai

Serve a self-hosted model to your people.

A GPU box running an inference server is the most sensitive thing to expose and the easiest to get wrong. Publish it as a service instead: it answers at a name on each teammate's own machine, and never on the internet.

alice · phone → llm · http → llm.acme.sideservices.localhost
  • Streaming worksToken streams and long-lived responses pass through unbuffered, so an OpenAI-compatible client behaves exactly as it would against localhost.
  • Prompts stay insideTraffic goes device-to-device over an encrypted tunnel. No inference provider, no vendor control plane, and no request contents are recorded — usage accounting stores counts, never payloads or query strings.
  • You can see who used itPer-request accounting shows which peer or which device called, how much moved and how long it took — the basis for a chargeback or for noticing a client that suddenly went quiet, or loud.

Or run it as an appliance.

SideServices also ships as ready-to-flash appliances — the whole mesh baked into a small, self-updating image. No server to run, no operating system to install or patch.

// self-host a federation

Federation appliance

The easiest way to self-host an entire SideServices federation. Flash a Raspberry Pi, plug it in, and the controller, relay and your services come up as a single tiny image.

  • A whole federation in a boxController, relay and services on one Raspberry Pi — no Linux to install, harden or patch.
  • Atomic over-the-air updatesThe box updates itself in one step onto a second system partition, with automatic A/B rollback if an update ever fails to boot.
  • An extra-small trusted baseBuilt on the gokrazy appliance framework: no shell, no package manager, a read-only image — a tiny attack surface.

Built on the gokrazy appliance framework.

// protect any device

IoT appliance

Put a SideServices appliance in front of an industrial machine or IoT device and expose only the specific services you choose — with no web traffic reaching the device by default.

  • SecurityPassword-authenticated over-the-air updates and a minimal trusted computing base keep the box secure and up to date.
  • Ease of useToken-based enrollment — as simple as plugging in a USB stick. No console, no config files.
Contact us about appliances Federation & IoT appliances are in alpha — tell us your use case.

Every grant, in plain sight.

The controller is a small web app on your own machine. You see every service, who it's granted to, and what's denied — and any device sees just the services granted to it, reachable by name. Click a screenshot to enlarge.

The controller's Services page: services grouped by the node that hosts them — postgres (tcp) on db-01 granted to specific peers, grafana (http) and a socks5 exit on web-01 with 'no clients — denied to all' — plus services offered to you and in use from other federations.
Services — deny-by-default. Each one shows exactly who it's granted to, or denied to all.
The controller's Nodes page: the devices enrolled in the federation — acme-controller, web-01, db-01 online and laptop-gabriel offline — with last-seen times and whether each may reach the controller UI.
Nodes — the devices in your federation, online or not, and who may reach the controller.
A node's dashboard at sideservices.localhost: services available to this node grouped by provider with direct/relay connection pills, each HTTP service browsable at service.federation.sideservices.localhost, services from an external federation, and the services this node provides.
One device's view at sideservices.localhost — HTTP services open at service.federation.sideservices.localhost.
The controller's Services page, full size. The controller's Nodes page, full size. A node's service list at sideservices.localhost, full size.

What's inside the binary.

Cross-platform, self-hosted, deny-by-default, post-quantum. No public DNS, no public TLS, no public anything.

// one binary

Every role, every OS.

Controller, hub, relay, consumer — the same static sidehub executable, the same commands. Linux, macOS, Windows, Android, iOS, FreeBSD, OpenBSD. No separate management or signal servers to run.

linuxmacoswindowsandroidiosfreebsdopenbsd
// access

Deny-by-default, per service.

You don’t join a flat network where everyone can see everyone. A service stays invisible until the controller grants it to a specific peer — and is revoked the same way.

postgres · tcp → laptop
wiki · http → phone
redis · tcp → denied to all
// addressing

Localhost is the address.

TCP and UDP services bind to a stable local port. Web services answer at service.federation.sideservices.localhost — resolved by the binary, never by a public resolver.

postgres localhost:5432
redis localhost:6379
wiki wiki.home-lab.sideservices.localhost
grafana grafana.home-lab.sideservices.localhost
// transport

WireGuard mesh, post-quantum keys.

Direct, encrypted hub-to-hub tunnels, with a relay fallback when a peer is behind NAT. Identities and key exchange are hybrid post-quantum (Ed25519 + ML-DSA, X25519 + ML-KEM) — harvest-now-decrypt-later doesn’t work here.

srv-01 laptop  wg · pq-sealed
laptop phone  wg · pq-sealed
edge-gw desktop  wg · pq-sealed
// federation

Share across organizations.

Hand another federation a code and it can consume one service of yours — scoped, revocable, deny-by-default. The two networks never merge, and neither side learns the other’s topology.

metrics acme → partner-co
registry acme → lab-team
// accounting

Every request, attributed.

A service records who used it, how much moved and how long it took — per HTTP request and per TCP flow. The detail stays on the node that serves the service; only counts roll up to the controller. Request paths are stored without their query string, so an audit trail never becomes the leak.

laptop registry · GET /v2/… · 412 KB
192.168.50.42 hmi-3 · tcp · 18 KB · 4m12s
partner-co metrics · 1,204 requests
// agentless

Devices that can't run software.

A PLC, a camera, a printer, a lab instrument. Plug it into the IoT appliance’s protected port and it reaches its granted service with nothing installed and nothing configured — and it is still attributed individually by its own address.

plc-1 192.168.50.20 → scada · tcp
camera 192.168.50.31 → nvr · tcp
analyzer 192.168.50.42 → lims · http
// trust

A tiny trusted base.

No sign-up, no vendor control plane, no telemetry. The only thing you have to trust is your own controller. The relay is untrusted — it can’t read your traffic, so it can be anyone’s (ours, a partner’s, your own). Your keys never leave your devices.

vendor control plane / telemetry
a relay that can read your traffic
public CA · public DNS · public ports
trust only your own controller.

How it compares.

Same WireGuard primitive as a mesh VPN. A different idea of who's in charge — and what's reachable.

Hosted mesh VPNs

A vendor runs the control plane you have to trust. You sign up; your coordination lives in their cloud.

SideServices

Your own controller is the only thing you trust — no account, no vendor in the loop. The relay is untrusted, so it can be ours, a partner's, or yours.

Self-hosted meshes

Every node gets a flat address, gated by network ACLs, plus a separate management or signal server to operate.

SideServices

Deny-by-default at the service level, and federation across organizations. One binary is every role — nothing extra to run.

Public meshes

A global, open network anyone can join; you firewall on top.

SideServices

Each federation is private and controller-signed — reachable only where you grant it.

Ad-hoc relay tools

A shared global relay for one-off shells through NAT.

SideServices

A persistent, encrypted mesh on infrastructure you own, with structured, revocable service access.

Across all of them: end-to-end post-quantum identity and key exchange, and services that answer on localhost — no public DNS, no public CA.

Questions, answered.

Short version: one binary, a post-quantum WireGuard mesh, deny-by-default service access, and a localhost addressing trick. Everything stays on devices you own.

Request alpha access →
What's a federation?

The set of devices (hubs) you’ve enrolled together. One hub is the controller — it signs enrollment and holds the federation’s identity. Every other hub reaches the others directly over WireGuard, with a relay fallback for hubs behind NAT. There’s no central data plane and no shared address space — just the services you grant.

How is this different from a mesh VPN?

A mesh VPN drops every device onto one flat network and gates it with ACLs. SideServices is deny-by-default at the service level: a service is invisible until the controller grants it to a specific peer. The WireGuard transport is the same proven primitive — the access model isn’t.

How do services get reached?

Through localhost. TCP and UDP services bind to a stable local port. Web services answer at service.federation.sideservices.localhost, resolved by the binary itself against state the controller signed — never by a public resolver.

Why no public TLS and no public DNS?

Because both already happen, privately, elsewhere in the stack. Traffic between hubs is encrypted by WireGuard; the few control channels use self-signed, fingerprint-pinned certificates and signed payloads, not the public PKI. Names resolve locally. Nothing about your services leaks to certificate-transparency logs or open resolvers.

Can I share a service with another organization?

Yes. Hand another federation a one-paste code and it can consume one specific service of yours — scoped and revocable, deny-by-default. The networks never merge, and the consuming side never learns your internal topology.

Is it quantum-safe?

Yes, end to end. Identities are hybrid Ed25519 + ML-DSA-65 signatures; enrollment and key exchange are hybrid X25519 + ML-KEM-768. A future quantum computer that records today’s traffic still can’t read it or forge an identity.

Can a device only consume — never host anything?

Yes. Deploy it without a rendezvous and it’s consume-only: it imports services others share with it and reaches them outbound, but exposes nothing. Attach a rendezvous later (no redeploy) if you ever want it to host services of its own.

Who runs the relay, and do I have to trust it?

No — and that’s the point. The relay (the same binary, in its rendezvous role) only forwards encrypted frames and sealed invitations between devices that can’t connect directly. It can’t read your traffic, your keys, or your service map, so it’s never part of your trusted computing base. Use our open relay (free, available on request), a partner’s, or run your own — it makes no difference to your security.

Do I need an account?

No. No sign-up, no central control plane, no telemetry. The controller is a device you own running the same binary. Codes travel hub-to-hub by whatever means you choose.

What's sidehub?

SideServices is the network and the product; sidehub is the single open binary that runs every role — controller, hub, relay, or consumer. One executable, no runtime dependencies.

How do I get it?

It’s in closed alpha right now, and free. Request access at info@sidephone.io. Public builds and source are coming soon.

Coming soon.

SideServices (the sidehub binary) is in closed alpha — free, but builds and source aren't public yet. Want in? Email us and we'll get you set up.

linux · amd64 linux · arm64 macos · universal windows · x64 ios · testflight android · apk freebsd openbsd source

one binary — every OS, every role · all builds coming soon