Documentation navigationOpen menu

Kepos / User guide

Use Kepos to share and connect

A publisher owns a service and decides which trusted devices may open it. A subscriber receives those selected services at local URLs or ports. Kepos has no account system; choose the role that matches the device in front of you.

01 / Install

Platforms and installation

On narrow screens, scroll horizontally to see all columns.

Kepos platform downloads
Platform Role Artifact Release boundary
Android Subscriber only APK download Android 12+ · arm64-v8a
macOS Publisher + subscriber Apple Silicon ZIP Apple Silicon · ad-hoc signed
Windows Publisher + subscriber x64 ZIP Windows 10 build 19045+ or Windows 11

All three downloads stay available.

Android subscriber

Sideload the release APK, approve the device's request to install from that source if Android asks, and start the foreground service from the app. Android does not publish services, install a VPN, or provide a TUN interface.

Apple Silicon macOS

Unzip the release and open Kepos.app. macOS Gatekeeper may warn on first launch because this local preview is ad-hoc signed and not notarized; it is not a Developer ID or Mac App Store package.

Windows desktop

Extract the ZIP as a complete Kepos\ tree. Launch App\Kepos.exe for a portable run, or use Install.cmd for the supported per-user installation. The ZIP includes Windows App Runtime files, but WebView2 and the Microsoft Visual C++ Redistributable remain system prerequisites. SmartScreen and Defender Firewall prompts are expected boundaries for this unsigned-by-Authenticode preview.

02 / Connecting device

Connect as a subscriber

A subscriber receives selected services from a publisher. Its device identity is a key pair; it shares only the public key for approval. The publisher's allowlist names subscriber keys it accepts, and the subscriber pins the publisher public key before connecting.

  1. Install and start

    Install the artifact for the device you will carry. Packaged desktop startup fills missing config and enabled role state; Android creates its subscriber identity in app-private storage. See Trust and identities for what that state contains.

  2. Pair one publisher

    For Android, open Add device on a running macOS or Windows desktop publisher and scan its two-minute QR invitation. The publisher sees the candidate's label, platform, and subscriber-key fingerprint before you choose Allow or Deny. This is the supported interactive QR path.

    For a desktop subscriber, click Copy this device subscriber public key. On the desktop publisher, add that key to the [publisher].allow list and restart after the manual TOML edit. Copy the publisher public key from the publisher app, enter it in Connect this subscriber, and choose Connect / Save. Desktop QR or deep-link receipt is not supported in this release.

  3. Confirm success

    When the subscriber shows Connected and its service list appears, open or copy the first service address below. If it stays Connecting, use the checks in troubleshooting.

Open the first service

Once the registry is current, an HTTP service normally appears at http://<service-id>.localhost:17480/, for example http://navidrome.localhost:17480/. Kepos can open a web action or copy the local URL for a media client. SSH and other raw TCP services use the local listener configured for that subscriber; use the address and port shown by the app rather than assuming a universal port.

ssh -p <configured-local-port> 127.0.0.1

03 / Service owner

Publisher (Desktop)

Use the packaged macOS or Windows app when the service owner wants a graphical relationship view. The desktop app reads its publisher policy from TOML.

Configure the packaged app

The packaged app uses the normal Kepos configuration file: ~/.config/kepos/config.toml on macOS unless XDG_CONFIG_HOME is set, and %APPDATA%\Kepos\config.toml on Windows. Edit the file before launch when you need to enable publishing or change the services, then restart after a manual policy edit. There is no graphical service editor in this preview.

[publisher]
enabled = true
display_name = "Home"
allow = []

[[publisher.services]]
id = "navidrome"
name = "Navidrome"
target_port = 4533

List loopback TCP targets that already run on the publisher device. An empty allow list starts in deny-all mode; approving a device later adds its public key to the running policy.

Let startup create the identity

When enabled = true, desktop startup creates missing publisher state from this policy before starting the role. On later launches it validates the existing state and reuses the same seed and public key; it never rotates or overwrites that state. TOML allowlist and service changes are runtime policy, so they can change without rewriting the identity.

Launch and approve a device

  1. Keep the app running

    Open Kepos and leave the process running while it publishes.

  2. Choose Add device for Android

    Generate the invitation and let the Android subscriber scan it. The invitation expires after two minutes.

  3. Inspect, then Allow

    Check the label, platform hint, and public-key fingerprint. Allow persists the subscriber public key, updates the live allowlist, and promotes the same connection without restarting.

04 / Headless service owner

Publisher (CLI)

Use this path for repository checkouts, Nix, containers, and other headless hosts with explicit state directories and manual public-key exchange.

Check out and initialize

On the subscriber device, create an identity and copy the public key printed by the command:

npm run kepos -- setup subscriber \
  --state <subscriber-state-directory>

On the publisher device, put that public key in the publisher TOML policy:

[publisher]
display_name = "Home"
allow = ["<subscriber-public-key>"]

[[publisher.services]]
id = "navidrome"
name = "Navidrome"
target_port = 4533
npm run kepos -- setup publisher \
  --state <publisher-state-directory> \
  --config <publisher-config.toml>

This creates the publisher with the subscriber already allowed. The setup command writes the manifest and identity into the explicit state directory. Repeat it only with the same policy; CLI setup remains strict and never rotates an identity. Without a TOML publisher table, the same command accepts --display-name, --allow, and repeated --service options instead.

Update the allowlist and pin the publisher

If publisher state already exists in deny-all mode and no [publisher] TOML table owns its policy, add the subscriber public key with publisher set-allow. For a TOML-owned publisher, edit its allow array instead; do not combine TOML policy with CLI policy flags.

npm run kepos -- publisher set-allow \
  --state <publisher-state-directory> \
  --allow <subscriber-public-key>

npm run kepos -- publisher key \
  --state <publisher-state-directory>

npm run kepos -- subscriber set-publisher \
  --state <subscriber-state-directory> \
  --label Home \
  --publisher-key <publisher-public-key>

Copy the Publisher key: output and pin it with subscriber set-publisher.

Run and update a headless publisher

npm run kepos -- publisher run \
  --state <publisher-state-directory> \
  --config <publisher-config.toml>

npm run kepos -- subscriber run \
  --state <subscriber-state-directory>

Start the publisher before the subscriber. A headless process reads its policy at startup, so restart publisher run after a TOML allowlist or service edit, then restart the subscriber if its pinned contact changed. Use the repository's Nix, container, Windows, and NUC deployment guides for process supervision and platform-specific lifecycle details.

05 / Identity and boundary

Public keys move; secrets stay home

Kepos uses device-owned identities rather than an account or shared bearer token. Publisher authorization remains local to the publisher that owns the service.

Separate identities
Publishers and subscribers each keep their own key pair. A displayed long hexadecimal key is public.
Local approval
The publisher's allowlist and optional per-service allowlist decide what an approved subscriber may open.
No secret copies
Keep publisher seeds and subscriber secret keys in the private storage that generated them. Do not put identity files in logs.

Configuration and role state

TOML is mutable role policy: it names services and allowed subscriber keys. Role state is private identity storage: publisher state holds its seed and subscriber state holds its key pair and publisher contact. Packaged startup behavior is described in Publisher (Desktop); Android keeps its subscriber identity in app-private storage across an in-place update.

06 / Why this shape

Keep the app; change the route

Kepos changes how a selected service is distributed, not what the service is. That keeps mature software's capabilities and client experience intact.

  1. Keep mature applications

    Navidrome, Jellyfin, Forgejo or Gitea, SSH, and other self-hosted services continue to run as ordinary local applications.

  2. Change service distribution

    Kepos carries selected TCP service byte streams through an authenticated direct peer connection with named channels.

  3. Preserve clients

    A browser opens a local URL, a media client receives a local address, a Git client uses its normal endpoint, and SSH uses a loopback port.

07 / A respectful comparison

Kepos and Holesail

Both projects offer direct encrypted peer-to-peer access without requiring a public service port. The difference is the unit of sharing: Kepos starts with a persistent authenticated device relationship; Holesail documents a port tunnel and connection key.
Compared with Holesail 2.4.1 and its current official documentation, checked 2026-08-22.

The architectural difference

Pair once. Open many named services.

Kepos turns a trusted device relationship into durable transport: adding a service opens a named channel, not another tunnel/key workflow.

Kepos

One relationship, many channels

One Kepos connection, three named service channels A publisher and subscriber share one outer connection. That connection branches into named home, ssh, and music service channels. TRUSTED DEVICE PAIR PUBLISHER DEVICE ONE OUTER CONNECTION SUBSCRIBER DEVICE home ssh music

One authenticated outer connection carries the registry and independent service streams.

Holesail

One port tunnel at a time

Three Holesail services, three port tunnel workflows Web, ssh, and music each use a distinct port tunnel and connection key in Holesail's documented model. DOCUMENTED PORT-TUNNEL MODEL web :3000 TUNNEL + KEY local :3000 ssh :22 TUNNEL + KEY local :22 music :4533 TUNNEL + KEY local :4533

Its connection key resolves to one host and port; another service begins another tunnel/key workflow.

Documented capabilityNot in the current documented model

On narrow screens, scroll horizontally to see all columns.

Kepos and Holesail comparison
Dimension Kepos Holesail
Direct encrypted P2P Yes. Authenticated HyperDHT connection. Yes. Direct encrypted tunnel.
Persistent device relationship Yes. Separate publisher and subscriber identities. No. Official docs define private or public connection keys per tunnel.
Pair once; add named services Yes. One durable outer connection carries named service channels. No. Its documented model uses one port tunnel and connection key per exposed service.
Per-service allowlists Yes. Services may narrow the publisher allowlist. No. Private mode checks the connection key; public mode has no key authentication.
Public connection mode No. Service access requires an allowed subscriber identity. Yes. --public exposes a public connection key without key authentication.
Graphical desktop onboarding Yes. macOS and Windows show relationships, services, and approval. No. The official desktop guide documents terminal executables.
Mobile QR onboarding Yes. Android scans a two-minute publisher invitation. Yes. Holesail Go scans connection-key QR codes on iOS and Android.
TCP service path Yes. Local TCP ends at each Kepos peer. Yes. TCP tunnels are supported.
UDP service path No. Kepos services are currently TCP-only. Yes. Official docs include UDP tunnels.
One-command port sharing No. Kepos starts from a device and service policy. Yes. Start with holesail --live <PORT>.

Choose Kepos for long-lived devices and named service policy; choose Holesail for a quick port tunnel or UDP. The indicators describe current documented capabilities, not a quality score. Sources:

08 / When it does not connect

Find the boundary

Kepos's current direct path needs usable outbound UDP and a NAT pair that HyperDHT can punch. Check the observable boundary in order.

Read the connection states

Connecting
The subscriber is attempting the pinned publisher connection.
Connected
The publisher registry has loaded; open or copy a listed service.
Reconnecting
Local listeners stay in place while Kepos replaces the outer connection; active client streams need their own retry.

Configured but still Connecting

  1. Confirm the subscriber's pinned publisher public key is the key shown by the intended publisher.
  2. Confirm the publisher is running and its subscriber public key is in the publisher allowlist. An empty allowlist denies everyone.
  3. Keep the desktop process in the tray or menu bar. Closing the window hides it; Quit Kepos stops publishing.
  4. Wait for a direct attempt to finish, then check local diagnostics rather than copying state files or secrets into a report.

Firewall, VPN, or TUN interference

On Windows, allow Kepos through Windows Defender Firewall on the networks where the session needs DHT connectivity. Kepos does not open a public TCP service port and does not automatically bypass a firewall. VPN or TUN software can intercept or block the UDP path; test its documented bypass or pause behavior only if you control that device and understand the consequence. Kepos does not promise a relay fallback.

Bootstrap and configuration

Packaged desktop releases include bootstrap endpoints for a fresh default config. A configured [network].bootstrap list takes precedence over that packaged default; verify each host and UDP port is reachable from both peers. In the CLI, an empty bootstrap list selects HyperDHT defaults. A bootstrap node helps a peer enter the DHT; it does not authorize a subscriber or carry the service stream.

Allowlist and policy edits

A desktop Add device approval updates the live publisher. A headless TOML edit does not: restart the headless publisher run process after changing the publisher allowlist or service policy. A per-service allowlist can be narrower than the publisher-wide list, so an approved device may still be unable to open one service.

Copy sanitized diagnostics

On desktop, open Settings and choose Copy diagnostics. The summary is bounded and sanitized for support: it can describe roles, states, observations, and counters, but omits secret identity material, pairing tokens, and candidate IP addresses. Do not paste config or state files into a bug report.