Skip to main content

Tailkitty

PyPI Python CI

Python tooling and verified platform wheels for Tailscale Tailcat: encrypted, account-free connections over Tailscale's data plane.

Tailkitty gives Python users two things:

  • Pure-Python tools for Tailcat addresses, DNS destinations, and DERP-map resolution.
  • A pinned Tailcat v0.6.0 executable for the interoperable WireGuard, DERP, NAT-traversal, and userspace-networking data plane.

The executable in each platform wheel is checked for its target, version, size, and SHA-256 digest before Tailkitty runs it. You do not need Go, root access, a Tailscale account, or a Tailscale control server.

Start here

Goal Start with
Send bytes between two computers One-shot pipe
Reach a remote web app or database Forward a port
Use Tailcat from Python Python quickstart
Transfer files or use SSH Recipes
Diagnose an installation Troubleshooting
Understand the trust boundary Security
Develop or package Tailkitty Contributing

Install

Install the command-line application with uv:

uv tool install tailkitty
tailkitty doctor

Add the typed Python library to a project with:

uv add tailkitty

pip install tailkitty also works. A supported platform receives a self-contained wheel with both the tailkitty command and a tailcat compatibility alias. Confirm what will run with:

tailkitty --version
tailkitty doctor
tailkitty version

These report the Tailkitty version, verified backend provenance, and upstream Tailcat version, respectively.

Supported wheels

Operating system Architecture Wheel platform tag
macOS 12+ Apple Silicon macosx_12_0_arm64
macOS 12+ Intel x86-64 macosx_12_0_x86_64
Linux, glibc 2.17+ x86-64 manylinux_2_17_x86_64
Linux, glibc 2.17+ ARM64 manylinux_2_17_aarch64
Windows x86-64 win_amd64
Windows ARM64 win_arm64

Python 3.11 and newer is supported. The Python code is ABI-independent, but the bundled executable is platform-specific. Unsupported systems can use the pure-Python control-plane features or set TAILKITTY_BACKEND to a compatible Tailcat executable.

Quickstart

Both machines need Tailkitty installed. A tc... address is case-sensitive; copy it exactly and quote it in shell commands.

One-shot pipe

On the receiving machine:

tailkitty --key=new < /dev/null
# 🐈 Server listening with new address: tc...

On the sending machine, paste the complete address:

printf 'hello from Tailkitty\n' | tailkitty --key=new 'tc...'

The receiver prints the message and both processes exit. This mode carries one finite byte stream; use port serving or Client.connect() for a long-lived connection.

PowerShell equivalents:

$null | tailkitty --key=new
'hello from Tailkitty' | tailkitty --key=new 'tc...'

Forward a port

Suppose a web application is listening on port 8080 of the server. Start Tailkitty there:

tailkitty serve 8080
# 🐈 Server listening with new address: tc...

On the client, expose it on local port 18080:

tailkitty forward 'tc...' 18080:8080

Open http://127.0.0.1:18080/. The forwarder binds to localhost by default and runs until you press Ctrl-C.

Python quickstart

On the server:

import time

from tailkitty import ServerProcess

# This allows anyone with the secret address to connect. See the security note below.
with ServerProcess(serve=8080, key="new", allow=None) as server:
    print(server.token, flush=True)
    while True:
        time.sleep(60)

On the client, after receiving the complete address:

from tailkitty import Client

response = Client("tc...").request(
    b"GET / HTTP/1.0\r\nHost: localhost\r\n\r\n",
    port=8080,
    timeout=30,
)
print(response.decode(errors="replace"))

The context manager waits for a validated address, then terminates and reaps the native process on exit. See the Python API guide for asyncio, streaming, token handling, server options, exceptions, and low-level execution.

CLI model

Tailkitty owns a small Python-native command surface:

tailkitty parse 'tc...'            # validate and decode an address as JSON
tailkitty resolve 'tc...'          # embed the referenced DERP relay details
tailkitty doctor                   # inspect backend selection and provenance
tailkitty doctor --json            # machine-readable diagnostics
tailkitty --version                # Tailkitty package version

Everything else passes unchanged to the pinned Tailcat executable:

tailkitty serve --help
tailkitty ping --until-direct 'tc...'
tailkitty forward 'tc...' 18080:8080
tailkitty recv ~/inbox
tailkitty cp report.pdf 'tc...':
tailkitty serve --ssh-authorized-keys=alice@github ssh
tailkitty ssh 'tc...'
tailkitty socks 'tc...' curl http://server.tailcat:8080/
tailkitty genkey --client --key=client-default

Run tailkitty readme for the documentation embedded in the bundled Tailcat version. Tailcat v0.6 also supports application-layer UDP through its Go API and SOCKS5 UDP association; Tailkitty does not currently expose a separate typed Python UDP API.

Addresses and DNS

A Tailcat address starts with tc and contains a CBOR document encoded as unpadded base64url. A current address can contain:

  • The server's WireGuard public key.
  • A separate discovery public key.
  • A WireGuard pre-shared key.
  • Either a DERP region ID or embedded relay details.

Tailkitty parses and validates those fields in Python. Unknown future CBOR fields are retained in the extensions mappings on ConnInfo, DerpRegion, and DerpNode, preventing resolution from silently stripping new upstream fields.

Tailcat commands and Client also accept a DNS name with a TXT record like:

service.example.com. 300 IN TXT "tailcat=tc..."

Short addresses refer to a DERP region by ID. tailkitty resolve expands one into a self-contained address using a bounded, validated DERP-map cache:

tailkitty parse 'tc...'
tailkitty resolve 'tc...' > full-address.txt

Python API overview

API Native backend? Purpose
parse_token(token) No Decode and validate an address into ConnInfo
resolve_token(token) No Embed DERP relay details in a short address
resolve_destination(value) No Resolve a literal address or DNS TXT destination
DerpMapCache No Bounded HTTP cache with ETag and stale fallback
Client.request(...) Yes Finite request/response exchange
Client.run(...) Yes Finite exchange with exit status and stderr
Client.connect(...) Yes Full-duplex subprocess.Popen connection
ServerProcess Yes Managed synchronous server
AsyncClient / AsyncServerProcess Yes Asyncio equivalents
run(...) / run_async(...) Yes Low-level Tailcat command execution
diagnostics() Yes Structured backend and environment report

The distribution includes py.typed and is checked with strict mypy. Public exceptions and full examples are documented in the Python API guide.

Security summary

  • Addresses are capability material. Current addresses include a pre-shared key; do not post them in logs, screenshots, issues, or public DNS unless another authorization layer is active.
  • A server allows every client by default. Generate a client identity with tailkitty genkey --client --key=client-default, then supply its printed public key with --allow.
  • allow=None in Python preserves the upstream allow-all default. allow=[] becomes --allow=none and denies every client.
  • TAILKITTY_BACKEND is explicit code-execution authority. Tailkitty checks that the path is executable but cannot prove the provenance of a user-selected binary.
  • Bundled executables fail closed: a present but invalid bundle raises an integrity error instead of falling back to an unverified executable.

Read the security policy before exposing SSH, files, an exit node, or a DNS-named service.

How it works

Python caller or tailkitty CLI
        |
        +-- token.py -------- address codec and validation (pure Python)
        +-- destination.py -- literal or DNS TXT lookup (pure Python)
        +-- derp.py --------- validated DERP-map cache (pure Python)
        +-- client.py ------- sync and asyncio client facade
        +-- process.py ------ managed server and subprocess lifecycle
        |
        +-- backend.py ------ deterministic backend discovery
                |
                +-- verified wheel bundle
                +-- development build
                +-- explicitly configured upstream executable

The encrypted data plane stays upstream because it depends on Tailscale's Go implementations of magicsock, userspace WireGuard, DERP, and gVisor netstack. Reimplementing only part of that stack in Python would lose interoperability and security properties. See architecture guide for the detailed boundary and data flows.

Backend selection

Tailkitty selects exactly one backend in this order:

  1. TAILKITTY_BACKEND, if explicitly set; an invalid path fails immediately.
  2. The verified, runtime-compatible executable bundled in the installed wheel.
  3. .tools/bin/tailcat, then the legacy .tools/bin/tailcat-go, in a source checkout.
  4. tailcat-go on PATH.

An installed tailcat command is not searched on PATH because Tailkitty itself provides that alias and recursive execution must be avoided. Use TAILKITTY_BACKEND=/path/to/tailcat for an external upstream binary.

Development

The repository uses mise for tool versions and uv for Python environments:

mise install
mise run setup
mise run test

Useful tasks:

mise run backend          # build the pinned development backend
mise run bundle           # build the host bundle
mise run bundle-verify    # verify its manifest and executable
mise run wheel            # build the host wheel
mise run wheels           # build and verify all six wheels
mise run upstream-check   # compare the pin with the latest stable Tailcat release

Start with CONTRIBUTING.md. Packaging and release details are in BUILDING.md and RELEASING.md. Coding agents must also follow AGENTS.md.

Documentation

  • Recipes — pipes, ports, files, SSH, SOCKS, DNS, and key management
  • Python API — typed sync/async usage and behavior
  • Troubleshooting — bounded diagnostics and common failures
  • Architecture — component boundaries, data flows, and invariants
  • Security policy — threats, authorization, bundle trust, and reporting
  • Building — reproducible native and wheel builds
  • Releasing — versioning, verification, publication, and rollback constraints
  • Comparison — Tailkitty versus the separate pytailcat package
  • Changelog — user-visible release history

License and relationship to Tailscale

Tailkitty is licensed under the MIT License. Bundled wheels contain upstream Tailcat under its BSD 3-Clause License and other Go dependencies; see THIRD_PARTY_NOTICES.md.

Tailcat and Tailscale are trademarks of Tailscale Inc. Tailkitty is not an official Tailscale product.

Metadata

Release files for tailkitty 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tailkitty 0.2.2
File Size Uploaded Provenance Actions
tailkitty-0.2.2.tar.gz 115.9 kB Without provenance Details for tailkitty-0.2.2.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for tailkitty 0.2.2
File Actions
tailkitty-0.2.2-py3-none-win_arm64.whl Python 3 none Windows ARM64 Details for tailkitty-0.2.2-py3-none-win_arm64.whl
tailkitty-0.2.2-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for tailkitty-0.2.2-py3-none-win_amd64.whl
tailkitty-0.2.2-py3-none-manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for tailkitty-0.2.2-py3-none-manylinux_2_17_x86_64.whl
tailkitty-0.2.2-py3-none-manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for tailkitty-0.2.2-py3-none-manylinux_2_17_aarch64.whl
tailkitty-0.2.2-py3-none-macosx_12_0_x86_64.whl Python 3 none macOS 12.0+ x86-64 Details for tailkitty-0.2.2-py3-none-macosx_12_0_x86_64.whl
tailkitty-0.2.2-py3-none-macosx_12_0_arm64.whl Python 3 none macOS 12.0+ ARM64 Details for tailkitty-0.2.2-py3-none-macosx_12_0_arm64.whl

Total release size: 40.6 MB

Release files / tailkitty-0.2.2.tar.gz

Download URL tailkitty-0.2.2.tar.gz
Size 115.9 kB
Tags Source
SHA-256 checksum
How to use checksums
09a0bafc20a34ac06c2f71bf3cd5b42856b7d347c610c79831b5065fcfe073e6
BLAKE2b-256 checksum
How to use checksums
824abd88c2de8c1a18539227423d5268b2da7036b1bb020a89aff3296a7129d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / tailkitty-0.2.2-py3-none-win_arm64.whl

Download URL tailkitty-0.2.2-py3-none-win_arm64.whl
Size 6.3 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
b280f36e214984929ec582ff73be42e62d0fe232545a3423116a98b404649d06
BLAKE2b-256 checksum
How to use checksums
452a075ee79b4aa6a841ac50b5bf1614f0fdb96b0e30d317d7a843bae6e24565
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / tailkitty-0.2.2-py3-none-win_amd64.whl

Download URL tailkitty-0.2.2-py3-none-win_amd64.whl
Size 7.1 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
0fa338e0090e3cbac81ebc877d725532fd1e1449821510a75268948be8821afc
BLAKE2b-256 checksum
How to use checksums
dd835e63d80222f1261588aadc82b5d30e2e07d6fe41502100a28f5358fae599
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / tailkitty-0.2.2-py3-none-manylinux_2_17_x86_64.whl

Download URL tailkitty-0.2.2-py3-none-manylinux_2_17_x86_64.whl
Size 7.1 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
df22baae7e1aa40ed7b449ab23576535ec3fcde1b82f924b06f61f686b47273a
BLAKE2b-256 checksum
How to use checksums
dc2245cc2843d676b0698618e6ff0e19583d023b877718b5caae1716c364bb0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / tailkitty-0.2.2-py3-none-manylinux_2_17_aarch64.whl

Download URL tailkitty-0.2.2-py3-none-manylinux_2_17_aarch64.whl
Size 6.4 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
75276ca4c483c4900149eddefb772a1ffa26938f70ac13d3cda3c424df62f249
BLAKE2b-256 checksum
How to use checksums
765cc1575fd461f9b8de483b82788b988716034941aba3625518193de89b6321
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / tailkitty-0.2.2-py3-none-macosx_12_0_x86_64.whl

Download URL tailkitty-0.2.2-py3-none-macosx_12_0_x86_64.whl
Size 7.0 MB
Tags Python 3 macOS 12.0+ x86-64
SHA-256 checksum
How to use checksums
5b0f22ae448a58a353183ceaa40372b277ce2f87b3fc508f209bf161e6810ff4
BLAKE2b-256 checksum
How to use checksums
bdb412a20cde5150f72b29a1165332872d3e8aafdf572c8f884e19771f002bd1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / tailkitty-0.2.2-py3-none-macosx_12_0_arm64.whl

Download URL tailkitty-0.2.2-py3-none-macosx_12_0_arm64.whl
Size 6.5 MB
Tags Python 3 macOS 12.0+ ARM64
SHA-256 checksum
How to use checksums
61a26645c119d98ebe0e03d638ae1c2cad3c8ab7e66aafc9817d084da14a0f72
BLAKE2b-256 checksum
How to use checksums
1e4202080486866bbd60a1687ba304c88433d375e27ff284b8708026fe9646c9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.11

Security for tailkitty 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page