skills/local/cali-ops-deploy-github-tailscale/SKILL.md
[Cali] Deploy to a private server via Tailscale OIDC with ephemeral credentials. Use when: setting up CI/CD deploy pipeline, configuring Tailscale SSH + OpenSSH for GitHub Actions, creating deploy users with restricted access, writing deploy.yml workflows, or troubleshooting deploy authentication failures. Triggers: "deploy to server", "tailscale deploy", "deploy pipeline", "CI/CD deploy", "deploy workflow", "deploy setup", "deploy key", "deploy user".
npx skillsauth add renatocaliari/agent-sync-public-skills cali-ops-deploy-github-tailscaleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Zero-trust deployment: ephemeral OIDC credentials, restricted SSH keys, WireGuard tunnel.
GITHUB RUNNER SERVER (100.120.175.47)
│ │
│ Tailscale OIDC (ephemeral) │
│ OpenSSH port 2222 (Tailscale) │
│ ed25519 key (restricted) │
│ command= limits commands │
└─────────────────────────────────┘
│
UFW: only tailscale0
Internet: blocked
Tailscale on Linux (since 1.32) runs an SSH server in userspace
on port 2222, separately from the local sshd on port 22.
This is the default. Don't try to also bind OpenSSH to 2222.
sshd): the system OpenSSH server. Authenticates
against ~/.ssh/authorized_keys on the server. If the key has
command=… it's restricted to that command. Used by the
treinamento-praticas-narrativas repo: a deploy-only key
with a hard-coded command="cd /opt/app && docker compose…".~/.ssh/authorized_keys
for the key part; OIDC/ACL just add a layer.For CI deploys, use port 2222 so the Tailscale identity
(ephemeral, OIDC-issued) authorizes the session, and put a
key with NO command= prefix in authorized_keys so you get
a real shell to run docker compose build && up etc.
curl -fsSL https://tailscale.com/install.sh | sh)"tagOwners": {
"tag:continuous-integration": ["autogroup:admin"]
}
{
"src": ["autogroup:member", "tag:continuous-integration"],
"dst": ["<SERVER_IP>"],
"ip": ["tcp:22", "tcp:2222"]
}
repo:{owner}/{repo}:ref:refs/heads/{branch}tag:continuous-integrationRun as root on the server:
# Tailscale SSH is enabled by default. Verify with:
tailscale set --ssh=true
# Tailscale runs SSH on port 2222 (userspace) — do NOT also
# bind OpenSSH to 2222. Local sshd stays on port 22.
# Discover Tailscale IP
TAILSCALE_IP=$(ip -4 addr show tailscale0 | grep -oP 'inet \K[\d.]+')
# UFW: only the Tailscale interface can reach SSH ports
ufw default deny incoming
ufw default allow outgoing
ufw allow in on tailscale0 to any port 22
ufw allow in on tailscale0 to any port 2222
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
# Create deploy user
adduser --disabled-password --gecos "" deploy
usermod -aG docker deploy
# SSH key for the deploy user — choose Pattern A or Pattern B:
#
# Pattern A: deploy-only (image is pushed to a registry, server just pulls)
# The key restricts the SSH session to a single command. The session
# can never get a real shell. Use this when you don't need to build
# on the server.
cat > /home/deploy/.ssh/authorized_keys << 'KEYEOF'
command="cd /opt/{project} && docker compose pull && docker rm -f {container} 2>/dev/null; docker compose up -d && docker image prune -f",no-agent-forwarding,no-port-forwarding,no-user-rc,no-X11-forwarding ssh-ed25519 AAA... github-actions-deploy-{project}
KEYEOF
#
# Pattern B: shell (image is built on the server)
# No command= prefix. The CI runner shells in, clones the repo,
# builds the Docker image, runs scripts/deploy-prod.sh. Tailscale
# ACL (the OIDC tag) is the only authorization layer beyond the key.
# cat > /home/deploy/.ssh/authorized_keys << 'KEYEOF'
# ssh-ed25519 AAA... github-actions-{project}
# KEYEOF
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
# Copy Docker registry auth to deploy user
cp /root/.docker/config.json /home/deploy/.docker/config.json
chown -R deploy:deploy /home/deploy/.docker
| Secret | Value |
|---|---|
| TS_OAUTH_CLIENT_ID | OIDC Client ID |
| TS_AUDIENCE | OIDC Audience |
| DEPLOY_SSH_KEY | Private ed25519 key |
See references/workflow-template.yml for the full template.
Key elements:
id-token: write permission for OIDCtailscale/github-action@v4 for ephemeral authcommand=The template in references/workflow-template.yml uses on: push: [main].
For release-driven deploys, change the trigger:
on:
release:
types: [published, prereleased]
This deploys on both stable and alpha/beta releases. Use [published] only
if you want to skip prereleases.
For repos whose CI runs a heavy matrix (multi-tag builds, -race, CGO),
run that exact gate locally before pushing so you never ship a broken
commit or wait on remote runners. gh-signoff
stamps a green commit status after your local tests pass:
gh extension install basecamp/gh-signoff
make ci-local # mirror CI locally (templ gen + lint + datastar-lint + css-check + tests + builds)
make signoff # ci-local + gh signoff -f
Advisory vs blocking. This deploy flow triggers on push to a branch
(not PR merge), so the signoff status is a signal, not a merge gate. Use
gh signoff -f (force) so it stamps even before push, and do not run
gh signoff install — that would require the status for PR merge and is
meaningless for a push-to-deploy flow. If the workflow moves to PRs, enable
gh signoff install to make signoff a merge requirement.
Linters run by make ci-local. The local gate runs golangci-lint
with the full project set (see cali-coding-go-standards → Recommended
linter set): dupl, goconst, revive, staticcheck (incl. the ST*
style checks), errcheck, ineffassign, govet, gocritic, gosec,
noctx, gocyclo, lll, funlen, mnd, tagliatelle, modernize,
nolintlint, plus gofumpt (formatter) and datastar-lint. Pin
golangci-lint v2.12.2+ — older v2.x lacks tagliatelle/modernize.
| Measure | What it protects |
|---|---|
| OIDC (ephemeral JWT) | No permanent credentials on runner |
| command= in authorized_keys (Pattern A) | Even with key, only docker compose allowed |
| Port 2222 on Tailscale IP only | Invisible on internet |
| UFW + tailscale0 interface | Only WireGuard traffic |
| deploy user (non-root) | Least privilege |
| Tailscale SSH (personal) | You access without keys, by identity |
| Ephemeral runner node | Disappears after workflow |
# On server (as root):
curl -fsSL https://tailscale.com/install.sh | sh
tailscale set --ssh=true
tailscale status # verify connection
# Create deploy user + restricted key
adduser --disabled-password --gecos "" deploy
usermod -aG docker deploy
- name: Authenticate with Tailscale
uses: tailscale/github-action@v4
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
audience: ${{ secrets.TS_AUDIENCE }}
tags: tag:continuous-integration
- name: Deploy
shell: bash
run: |
echo "$SSH_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
ssh -i /tmp/deploy_key -p 2222 [email protected] \
'cd /opt/app && docker compose pull && docker compose up -d'
shred -u /tmp/deploy_key
# Check deploy user
# Pattern A only (deploy-only key with command=):
ssh [email protected] # should NOT get shell (command= restricts)
ssh [email protected] whoami # should fail (command= only allows docker)
# Pattern B (shell key): both work normally
# Check UFW
sudo ufw status verbose # should show only tailscale0 ports
tailscale status on both endscp /root/.docker/config.json /home/deploy/.docker/config.jsonsystemctl status ssh.socketListenStream includes Tailscale IPsystemctl daemon-reload && systemctl restart ssh.socketreferences/workflow-template.yml — Full GitHub Actions workflowreferences/server-setup.sh — Server setup scriptreferences/security-checklist.md — Pre-deploy security verificationtools
Extrai métricas estruturadas, cálculos e estimativas de transcripts de entrevistas com clientes do Sommelier de IA. Produz um JSON com dores, frequências, tempo gasto, pessoas envolvidas, economia potencial, ROI e recomendações financeiras. Projetado para alimentar o cali-degustia-diagnostico ou integrar com dashboards/planilhas.
tools
Guia a coleta de depoimentos de clientes do Sommelier de IA no momento certo do processo, usando a abordagem de Hormozi: pedir depois da primeira evidência de resultado, nunca na entrega. Gera depoimentos mais autênticos e reduz a sensação de que o cliente está sendo "solicitado".
development
[stelow] Full UX critique for visual interfaces. Accepts a live URL, source code directory, or screenshot image. Evaluates accessibility (WCAG AA), Nielsen's 10 heuristics, visual hierarchy, cognitive load, consistency, mobile responsiveness, AI slop, emotional journey, and design personas — then generates a classified gap report. Standalone or integrated into stelow and stelow-product-testing-execution.
development
Building trust through perception and guarantee mechanisms. Covers ten pillars to materialize trust, guarantee types from unconditional to anti-guarantees, and strategic approaches for different contexts.