tailkitty 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {tailkitty-0.2.1 → tailkitty-0.2.2}/AGENTS.md +36 -3
  2. tailkitty-0.2.2/BUILDING.md +216 -0
  3. {tailkitty-0.2.1 → tailkitty-0.2.2}/CHANGELOG.md +11 -0
  4. tailkitty-0.2.2/COMPARISON.md +64 -0
  5. tailkitty-0.2.2/CONTRIBUTING.md +131 -0
  6. {tailkitty-0.2.1 → tailkitty-0.2.2}/ITERATIONS.md +5 -0
  7. tailkitty-0.2.2/PKG-INFO +372 -0
  8. tailkitty-0.2.2/README.md +347 -0
  9. tailkitty-0.2.2/RELEASING.md +172 -0
  10. tailkitty-0.2.2/SECURITY.md +207 -0
  11. tailkitty-0.2.2/docs/architecture.md +203 -0
  12. tailkitty-0.2.2/docs/python-api.md +355 -0
  13. tailkitty-0.2.2/docs/recipes.md +341 -0
  14. tailkitty-0.2.2/docs/troubleshooting.md +259 -0
  15. {tailkitty-0.2.1 → tailkitty-0.2.2}/pyproject.toml +15 -2
  16. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/targets.py +8 -0
  17. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/__init__.py +4 -2
  18. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/constants.py +1 -1
  19. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/derp.py +3 -1
  20. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/process.py +53 -5
  21. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_bundle.py +7 -0
  22. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_cli.py +1 -1
  23. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_module.py +1 -1
  24. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_process.py +30 -2
  25. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_targets.py +2 -2
  26. {tailkitty-0.2.1 → tailkitty-0.2.2}/uv.lock +1 -1
  27. tailkitty-0.2.1/BUILDING.md +0 -50
  28. tailkitty-0.2.1/COMPARISON.md +0 -30
  29. tailkitty-0.2.1/PKG-INFO +0 -414
  30. tailkitty-0.2.1/README.md +0 -389
  31. tailkitty-0.2.1/SECURITY.md +0 -27
  32. {tailkitty-0.2.1 → tailkitty-0.2.2}/.gitignore +0 -0
  33. {tailkitty-0.2.1 → tailkitty-0.2.2}/.mise.toml +0 -0
  34. {tailkitty-0.2.1 → tailkitty-0.2.2}/.python-version +0 -0
  35. {tailkitty-0.2.1 → tailkitty-0.2.2}/LICENSE +0 -0
  36. {tailkitty-0.2.1 → tailkitty-0.2.2}/THIRD_PARTY_NOTICES.md +0 -0
  37. {tailkitty-0.2.1 → tailkitty-0.2.2}/hatch_build.py +0 -0
  38. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/__init__.py +0 -0
  39. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/build_binary.py +0 -0
  40. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/build_wheels.py +0 -0
  41. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/check_upstream.py +0 -0
  42. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/smoke_wheel.py +0 -0
  43. {tailkitty-0.2.1 → tailkitty-0.2.2}/scripts/verify_wheel.py +0 -0
  44. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/__main__.py +0 -0
  45. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/backend.py +0 -0
  46. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/bundle.py +0 -0
  47. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/cli.py +0 -0
  48. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/client.py +0 -0
  49. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/destination.py +0 -0
  50. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/diagnostics.py +0 -0
  51. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/py.typed +0 -0
  52. {tailkitty-0.2.1 → tailkitty-0.2.2}/src/tailkitty/token.py +0 -0
  53. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_backend.py +0 -0
  54. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_derp.py +0 -0
  55. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_destination.py +0 -0
  56. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_project.py +0 -0
  57. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_token.py +0 -0
  58. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_token_validation.py +0 -0
  59. {tailkitty-0.2.1 → tailkitty-0.2.2}/tests/test_upstream.py +0 -0
@@ -12,6 +12,14 @@ Read these files before making architectural, packaging, security, or release ch
12
12
  3. `BUILDING.md` for the reproducible bundle and wheel pipeline.
13
13
  4. `pyproject.toml` and `.mise.toml` for supported versions and canonical tasks.
14
14
 
15
+ For a focused task, then read the matching guide:
16
+
17
+ - `docs/python-api.md` for public Python behavior.
18
+ - `docs/recipes.md` for user-facing command examples.
19
+ - `docs/troubleshooting.md` for failure and diagnostic guidance.
20
+ - `docs/architecture.md` for ownership and data flows.
21
+ - `RELEASING.md` before changing versions, tags, workflows, or publication.
22
+
15
23
  Do not confuse Tailkitty with the separate `pytailcat` distribution on PyPI. This checkout is the
16
24
  source of truth for Tailkitty; upstream Tailcat remains the data-plane source of truth.
17
25
 
@@ -44,11 +52,14 @@ compatibility and fail-closed bundle discovery are core requirements.
44
52
  | `scripts/targets.py` | Supported build targets and wheel tags |
45
53
  | `scripts/build_binary.py` | Reproducible cross-compilation and manifest creation |
46
54
  | `patches/` | Auditable patches applied to the immutable upstream Tailcat source |
47
- | `scripts/build_wheels.py` | Isolated five-target wheel orchestration |
55
+ | `scripts/build_wheels.py` | Isolated six-target wheel orchestration |
48
56
  | `scripts/verify_wheel.py` | Wheel metadata, archive, binary, and RECORD verification |
49
57
  | `scripts/check_upstream.py` | Latest stable Tailcat release drift check |
50
58
  | `hatch_build.py` | Platform-wheel build hook |
51
59
  | `tests/` | Unit and subprocess behavior tests |
60
+ | `docs/` | Task-oriented user, API, troubleshooting, and architecture guides |
61
+ | `CONTRIBUTING.md` | Human contributor workflow and checklist |
62
+ | `RELEASING.md` | Release metadata, artifact, publishing, and verification checklist |
52
63
 
53
64
  Public imports are curated in `src/tailkitty/__init__.py`. Adding a public API requires updating
54
65
  that file, type annotations, tests, and README documentation.
@@ -67,6 +78,9 @@ mise run test
67
78
  strict mypy, and pytest. During iteration, run the narrowest relevant test first, then run the full
68
79
  gate before declaring completion.
69
80
 
81
+ Direct build-module commands must run through `mise exec --`; plain `uv run` can select an
82
+ unrelated system Go version.
83
+
70
84
  Packaging changes also require:
71
85
 
72
86
  ```console
@@ -144,6 +158,24 @@ without an explicit compatibility decision.
144
158
  keys, cache, bundle, or configured backend.
145
159
  - Keep Ruff's 100-character line length and run formatting rather than hand-aligning code.
146
160
 
161
+ ## Documentation conventions
162
+
163
+ - The README is the onboarding map, not the exhaustive reference. Put task recipes, detailed API
164
+ behavior, troubleshooting, and architecture in their dedicated guides.
165
+ - Documentation on `main` describes the current source tree. Cut a release promptly when the user
166
+ asks for GitHub, PyPI, and `main` to match.
167
+ - State which machine runs each side of multi-peer examples.
168
+ - Use complete copyable commands and quote `tc...` placeholders because addresses are
169
+ case-sensitive shell data.
170
+ - Never put a syntactically plausible private key or active address in documentation.
171
+ - Distinguish encryption, address possession, tunnel allow-lists, SSH keys, and application
172
+ authentication; do not collapse them into a generic “secure” claim.
173
+ - Do not claim typed Python support for an upstream CLI or Go-library feature unless a Python API
174
+ actually exists.
175
+ - Keep relative Markdown links resolvable from the file containing them and include new `docs/`
176
+ files in the source distribution.
177
+ - When a command or flag is version-sensitive, check it against the pinned backend's `--help`.
178
+
147
179
  ## Generated files and release safety
148
180
 
149
181
  Do not manually edit or commit generated files under:
@@ -155,8 +187,9 @@ Do not manually edit or commit generated files under:
155
187
 
156
188
  Use the scripts and mise tasks that own those artifacts. Publish only the `tailkitty` distribution;
157
189
  the `pytailcat` name belongs to another project. Before a release, verify project URLs and the `pypi`
158
- GitHub environment, build all five wheels plus the source distribution, smoke-test the host wheel,
159
- and retain checksums and provenance attestations.
190
+ GitHub environment, build all six wheels plus the source distribution, smoke-test the host wheel,
191
+ and retain checksums and provenance attestations. Follow `RELEASING.md`; do not race automated and
192
+ local publishers or upload from a directory containing artifacts from multiple versions.
160
193
 
161
194
  ## Definition of done
162
195
 
@@ -0,0 +1,216 @@
1
+ # Building Tailkitty
2
+
3
+ Tailkitty builds one upstream Tailcat executable per supported target, records its provenance in a
4
+ manifest, and packages it in a platform-specific Python wheel. Source distributions remain
5
+ binary-free.
6
+
7
+ For the end-to-end publication checklist, see [RELEASING.md](RELEASING.md).
8
+
9
+ ## Pinned environment
10
+
11
+ Mise is the source of truth for build tools:
12
+
13
+ | Tool | Pin |
14
+ | --- | --- |
15
+ | Python | 3.13.11 |
16
+ | Go | 1.27.1 |
17
+ | uv | 0.12.7 |
18
+ | Tailcat | `v0.6.0` |
19
+
20
+ Install and synchronize them with:
21
+
22
+ ```console
23
+ mise install
24
+ uv sync --all-groups --locked
25
+ ```
26
+
27
+ The builder sets `GOTOOLCHAIN=local` and rejects any Go version other than the exact pin. When
28
+ running a Python build module directly, enter mise's environment:
29
+
30
+ ```console
31
+ mise exec -- uv run python -m scripts.build_wheels --target linux-x86_64
32
+ ```
33
+
34
+ Using plain `uv run` can select an unrelated system Go compiler.
35
+
36
+ ## Canonical tasks
37
+
38
+ ```console
39
+ mise run test # Ruff, formatting, strict mypy, pytest
40
+ mise run backend # source-checkout backend under .tools/bin
41
+ mise run bundle # host bundle under src/tailkitty/bin
42
+ mise run bundle-verify # verify the host bundle and manifest
43
+ mise run wheel # package the host bundle
44
+ mise run wheels # build and verify all six platform wheels
45
+ mise run upstream-check # compare the Tailcat pin with the latest stable release
46
+ ```
47
+
48
+ Generated content under `.tools/bin/`, `src/tailkitty/bin/`, and `dist/` is ignored and must not be
49
+ edited or committed.
50
+
51
+ ## Build pipeline
52
+
53
+ `scripts.build_binary` performs these steps:
54
+
55
+ 1. Resolve the requested target.
56
+ 2. Verify the exact Go compiler.
57
+ 3. Download `github.com/tailscale/tailcat@<immutable-pin>` through the Go module system.
58
+ 4. Copy the source into a temporary writable workspace.
59
+ 5. Check and apply every lexically sorted patch under `patches/`.
60
+ 6. Read upstream's release build tags from `build-tags.txt`.
61
+ 7. Cross-compile `./cmd/tailcat` with the reproducibility and compatibility settings below.
62
+ 8. Check the executable header and minimum plausible size.
63
+ 9. Atomically place the executable and write `manifest.json`.
64
+
65
+ The build uses:
66
+
67
+ - `CGO_ENABLED=0`
68
+ - `GOTOOLCHAIN=local`
69
+ - `GOAMD64=v1`
70
+ - `GOARM64=v8.0`
71
+ - `-trimpath`
72
+ - `-buildvcs=false`
73
+ - stripped symbols
74
+ - an empty Go build ID
75
+ - upstream's official release omission tags
76
+
77
+ Module downloads have three bounded attempts with short exponential backoff for transient failures.
78
+ Build failures are not silently converted into old or partial bundles.
79
+
80
+ ## Bundle manifest
81
+
82
+ Each generated bundle contains its executable and a schema-1 JSON manifest. The manifest records:
83
+
84
+ - Internal target name and wheel platform tag.
85
+ - Executable filename, size, and SHA-256.
86
+ - Tailcat module and immutable version.
87
+ - Applied patch filenames and SHA-256 values.
88
+ - Go compiler version.
89
+ - Reproducibility flags and release build tags.
90
+ - CGO policy.
91
+
92
+ Build-time verification rechecks those inputs. Runtime verification additionally requires the
93
+ manifest target to match the current interpreter platform, rejects path-containing filenames and
94
+ symlinks, and refuses size or digest mismatches.
95
+
96
+ ## Wheel matrix
97
+
98
+ | Build target | Go target | Wheel tag | Executable |
99
+ | --- | --- | --- | --- |
100
+ | `macos-arm64` | `darwin/arm64` | `macosx_12_0_arm64` | Mach-O `tailcat` |
101
+ | `macos-x86_64` | `darwin/amd64` | `macosx_12_0_x86_64` | Mach-O `tailcat` |
102
+ | `linux-x86_64` | `linux/amd64` | `manylinux_2_17_x86_64` | ELF `tailcat` |
103
+ | `linux-aarch64` | `linux/arm64` | `manylinux_2_17_aarch64` | ELF `tailcat` |
104
+ | `windows-x86_64` | `windows/amd64` | `win_amd64` | PE `tailcat.exe` |
105
+ | `windows-arm64` | `windows/arm64` | `win_arm64` | PE `tailcat.exe` |
106
+
107
+ Build one target:
108
+
109
+ ```console
110
+ mise exec -- uv run python -m scripts.build_wheels \
111
+ --target windows-arm64 \
112
+ --output dist/wheels
113
+ ```
114
+
115
+ Build all targets:
116
+
117
+ ```console
118
+ mise run wheels
119
+ ```
120
+
121
+ Each target uses a separate temporary bundle directory. Environment variables tell the Hatch build
122
+ hook which bundle and exact tag to include. This prevents one architecture's executable from
123
+ leaking into another wheel.
124
+
125
+ ## Wheel verification
126
+
127
+ `scripts.verify_wheel` opens the wheel as an archive and checks:
128
+
129
+ - The filename and `WHEEL` metadata carry the expected platform tag.
130
+ - The wheel is marked non-pure.
131
+ - Archive members are unique and cannot escape through absolute or `..` paths.
132
+ - Total uncompressed content is bounded.
133
+ - `py.typed`, the manifest, and the one expected executable are present.
134
+ - Manifest target, module, Tailcat pin, patch set, tags, size, and digest match build inputs.
135
+ - The executable begins with the expected Mach-O, ELF, or PE magic.
136
+ - Every non-`RECORD` member has the exact hash and size recorded in `RECORD`.
137
+ - `RECORD` names exactly the archive contents.
138
+
139
+ Cross-compiled executables cannot be run on the build host, so this structural verification is
140
+ paired with native CI builds and the host smoke test.
141
+
142
+ ## Host smoke test
143
+
144
+ Build or select the current host wheel, then run:
145
+
146
+ ```console
147
+ uv run python -m scripts.smoke_wheel dist/wheels/<host-wheel>.whl
148
+ ```
149
+
150
+ The smoke test:
151
+
152
+ 1. Creates a temporary isolated Python environment.
153
+ 2. Installs only the selected wheel and its dependencies.
154
+ 3. Runs `tailkitty doctor --json` and confirms the bundled backend is selected.
155
+ 4. Starts an isolated local DERP/STUN relay through Tailcat's test mode.
156
+ 5. Starts a peer and performs an actual encrypted ping.
157
+ 6. Applies five-second startup and client bounds, followed by bounded termination.
158
+
159
+ It does not depend on public relay availability and does not touch saved developer keys.
160
+
161
+ ## Source distribution
162
+
163
+ Build a source archive after removing any generated host bundle:
164
+
165
+ ```console
166
+ mise run bundle-clean
167
+ uv build --sdist --out-dir dist/release
168
+ ```
169
+
170
+ The Hatch sdist configuration includes Python source, scripts, tests, patches, documentation, lock
171
+ and tool configuration, but excludes generated executables and manifests. A source installation can
172
+ use pure-Python address functionality immediately; networking requires a separately built or
173
+ configured backend.
174
+
175
+ ## Reproducibility scope
176
+
177
+ Tailkitty pins the compiler, module version, build tags, target baseline, and flags that commonly
178
+ introduce host or VCS variation. The manifest makes every shipped executable auditable. Byte-for-byte
179
+ reproduction can still depend on the Go module proxy serving the same immutable module contents and
180
+ on the pinned tool downloads remaining available.
181
+
182
+ `SOURCE_DATE_EPOCH` is set during matrix wheel builds so archive metadata is stable. Reproducibility
183
+ does not mean one binary can be relabeled for another platform; each wheel is built and verified for
184
+ exactly one target.
185
+
186
+ ## Upstream updates
187
+
188
+ ```console
189
+ mise run upstream-check
190
+ ```
191
+
192
+ This bounded GitHub API check fails when a newer stable Tailcat release exists. A daily scheduled
193
+ workflow runs the same command. Before updating the pin, review:
194
+
195
+ - Upstream changelog and security notes.
196
+ - `wire.go` for address fields and validation changes.
197
+ - CLI help and command ownership conflicts.
198
+ - `build-tags.txt` and Go version.
199
+ - New platform requirements.
200
+ - Whether local patches are now upstream or no longer apply.
201
+
202
+ Do not silently pin `main`. An immutable pseudo-version is acceptable only as an explicit,
203
+ documented compatibility decision.
204
+
205
+ ## Common build failures
206
+
207
+ | Failure | Likely cause | Action |
208
+ | --- | --- | --- |
209
+ | Wrong Go version | Command ran outside mise | Prefix the direct command with `mise exec --` |
210
+ | Patch check fails | Upstream source changed | Review and update or remove the patch; never force it |
211
+ | One wheel expected, several found | Output contains another artifact with the same version/tag | Use a clean release directory |
212
+ | Wrong executable magic | Target or output staging mismatch | Inspect target selection; do not relabel the wheel |
213
+ | Host smoke timeout | Backend did not advertise or handshake in bounds | Inspect local relay logs and process cleanup |
214
+ | Public DERP timeout | External relay availability | Reproduce with the isolated smoke test |
215
+
216
+ More user-facing failures are covered in [docs/troubleshooting.md](docs/troubleshooting.md).
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2 - 2026-09-08
4
+
5
+ - Use Tailcat's declarative `serve` subcommand in managed Python server processes.
6
+ - Add typed sync/async server options for DERP-map selection, PSK policy, file roots, and SSH
7
+ authorized-key sources.
8
+ - Add a verified Windows ARM64 platform wheel target.
9
+ - Replace the monolithic README with task-oriented recipes, API, troubleshooting, architecture,
10
+ contributor, build, security, and release documentation.
11
+ - Export `BackendNotFound` and `ServerStartError` from the public package and report the current
12
+ version in DERP-map requests.
13
+
3
14
  ## 0.2.1 - 2026-09-08
4
15
 
5
16
  - Match upstream token rejection for trailing CBOR data, signed 64-bit overflows, and malformed
@@ -0,0 +1,64 @@
1
+ # Tailkitty and `pytailcat`
2
+
3
+ Tailkitty and the separate [`pytailcat`](https://pypi.org/project/pytailcat/) distribution are
4
+ independent Python projects built around upstream
5
+ [Tailscale Tailcat](https://github.com/tailscale/tailcat). They are not rename-compatible packages:
6
+ install and import the one you intend.
7
+
8
+ This comparison was checked on 2026-09-08 against `pytailcat` 0.1.4 and Tailkitty 0.2.2.
9
+
10
+ ## Summary
11
+
12
+ - Choose Tailkitty for current Tailcat behavior, pure-Python address tooling, asyncio, managed
13
+ lifecycle, fail-closed bundle verification, and reproducible release evidence.
14
+ - Choose `pytailcat` when Python 3.8–3.10 or older macOS deployment targets are required.
15
+ - Choose upstream Tailcat directly when no Python API or Python packaging is needed.
16
+
17
+ ## Feature comparison
18
+
19
+ | Capability | `pytailcat` 0.1.4 | Tailkitty 0.2.2 |
20
+ | --- | --- | --- |
21
+ | Distribution/import | `pytailcat` | `tailkitty` |
22
+ | Upstream data plane | Bundled Tailcat executable | Pinned Tailcat v0.6.0 executable |
23
+ | Upstream revision | Pre-release commit `c04c5af` | Signed stable tag `v0.6.0` |
24
+ | CLI compatibility | `tailcat` wrapper | `tailkitty` plus `tailcat` alias |
25
+ | Address parsing | Delegated to executable | Typed pure-Python `ConnInfo` model |
26
+ | Modern address fields | Depends on older bundled executable | Disco key and WireGuard PSK validated and preserved |
27
+ | Future address fields | Depends on executable | Unknown root and nested extensions round-trip losslessly |
28
+ | DERP expansion | Delegated to executable | Pure Python with bounded ETag cache and stale fallback |
29
+ | DNS destinations | Delegated to executable | Pure-Python sync and asyncio resolution |
30
+ | Client API | Thin synchronous process wrapper | Typed finite, streaming, sync, and asyncio clients |
31
+ | Server API | Synchronous `ServerProcess` | Managed sync/async servers with typed v0.6 options |
32
+ | Lifecycle cleanup | Basic process management | Bounded startup, terminate/kill escalation, reaping |
33
+ | Bundle verification | Package transport integrity | Runtime target, schema, size, and SHA-256 verification |
34
+ | Invalid bundle policy | Not documented as fail-closed | Explicit fail-closed behavior |
35
+ | Build environment | uv-based | mise + uv with exact Python, Go, and Tailcat pins |
36
+ | Wheel checks | Prebuilt wheel matrix | Archive, manifest, binary magic, and every `RECORD` entry |
37
+ | Data-plane smoke test | Server lifecycle startup | Real encrypted peer handshake through local DERP/STUN |
38
+ | Platforms | Five wheel targets | Six wheel targets, including Windows ARM64 |
39
+ | Python requirement | 3.8+ | 3.11+ |
40
+ | macOS floor | Older Intel/ARM targets | macOS 12+ |
41
+ | Project license | BSD-3-Clause | MIT; bundled Tailcat remains BSD-3-Clause |
42
+
43
+ Both projects retain the correct architectural boundary: the complex, interoperable network data
44
+ plane is native Go. Tailkitty's “pure Python” claim applies to address parsing, DNS lookup, and DERP
45
+ resolution—not to WireGuard, NAT traversal, or relay transport.
46
+
47
+ ## What Tailkitty deliberately does not claim
48
+
49
+ - Tailkitty is not an official Tailscale product.
50
+ - It is not a pure-Python VPN or a replacement Tailscale control plane.
51
+ - It has not demonstrated a general throughput advantage over `pytailcat`; both ultimately depend
52
+ on Tailcat and network path behavior.
53
+ - More validation does not replace PyPI/package-manager trust or an upstream security review.
54
+ - Newer Python and macOS minimums are real compatibility costs, not hidden implementation details.
55
+
56
+ ## Why a separate name exists
57
+
58
+ The `pytailcat` name belongs to another project. Tailkitty uses distinct distribution, import, and
59
+ primary CLI names to avoid dependency confusion and accidental replacement. The optional `tailcat`
60
+ command is only a compatibility alias inside an environment where Tailkitty was deliberately
61
+ installed.
62
+
63
+ For architectural details, see [docs/architecture.md](docs/architecture.md). For the exact current
64
+ platform matrix, see [README.md](README.md#supported-wheels).
@@ -0,0 +1,131 @@
1
+ # Contributing to Tailkitty
2
+
3
+ Thank you for improving Tailkitty. The project values upstream compatibility, explicit security
4
+ boundaries, deterministic tests, and documentation that can be followed without hidden context.
5
+
6
+ ## Before starting
7
+
8
+ Read:
9
+
10
+ 1. [README.md](README.md) for public behavior.
11
+ 2. [SECURITY.md](SECURITY.md) for authorization and executable trust.
12
+ 3. [docs/architecture.md](docs/architecture.md) for the Python/native boundary.
13
+ 4. [BUILDING.md](BUILDING.md) for packaging changes.
14
+
15
+ Automated contributors must also follow [AGENTS.md](AGENTS.md).
16
+
17
+ ## Development environment
18
+
19
+ Install [mise](https://mise.jdx.dev/) and clone the repository, then run:
20
+
21
+ ```console
22
+ mise install
23
+ mise run setup
24
+ mise run test
25
+ ```
26
+
27
+ Mise pins Python, Go, and uv. Do not create a second toolchain configuration or commit a local
28
+ virtual environment. `mise run setup` synchronizes the uv environment and builds the pinned Tailcat
29
+ development backend.
30
+
31
+ The normal edit loop is:
32
+
33
+ ```console
34
+ uv run pytest tests/test_token.py
35
+ mise run test
36
+ ```
37
+
38
+ Use the narrowest relevant test while iterating, then run the complete quality gate before handing
39
+ off a change. The gate includes Ruff linting, Ruff formatting checks, strict mypy, and pytest.
40
+
41
+ ## Change boundaries
42
+
43
+ Tailkitty owns the Python control plane:
44
+
45
+ - Address parsing, validation, and resolution.
46
+ - DNS TXT destination lookup.
47
+ - DERP-map caching.
48
+ - Typed sync and asyncio wrappers.
49
+ - Process lifecycle and diagnostics.
50
+ - Verified distribution of the pinned backend.
51
+
52
+ Upstream Tailcat owns magicsock, WireGuard, STUN, DERP transport, and gVisor netstack. Do not add a
53
+ Python-only transport that only interoperates with itself. If a behavior belongs in Tailcat,
54
+ consider an upstream contribution and pin the released result here.
55
+
56
+ The Python CLI intentionally owns only `parse`, `resolve`, `doctor`, `--version`, and top-level
57
+ help. New Python-native subcommands can shadow future Tailcat commands; adding one requires an
58
+ explicit compatibility decision.
59
+
60
+ ## Tests
61
+
62
+ Good tests are:
63
+
64
+ - Deterministic and independent of public DERP availability.
65
+ - Bounded by short, explicit timeouts when processes or networks are involved.
66
+ - Isolated from saved user keys, caches, package directories, and configured backends.
67
+ - Paired across sync and asyncio APIs when behavior should match.
68
+ - Written as regressions for the exact failure being repaired.
69
+
70
+ Public relays can be slow or unavailable. Default tests must not use them. The wheel smoke test uses
71
+ an isolated local DERP/STUN relay and a five-second handshake bound.
72
+
73
+ For Python compatibility changes, test the minimum supported runtime:
74
+
75
+ ```console
76
+ uv run --isolated --python 3.11 --all-groups pytest
77
+ ```
78
+
79
+ ## Documentation
80
+
81
+ Update documentation with the same change when public behavior changes:
82
+
83
+ - README for discovery, installation, quickstarts, and the public boundary.
84
+ - `docs/python-api.md` for Python parameters, results, exceptions, and lifecycle.
85
+ - `docs/recipes.md` for task-oriented CLI examples.
86
+ - `docs/troubleshooting.md` for new failure modes.
87
+ - SECURITY for authorization, secrets, and trust changes.
88
+ - BUILDING and RELEASING for packaging changes.
89
+ - CHANGELOG for user-visible changes.
90
+ - AGENTS when repository invariants or canonical workflows change.
91
+
92
+ Examples must use placeholders, never live addresses or private keys. Keep commands copyable and
93
+ state which machine runs each side of a multi-machine example.
94
+
95
+ ## Packaging changes
96
+
97
+ Changes to bundle building, wheel tags, targets, manifests, dependencies, or build hooks require:
98
+
99
+ ```console
100
+ mise run test
101
+ mise run bundle
102
+ mise run bundle-verify
103
+ mise run wheels
104
+ uv run python -m scripts.smoke_wheel dist/wheels/<host-wheel>.whl
105
+ ```
106
+
107
+ When running a build module directly, keep it inside mise so the exact Go pin is selected:
108
+
109
+ ```console
110
+ mise exec -- uv run python -m scripts.build_wheels --target windows-arm64
111
+ ```
112
+
113
+ Do not manually edit or commit generated content under `src/tailkitty/bin/`, `dist/`, `.tools/bin/`,
114
+ caches, or virtual environments.
115
+
116
+ ## Pull-request checklist
117
+
118
+ - [ ] The change stays inside the Python/native ownership boundary.
119
+ - [ ] Public APIs have complete annotations and root-package exports where appropriate.
120
+ - [ ] Tests cover success, validation failure, timeout, and cleanup where relevant.
121
+ - [ ] Sync and asyncio behavior remain aligned.
122
+ - [ ] Documentation and changelog describe the public result.
123
+ - [ ] No active address, private key, credential, or verbose network log is committed.
124
+ - [ ] `mise run test` passes.
125
+ - [ ] Packaging verification passes when packaging changed.
126
+ - [ ] Remaining external limitations are stated explicitly.
127
+
128
+ ## Reporting security issues
129
+
130
+ Do not open a public report containing secrets, live unrestricted addresses, private key files, or
131
+ raw verbose logs. Follow [SECURITY.md](SECURITY.md).
@@ -1,5 +1,10 @@
1
1
  # 100-iteration improvement ledger
2
2
 
3
+ > [!NOTE]
4
+ > This is the historical ledger for the initial Tailkitty implementation, not current product
5
+ > documentation. See `README.md`, `CHANGELOG.md`, and `docs/` for the current release. Version and
6
+ > platform statements below intentionally record what was true when each iteration was completed.
7
+
3
8
  Each checkbox is a distinct implementation, hardening, verification, or documentation pass. Items
4
9
  are marked only after their stated evidence exists.
5
10