TailStick Documentation Drift Report
Repository: ~/nightshift-workspace/clones/tailstick
Analysis Date: 2026-04-17
Analyzer: nightshift doc-drift (automated)
Summary
| Severity |
Count |
| P0 Critical |
0 |
| P1 High |
4 |
| P2 Medium |
5 |
| P3 Low |
3 |
| Total |
12 |
Findings
DRIFT-001: Go version mismatch between go.mod, docs, and CI
- Severity: P1 High
- Files:
go.mod line 3: go 1.25.6
README.md line 29: Go 1.22+ is required for building from source
CONTRIBUTING.md line 7: Go 1.22+ is required
.github/workflows/ci.yml line 17: go-version: "1.22"
.github/workflows/release.yml line 36: go-version: "1.22"
.github/workflows/live-e2e.yml lines 32, 120: go-version: "1.25.6"
- Description:
go.mod declares go 1.25.6, but README and CONTRIBUTING say "Go 1.22+." The CI and release workflows use Go 1.22, while only the live-e2e workflow uses 1.25.6. A go build with Go 1.22 will fail against a go.mod requiring 1.25.6. The docs, CI, and go.mod are all inconsistent with each other.
- Recommendation: Decide on the minimum Go version. If 1.25.6 is required, update README, CONTRIBUTING, ci.yml, and release.yml to match. If 1.22 is the target, downgrade go.mod. Align all references.
DRIFT-002: Release artifact names and formats in docs don't match CI
- Severity: P1 High
- Files:
docs/release-runbook.md lines 49-52: lists tailstick-linux-amd64.tar.gz, tailstick-linux-arm64.tar.gz, tailstick-windows-amd64.tar.gz, tailstick-windows-arm64.tar.gz
.github/workflows/release.yml lines 41-49: builds tailstick-cli-{os}-{arch} and tailstick-gui-{os}-{arch} as raw binaries
- Description: The release runbook documents
.tar.gz archives (4 total), but release.yml produces 8 individual raw binaries named tailstick-cli-linux-amd64, tailstick-gui-linux-amd64, etc. The names and formats are completely different. Anyone following the runbook to verify releases will not find the documented artifacts.
- Recommendation: Update the release runbook to reflect the actual artifact naming scheme:
tailstick-cli-{os}-{arch} and tailstick-gui-{os}-{arch} (8 binaries total, no tar.gz). Alternatively, modify release.yml to produce the documented tar.gz archives.
DRIFT-003: docs/configuration.md example config link is a local absolute path
- Severity: P1 High
- Files:
docs/configuration.md line 5: [configs/tailstick.config.example.json](/home/ubuntu/workspace/tailscale-usb/configs/tailstick.config.example.json)
- Description: The example config link points to
/home/ubuntu/workspace/tailscale-usb/configs/... — a developer's local filesystem path from a different machine/project. This link will 404 for anyone else and is not a valid relative path in the repo.
- Recommendation: Change to a relative path:
[configs/tailstick.config.example.json](../configs/tailstick.config.example.json).
DRIFT-004: Go module path doesn't match GitHub repository URL
- Severity: P1 High
- Files:
go.mod line 1: module github.com/tailstick/tailstick
README.md lines 10-11: references github.com/Microck/tailstick
CONTRIBUTING.md line 55: references Microck/tailstick
.github/workflows/release.yml line 59: gh run list -R Microck/tailstick
- Description: The Go module path is
github.com/tailstick/tailstick, but the actual GitHub repository is github.com/Microck/tailstick. Running go build or go get would attempt to resolve the module from the wrong URL (github.com/tailstick/tailstick), which either doesn't exist or is a different repo.
- Recommendation: Update
go.mod to module github.com/Microck/tailstick and update all Go import paths accordingly, or ensure the github.com/tailstick/tailstick GitHub redirect/mirror exists and works.
DRIFT-005: Makefile build-all only builds amd64; README claims arm64 support
- Severity: P2 Medium
- Files:
README.md line 49: "release binaries are built for linux/amd64, linux/arm64, windows/amd64, and windows/arm64"
README.md lines 183-184: make build-all listed as way to cross-compile all targets
Makefile lines 21-25: build-all only builds GOARCH=amd64 for all 4 targets
- Description: README says
make build-all cross-compiles all targets including arm64, but the Makefile's build-all target only builds amd64 variants. The release.yml CI workflow does build arm64, but developers using make build-all locally won't get arm64 binaries.
- Recommendation: Either add arm64 builds to the Makefile
build-all target, or update README to clarify that make build-all only builds amd64 and that arm64 builds are done in CI.
DRIFT-006: Preset creator generates config fields not supported by the Go model
- Severity: P2 Medium
- Files:
.github/pages/index.html lines 464-478: generates defaultMode, defaultChannel, defaultDays fields in preset objects
assets/preset-creator.html lines 385-392: same fields
internal/model/types.go lines 39-53: Preset struct has no defaultMode, defaultChannel, or defaultDays fields
- Description: The preset creator web tool generates JSON with
defaultMode, defaultChannel, and defaultDays fields per preset, but the Go Preset struct doesn't define these fields. When the config is loaded, these fields are silently ignored by json.Unmarshal. Users who build configs with the preset creator will have non-functional fields in their JSON.
- Recommendation: Either add
DefaultMode, DefaultChannel, and DefaultDays to the model.Preset struct and implement the corresponding logic, or remove these fields from the preset creator output.
DRIFT-007: --audit default path is relative to config directory, not current directory
- Severity: P2 Medium
- Files:
README.md line 76: --audit | logs/tailstick-audit.ndjson
internal/app/workflow.go lines 56-58: rt.AuditPath = filepath.Join(filepath.Dir(rt.ConfigPath), "logs", "tailstick-audit.ndjson")
- Description: README documents the default audit path as
logs/tailstick-audit.ndjson, implying it's relative to the current working directory. The actual code makes it relative to the config file's directory. In USB deployment (config on the USB drive), this is a meaningful difference — the audit log goes on the USB drive, not in the CWD.
- Recommendation: Update README to clarify:
--audit default is logs/tailstick-audit.ndjson (relative to config file directory). This is consistent with docs/operations.md line 55 which says <config_dir>/logs/tailstick-audit.ndjson.
DRIFT-008: --non-interactive flag documented for run but not available in GUI mode
- Severity: P2 Medium
- Files:
README.md line 72: --non-interactive documented as a run flag
internal/app/gui.go: RunGUI does not accept or pass NonInteractive
internal/gui/server.go lines 33-43: enrollRequest has no nonInteractive field
- Description: The
--non-interactive flag is documented as a run flag and implemented in the CLI, but the GUI mode always sets NonInteractive: false. This is not documented. While it makes sense (GUI is inherently interactive), a user reading the docs might expect this flag to work in the GUI binary too, especially since the README lists shared flags for both modes.
- Recommendation: Add a note in the README GUI flags section clarifying that
--non-interactive is CLI-only.
DRIFT-009: --days description in README is misleading about valid values
- Severity: P2 Medium
- Files:
README.md line 67: --days described generically as "timed lease duration in days"
internal/tailscale/client.go lines 253-258: ParseDurationDays validates days must be in {1, 3, 7}
- Description: The README implies
--days accepts any number, but the code only accepts 1, 3, or 7. Users who try --days 5 will get an error: "timed lease requires days in {1,3,7} or custom-days in [1,30]". The README should document this constraint.
- Recommendation: Change the README
--days description to: "timed lease duration in days (allowed: 1, 3, or 7; use --custom-days for other values)".
DRIFT-010: README mentions tailstick.micr.dev preset maker URL; pages.yml deploys from .github/pages/
- Severity: P3 Low
- Files:
README.md line 35: "use the optional preset maker"
.github/workflows/pages.yml: deploys .github/pages/ directory as GitHub Pages
- Description: The README links to
tailstick.micr.dev for the preset maker, which is presumably a custom domain pointing to the GitHub Pages deployment. If the custom domain is not configured in the repo settings, this URL won't work. The internal reference is consistent (.github/pages/index.html is the preset creator), but the external URL depends on DNS configuration outside the repo.
- Recommendation: Verify the custom domain is configured in GitHub Pages settings. Consider adding a fallback link like
[preset maker](https://microck.github.io/tailstick/) or noting the URL may vary.
DRIFT-011: internal/platform/exec.go package doc comment says "Runner interface" but Runner is a concrete struct
- Severity: P3 Low
- Files:
internal/platform/exec.go line 3: doc comment says "It provides a Runner interface"
internal/platform/exec.go line 15: type Runner struct { ... }
- Description: The package-level doc comment in exec.go says it provides a "Runner interface," but
Runner is actually a concrete struct, not a Go interface. This is a minor inaccuracy in the code documentation.
- Recommendation: Change the doc comment from "It provides a Runner interface" to "It provides a Runner type."
DRIFT-012: CONTRIBUTING.md says go build ./... but Makefile uses targeted builds
- Severity: P3 Low
- Files:
CONTRIBUTING.md line 10: go build ./...
Makefile lines 14-18: build target builds specific ./cmd/* entrypoints
- Description: CONTRIBUTING.md suggests
go build ./... as the development build command. While this works, it differs from the Makefile's targeted build approach. More importantly, go build ./... builds all packages but only leaves binaries for main packages, which is fine but not the canonical approach documented in the Makefile and README.
- Recommendation: Consider aligning CONTRIBUTING.md with the README's build instructions or the Makefile's
build target for consistency.
Methodology
- Read all documentation files:
README.md, CONTRIBUTING.md, docs/architecture.md, docs/configuration.md, docs/operations.md, docs/testing.md, docs/release-runbook.md
- Read all Go source files:
go.mod, cmd/*/main.go, internal/app/cli.go, internal/app/gui.go, internal/app/workflow.go, internal/config/config.go, internal/model/types.go, internal/platform/platform.go, internal/platform/exec.go, internal/tailscale/client.go, internal/gui/server.go
- Read all CI/CD configs:
.github/workflows/ci.yml, .github/workflows/release.yml, .github/workflows/live-e2e.yml, .github/workflows/pages.yml
- Read supporting files:
Makefile, configs/tailstick.config.example.json, .env.example, .gitignore, .github/pages/index.html, assets/preset-creator.html
- Cross-referenced documented CLI flags against actual
flag.NewFlagSet definitions
- Cross-referenced documented config fields against
model.Config and model.Preset structs
- Cross-referenced CI workflow configurations against documented build/test procedures
- Cross-referenced release artifact naming between runbook and release.yml
TailStick Documentation Drift Report
Repository:
~/nightshift-workspace/clones/tailstickAnalysis Date: 2026-04-17
Analyzer: nightshift doc-drift (automated)
Summary
Findings
DRIFT-001: Go version mismatch between go.mod, docs, and CI
go.modline 3:go 1.25.6README.mdline 29:Go 1.22+ is required for building from sourceCONTRIBUTING.mdline 7:Go 1.22+ is required.github/workflows/ci.ymlline 17:go-version: "1.22".github/workflows/release.ymlline 36:go-version: "1.22".github/workflows/live-e2e.ymllines 32, 120:go-version: "1.25.6"go.moddeclaresgo 1.25.6, but README and CONTRIBUTING say "Go 1.22+." The CI and release workflows use Go 1.22, while only the live-e2e workflow uses 1.25.6. Ago buildwith Go 1.22 will fail against ago.modrequiring 1.25.6. The docs, CI, and go.mod are all inconsistent with each other.DRIFT-002: Release artifact names and formats in docs don't match CI
docs/release-runbook.mdlines 49-52: liststailstick-linux-amd64.tar.gz,tailstick-linux-arm64.tar.gz,tailstick-windows-amd64.tar.gz,tailstick-windows-arm64.tar.gz.github/workflows/release.ymllines 41-49: buildstailstick-cli-{os}-{arch}andtailstick-gui-{os}-{arch}as raw binaries.tar.gzarchives (4 total), but release.yml produces 8 individual raw binaries namedtailstick-cli-linux-amd64,tailstick-gui-linux-amd64, etc. The names and formats are completely different. Anyone following the runbook to verify releases will not find the documented artifacts.tailstick-cli-{os}-{arch}andtailstick-gui-{os}-{arch}(8 binaries total, no tar.gz). Alternatively, modify release.yml to produce the documented tar.gz archives.DRIFT-003: docs/configuration.md example config link is a local absolute path
docs/configuration.mdline 5:[configs/tailstick.config.example.json](/home/ubuntu/workspace/tailscale-usb/configs/tailstick.config.example.json)/home/ubuntu/workspace/tailscale-usb/configs/...— a developer's local filesystem path from a different machine/project. This link will 404 for anyone else and is not a valid relative path in the repo.[configs/tailstick.config.example.json](../configs/tailstick.config.example.json).DRIFT-004: Go module path doesn't match GitHub repository URL
go.modline 1:module github.com/tailstick/tailstickREADME.mdlines 10-11: referencesgithub.com/Microck/tailstickCONTRIBUTING.mdline 55: referencesMicrock/tailstick.github/workflows/release.ymlline 59:gh run list -R Microck/tailstickgithub.com/tailstick/tailstick, but the actual GitHub repository isgithub.com/Microck/tailstick. Runninggo buildorgo getwould attempt to resolve the module from the wrong URL (github.com/tailstick/tailstick), which either doesn't exist or is a different repo.go.modtomodule github.com/Microck/tailstickand update all Go import paths accordingly, or ensure thegithub.com/tailstick/tailstickGitHub redirect/mirror exists and works.DRIFT-005: Makefile
build-allonly builds amd64; README claims arm64 supportREADME.mdline 49: "release binaries are built forlinux/amd64,linux/arm64,windows/amd64, andwindows/arm64"README.mdlines 183-184:make build-alllisted as way to cross-compile all targetsMakefilelines 21-25:build-allonly buildsGOARCH=amd64for all 4 targetsmake build-allcross-compiles all targets including arm64, but the Makefile'sbuild-alltarget only builds amd64 variants. The release.yml CI workflow does build arm64, but developers usingmake build-alllocally won't get arm64 binaries.build-alltarget, or update README to clarify thatmake build-allonly builds amd64 and that arm64 builds are done in CI.DRIFT-006: Preset creator generates config fields not supported by the Go model
.github/pages/index.htmllines 464-478: generatesdefaultMode,defaultChannel,defaultDaysfields in preset objectsassets/preset-creator.htmllines 385-392: same fieldsinternal/model/types.golines 39-53:Presetstruct has nodefaultMode,defaultChannel, ordefaultDaysfieldsdefaultMode,defaultChannel, anddefaultDaysfields per preset, but the GoPresetstruct doesn't define these fields. When the config is loaded, these fields are silently ignored byjson.Unmarshal. Users who build configs with the preset creator will have non-functional fields in their JSON.DefaultMode,DefaultChannel, andDefaultDaysto themodel.Presetstruct and implement the corresponding logic, or remove these fields from the preset creator output.DRIFT-007:
--auditdefault path is relative to config directory, not current directoryREADME.mdline 76:--audit|logs/tailstick-audit.ndjsoninternal/app/workflow.golines 56-58:rt.AuditPath = filepath.Join(filepath.Dir(rt.ConfigPath), "logs", "tailstick-audit.ndjson")logs/tailstick-audit.ndjson, implying it's relative to the current working directory. The actual code makes it relative to the config file's directory. In USB deployment (config on the USB drive), this is a meaningful difference — the audit log goes on the USB drive, not in the CWD.--auditdefault islogs/tailstick-audit.ndjson(relative to config file directory). This is consistent withdocs/operations.mdline 55 which says<config_dir>/logs/tailstick-audit.ndjson.DRIFT-008:
--non-interactiveflag documented forrunbut not available in GUI modeREADME.mdline 72:--non-interactivedocumented as arunflaginternal/app/gui.go:RunGUIdoes not accept or passNonInteractiveinternal/gui/server.golines 33-43:enrollRequesthas nononInteractivefield--non-interactiveflag is documented as a run flag and implemented in the CLI, but the GUI mode always setsNonInteractive: false. This is not documented. While it makes sense (GUI is inherently interactive), a user reading the docs might expect this flag to work in the GUI binary too, especially since the README lists shared flags for both modes.--non-interactiveis CLI-only.DRIFT-009:
--daysdescription in README is misleading about valid valuesREADME.mdline 67:--daysdescribed generically as "timed lease duration in days"internal/tailscale/client.golines 253-258:ParseDurationDaysvalidates days must be in{1, 3, 7}--daysaccepts any number, but the code only accepts 1, 3, or 7. Users who try--days 5will get an error:"timed lease requires days in {1,3,7} or custom-days in [1,30]". The README should document this constraint.--daysdescription to:"timed lease duration in days (allowed: 1, 3, or 7; use --custom-days for other values)".DRIFT-010: README mentions
tailstick.micr.devpreset maker URL; pages.yml deploys from.github/pages/README.mdline 35: "use the optional preset maker".github/workflows/pages.yml: deploys.github/pages/directory as GitHub Pagestailstick.micr.devfor the preset maker, which is presumably a custom domain pointing to the GitHub Pages deployment. If the custom domain is not configured in the repo settings, this URL won't work. The internal reference is consistent (.github/pages/index.htmlis the preset creator), but the external URL depends on DNS configuration outside the repo.[preset maker](https://microck.github.io/tailstick/)or noting the URL may vary.DRIFT-011:
internal/platform/exec.gopackage doc comment says "Runner interface" but Runner is a concrete structinternal/platform/exec.goline 3: doc comment says "It provides a Runner interface"internal/platform/exec.goline 15:type Runner struct { ... }Runneris actually a concrete struct, not a Go interface. This is a minor inaccuracy in the code documentation.DRIFT-012: CONTRIBUTING.md says
go build ./...but Makefile uses targeted buildsCONTRIBUTING.mdline 10:go build ./...Makefilelines 14-18:buildtarget builds specific./cmd/*entrypointsgo build ./...as the development build command. While this works, it differs from the Makefile's targeted build approach. More importantly,go build ./...builds all packages but only leaves binaries formainpackages, which is fine but not the canonical approach documented in the Makefile and README.buildtarget for consistency.Methodology
README.md,CONTRIBUTING.md,docs/architecture.md,docs/configuration.md,docs/operations.md,docs/testing.md,docs/release-runbook.mdgo.mod,cmd/*/main.go,internal/app/cli.go,internal/app/gui.go,internal/app/workflow.go,internal/config/config.go,internal/model/types.go,internal/platform/platform.go,internal/platform/exec.go,internal/tailscale/client.go,internal/gui/server.go.github/workflows/ci.yml,.github/workflows/release.yml,.github/workflows/live-e2e.yml,.github/workflows/pages.ymlMakefile,configs/tailstick.config.example.json,.env.example,.gitignore,.github/pages/index.html,assets/preset-creator.htmlflag.NewFlagSetdefinitionsmodel.Configandmodel.Presetstructs