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.7.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
Exchange UDP datagrams Typed UDP
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.

Typed UDP

Tailkitty preserves application datagram boundaries instead of treating UDP as a byte stream. On the server, bind your UDP application to localhost and opt in to the same Tailcat port:

from tailkitty import ServerProcess

with ServerProcess(udp=5353, key="new") as server:
    print(server.token)
    input("Press Enter to stop the UDP tunnel... ")

On the client:

from tailkitty import Client

with Client("tc...").connect_udp(5353, timeout=10) as connection:
    reply = connection.request(b"one datagram", timeout=3)
    print(reply.data)

send() and receive() each handle exactly one datagram. Payloads are limited to MAX_UDP_PAYLOAD (1232 bytes), the safe size for Tailcat's tunnel MTU. Asyncio equivalents are available through AsyncClient.connect_udp().

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 browse 'tc...'
tailkitty recv ~/inbox
tailkitty cp report.pdf 'tc...':
tailkitty serve exec -- /usr/bin/fortune
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. Tailkitty's typed UDP API uses Tailcat v0.7's SOCKS5 UDP association and native UDP forwarding primitives plus an auditable patch that adds narrowly filtered, opt-in IPv4-loopback UDP serving to the CLI.

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
Client.connect_udp(...) Yes Datagram-preserving UDP connection
ServerProcess Yes Managed synchronous server
AsyncClient / AsyncServerProcess Yes Asyncio equivalents, including UDP
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.3.0

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.3.0
File Size Uploaded Provenance Actions
tailkitty-0.3.0.tar.gz 128.2 kB With provenance Details for tailkitty-0.3.0.tar.gz

Built distributions (wheels)

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

Total release size: 41.2 MB

Release files / tailkitty-0.3.0.tar.gz

Download URL tailkitty-0.3.0.tar.gz
Size 128.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5a2a3474f7b466bfb25bb2aa7750bdecd570e68ccddcf1a19474cb2d52f30e70
BLAKE2b-256 checksum
How to use checksums
0616ffbceaa61f0e87f70444bcfe27a91555577815d1f2a263012e5d2970ae21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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

Download URL tailkitty-0.3.0-py3-none-win_arm64.whl
Size 6.4 MB
Tags Python 3 Windows ARM64
SHA-256 checksum
How to use checksums
1071898c679cd1d3ef587febddea24529c94bc1eb16b354f77ba82629ce9dd02
BLAKE2b-256 checksum
How to use checksums
78f33f1215370ec6bac4f4523679748f4276684009c7ada3a0168f4b3a9b5868
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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

Download URL tailkitty-0.3.0-py3-none-win_amd64.whl
Size 7.2 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
972b05b1b89d4347d09f1e9e35eb4702e072da81713d73cb7b07d087d3791d26
BLAKE2b-256 checksum
How to use checksums
ba1623015914e3d615dfc3a9ec45ec18d8876811259f1e21c158a13330c83a83
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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

Download URL tailkitty-0.3.0-py3-none-manylinux_2_17_x86_64.whl
Size 7.2 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
a722612bd026508ab786da1a0746f79fad6723af4395428c7f1495e115044331
BLAKE2b-256 checksum
How to use checksums
565470d870bfe5d19eafb4e60fd91a0edfbf58d79caf6a1eaa4f7fb898dcf663
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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

Download URL tailkitty-0.3.0-py3-none-manylinux_2_17_aarch64.whl
Size 6.5 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
679ecb9a642bbc0e962270262713ab3abfbdc93c03e90aac723b553dbbcf7906
BLAKE2b-256 checksum
How to use checksums
f3d9121b6ec4f4c73bcdd99ce555cfb970793dc80e77e34191b36404d091fc14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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

Download URL tailkitty-0.3.0-py3-none-macosx_12_0_x86_64.whl
Size 7.1 MB
Tags Python 3 macOS 12.0+ x86-64
SHA-256 checksum
How to use checksums
04390ffd9fa0aaeb226900752e175d9234e8671520c5b0fd4a7bec389c074be4
BLAKE2b-256 checksum
How to use checksums
0a785801f7f37d8bfcfb8ba22bb4a50b7f5f91316dd02e26d8e682bfacabcda3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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

Download URL tailkitty-0.3.0-py3-none-macosx_12_0_arm64.whl
Size 6.6 MB
Tags Python 3 macOS 12.0+ ARM64
SHA-256 checksum
How to use checksums
f89b4147d0b80abb7bfc908aaff6e609a6cc605b4d139807c4a3e466abe0337d
BLAKE2b-256 checksum
How to use checksums
5322d87cd59028877728d2d6382dbe7dcbaf839d7e2d71649cf28384be086fc1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Security for tailkitty 0.3.0

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.

Full provenance

All 7 files in this release have been uploaded with provenance metadata.

Select from 7 files

Attestation for tailkitty-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

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