Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,33 @@ body:
validations:
required: true

- type: input
id: service
attributes:
label: ScaleTail Service
description: Which service directory is affected (for example, services/homebox)?
placeholder: services/<service-name>
validations:
required: true

- type: input
id: image
attributes:
label: Image and Tag
description: Which application and Tailscale image tags are running?
placeholder: ghcr.io/example/service:1.2.3; tailscale/tailscale:latest
validations:
required: true

- type: input
id: architecture
attributes:
label: Host Architecture
description: For example amd64, arm64, or armv7.
placeholder: amd64
validations:
required: true

- type: textarea
id: expected
attributes:
Expand All @@ -36,6 +63,18 @@ body:
validations:
required: true

- type: textarea
id: reproduction
attributes:
label: Reproduction Steps
description: Include the exact commands used, such as docker compose config, up, or logs.
placeholder: |
1. cd services/<service-name>
2. docker compose config --quiet
3. docker compose up -d
validations:
required: true

- type: textarea
id: screenshots
attributes:
Expand Down Expand Up @@ -76,6 +115,15 @@ body:
validations:
required: true

- type: input
id: compose-version
attributes:
label: Docker Compose Version
description: What Docker Compose version are you using? (Run `docker compose version`).
placeholder: 'v2.27.0'
validations:
required: true

- type: textarea
id: logs
attributes:
Expand All @@ -87,7 +135,7 @@ body:
To get Docker logs: docker logs <container-name>
render: shell
validations:
required: false
required: true

- type: textarea
id: compose
Expand All @@ -103,7 +151,7 @@ body:

render: yaml
validations:
required: false
required: true

- type: textarea
id: env
Expand All @@ -117,6 +165,7 @@ body:
- TS_KEY
- ENCRYPTION_KEY
- JWT_SECRET
- Passwords and API tokens

render: shell
validations:
Expand All @@ -130,4 +179,3 @@ body:
placeholder: Any other relevant information...
validations:
required: false

9 changes: 9 additions & 0 deletions .github/ISSUE_TEMPLATE/feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ body:
validations:
required: true

- type: textarea
id: compatibility
attributes:
label: Compatibility and Validation
description: Explain how this would affect existing services and how the change could be tested without breaking the sidecar contract.
placeholder: Existing services remain compatible because... I would validate this with...
validations:
required: true

- type: checkboxes
id: contribute
attributes:
Expand Down
43 changes: 42 additions & 1 deletion .github/ISSUE_TEMPLATE/new_service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,24 @@ body:
validations:
required: true

- type: input
id: service-name
attributes:
label: Proposed Service Name
description: The lowercase directory name to use under services/.
placeholder: service-name
validations:
required: true

- type: input
id: image
attributes:
label: Maintained Container Image
description: Provide the official registry image and a tag or release reference.
placeholder: ghcr.io/owner/service:1.2.3
validations:
required: true

- type: input
id: link-to-compose
attributes:
Expand Down Expand Up @@ -50,7 +68,30 @@ body:
description: Please provide a link to the official website of the service.
placeholder: https://servicename.com/service
validations:
required: false
required: true

- type: textarea
id: runtime-details
attributes:
label: Runtime Details
description: Document the internal HTTP/HTTPS/UDP ports, persistent paths, dependencies, required permissions/devices, and supported architectures.
placeholder: |
Internal port(s):
Persistent paths:
Dependencies:
Required capabilities/devices:
Architectures:
validations:
required: true

- type: textarea
id: official-docs
attributes:
label: Official Install and Compose Documentation
description: Link the upstream installation, Docker image, and configuration documentation used to verify the proposal.
placeholder: https://docs.example.com/self-hosting
validations:
required: true

- type: checkboxes
id: contribute
Expand Down
2 changes: 2 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- [ ] I have added verification that the stack works as expected.
- [ ] I have updated necessary documentation (e.g. frontpage [README.md](https://github.com/tailscale-dev/ScaleTail/blob/main/README.md) ).
- [ ] I have selected the correct label(s) for this PR.
- [ ] I preserved the template comments and ran `python tools/validate_services.py services/<changed-service>` plus `(cd services/<changed-service> && docker compose config --quiet)` for each changed service.
- [ ] For a new service, I checked official upstream documentation for the image, internal port, healthcheck, volumes, permissions, dependencies, and architecture support.

## Additional Context
<!-- Any extra info for reviewers, such as gotchas, special requirements, devices, or dependencies. Use "None" if not applicable. -->
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone this repo
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
persist-credentials: false

- name: Lint services
uses: rvben/rumdl@v0.2.41
uses: rvben/rumdl@86694902f916bedaa5e37f024cdc7858929092e6
with:
path: "services/"
config: ".markdownlint.yml"
Expand Down
130 changes: 130 additions & 0 deletions .github/workflows/service-contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Validate ScaleTail service contract

on:
workflow_dispatch:
push:
branches:
- main
paths:
- "services/**"
- "templates/service-template/**"
- "tools/**"
- "README.md"
- "CONTRIBUTING.md"
- ".github/PULL_REQUEST_TEMPLATE.md"
- ".github/ISSUE_TEMPLATE/**"
- ".github/workflows/service-contract.yml"
pull_request:
branches:
- main
paths:
- "services/**"
- "templates/service-template/**"
- "tools/**"
- "README.md"
- "CONTRIBUTING.md"
- ".github/PULL_REQUEST_TEMPLATE.md"
- ".github/ISSUE_TEMPLATE/**"
- ".github/workflows/service-contract.yml"

permissions:
contents: read

jobs:
service-contract:
name: Service contract
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Clone this repo
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
fetch-depth: 0
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.12"

- name: Install validator dependencies
run: >-
python -m pip install --disable-pip-version-check --no-input
--only-binary=:all: PyYAML==6.0.2

- name: Run validator tests
run: python -m unittest discover -s tools/tests -v

- name: Validate repository contract
run: python tools/validate_services.py --check-repository --format github

- name: Validate changed services
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
BEFORE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "pull_request" ]; then
python tools/validate_services.py --changed-from "$BASE_SHA" --format github
elif [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
python tools/validate_services.py --changed-from "$BEFORE_SHA" --format github
else
python tools/validate_services.py --all --baseline --format github
fi

- name: Run Compose config checks
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
BEFORE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "pull_request" ]; then
reference="$BASE_SHA"
elif [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
reference="$BEFORE_SHA"
else
reference=""
fi
if [ -n "$reference" ]; then
service_dirs=$(git diff --name-only "$reference" HEAD -- services/ | awk -F/ 'NF >= 2 {print $1 "/" $2}' | sort -u)
else
service_dirs=$(find services -mindepth 1 -maxdepth 1 -type d | sort)
fi
while IFS= read -r service_dir; do
[ -n "$service_dir" ] || continue
if [ -f "$service_dir/compose.yaml" ]; then
compose_file=compose.yaml
elif [ -f "$service_dir/compose.yml" ]; then
compose_file=compose.yml
else
continue
fi
if [ -L "$service_dir/$compose_file" ]; then
echo "Refusing symlinked Compose file: $service_dir/$compose_file" >&2
exit 1
fi
(cd "$service_dir" && timeout 90s docker compose -f "$compose_file" config --quiet)
done <<< "$service_dirs"

- name: Lint service documentation
uses: rvben/rumdl@86694902f916bedaa5e37f024cdc7858929092e6
with:
path: "services/"
config: ".markdownlint.yml"
report-type: annotations

- name: Lint template documentation
uses: rvben/rumdl@86694902f916bedaa5e37f024cdc7858929092e6
with:
path: "templates/service-template/"
config: ".markdownlint.yml"
report-type: annotations

- name: Lint root documentation
uses: rvben/rumdl@86694902f916bedaa5e37f024cdc7858929092e6
with:
path: "README.md"
config: ".markdownlint.yml"
report-type: annotations
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@ Temporary Items
.rumdl_cache/
**/.rumdl_cache/

# End of https://www.toptal.com/developers/gitignore/api/macos
# End of https://www.toptal.com/developers/gitignore/api/macos

# Python validator/test artifacts
__pycache__/
*.py[cod]
48 changes: 48 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,55 @@ Thanks for helping expand these Tailscale sidecar examples. Keeping services ali
- Link to upstream service docs and any official setup videos.
5. Sanity-check the stack with `docker compose config` from the service directory to catch typos and missing variables.

### Service contract and validation

The files in `templates/service-template` are the canonical structure for a
new service. Keep the explanatory comments in the Tailscale and application
blocks; add service-specific comments beside the template comments instead of
deleting them. New services must use `compose.yaml`, include a complete `.env`
template, and add a categorized link to the root `README.md`.

Run the repository validator before opening a pull request:

```console
python -m pip install -r tools/requirements.txt
python tools/validate_services.py services/<service-name> --new-service <service-name>
(cd services/<service-name> && docker compose config --quiet)
```

The `service-contract` GitHub check runs these deterministic checks for changed
services. It does not pull images or start third-party containers. Multi-container
layouts must be listed in `tools/service-profiles.yml` with an ingress service;
all non-default profiles need a maintainer-owned reason. Tailscale-node profiles
are restricted to the approved routing services.

The validator cannot prove that an upstream image's internal port, healthcheck,
volume path, UID/GID, or device requirements are correct. Verify those details
against the service's official documentation and record the links and gotchas in
the service README before requesting review.

The repository-wide baseline and remediation backlog are recorded in
[`documentation/service-contract-baseline.md`](documentation/service-contract-baseline.md).

## Updating an existing service

- Keep the sidecar pattern intact (`network_mode: service:tailscale`, health checks, `depends_on`).
- Avoid removing existing volumes or changing container names unless the change is clearly documented in the README.
- Preserve the template comments and run the validator for the service after any
Compose or `.env` change.

## Issue and pull request review

Use the personal `scaletail-maintainer` Codex skill for research-heavy reviews,
new-service validation, and issue triage. It reports findings by default and
only edits the local checkout when explicitly asked to fix something. It does
not push branches, post GitHub comments, resolve review threads, apply labels,
or close issues unless those actions are separately requested.

Issue triage uses the existing GitHub labels plus these small cross-cutting
labels when they are useful: `needs-info`, `template`, `service`, `upstream`,
`security`, and `blocked`. Start by checking for duplicates and whether the
form contains enough reproduction or upstream information. Runtime reports
such as sidecar healthcheck and database-DNS failures need evidence from the
service, image, Docker/Compose, and Tailscale layers; a formatting-only change
is not proof that they are resolved.
Loading
Loading