Install
$ agentstack add skill-evolplus-talos-local-deployment ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Local Deployment
When to use
You are DevOps, dispatched against a task in status ready-for-deploy. Your job is to bring the local environment up — typically FE + BE + datastores composed via Docker — so two consumers can work against it:
- QA-Exec runs the test runner against
base_url/api_base_urlper the kit's QA-Exec Run Contract. - The operator (PM, designer, eng lead) opens the running app in a browser to manually trial the feature before sign-off — confirming the feature looks and behaves right against intent, not just against test assertions.
The skill covers Docker-based composition, port discovery (probe-then-bind, never hardcode), and deploy-report population. For non-Docker stacks (rare in modern projects), consult docs/architecture.md for the project's documented run mode.
Project-scoped container discipline (safety rule)
The kit dispatches DevOps inside an operator's environment that almost always has unrelated containers running — the operator's personal Postgres, another project's Redis, a sibling repo's docker-compose stack. A careless docker compose down -v would nuke them all. DevOps's blast radius is scoped to the current project's containers only.
Project slug — single source of truth
Determine the project slug (the Compose project name) once at deploy start; thread it through every subsequent docker command. Lookup priority:
COMPOSE_PROJECT_NAMEenvironment variable if set (most explicit; respects the operator's intent).- SRS header's project name field if Phase 0 read SRS successfully — sanitize:
lowercase(name).replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''). Example:"Stats Overflow"→stats-overflow. - Working-directory basename as a fallback when neither of the above is available. Same sanitization.
Record the chosen slug at the top of the deploy report (## Test Environment block adds project_slug: field) so QA-Exec and the operator can verify.
Every command takes the slug
Apply the slug to every docker invocation:
slug="stats-overflow" # from lookup above
# Compose: pass -p explicitly
docker compose -p "$slug" -f docker-compose.yml -f docker-compose.override.yml up -d --wait
docker compose -p "$slug" down # tear-down
docker compose -p "$slug" logs --tail=200 api # inspection
# Plain docker ps: filter by Compose-project label
docker ps --filter "label=com.docker.compose.project=$slug"
docker ps --filter "name=^${slug}-" # if not using Compose labels
# Container operations: name MUST start with the slug
container_name="${slug}-api-1"
docker inspect "$container_name"
docker logs "$container_name"
Out-of-scope reads — explicitly allowed (for conflict detection only)
Conflict detection legitimately requires reading what ELSE is running on the host. The following READ operations are permitted on out-of-scope containers / system state:
| Operation | Why allowed | |---|---| | docker ps (no filter) | Conflict detection — what ports are in use, what's running | | docker ps -a | Same — including stopped containers | | docker inspect | Read-only inspection for conflict resolution | | docker logs | Read-only diagnostics if a port owner is identified | | docker port | Read which ports a container is publishing | | docker stats --no-stream | Resource visibility | | docker version / docker info | System info | | docker network ls / docker network inspect | Network state visibility (read-only) | | docker volume ls / docker volume inspect | Volume state visibility (read-only) |
These are HOW DevOps probes ports + detects conflicts. They never mutate state.
Out-of-scope mutations — explicitly forbidden
DevOps MUST NOT run any of the following:
| Operation | Why forbidden | |---|---| | docker stop where ` doesn't start with | Could stop the operator's personal services | | docker rm where doesn't start with | Same | | docker kill where doesn't start with | Same | | docker restart where doesn't start with | Same | | docker pause / unpause outside slug | Same | | docker compose -p ANY mutation | Operates on another project entirely | | docker compose down without -p | Defaults to cwd basename → might match wrong project if cwd is non-standard | | docker system prune | Nukes ALL unused containers / networks / volumes across all projects | | docker volume prune | Nukes unused volumes globally | | docker network prune | Nukes unused networks globally | | docker container prune | Nukes all stopped containers globally | | docker image prune | Nukes unused images globally — affects other projects' rebuild times | | docker rm -f $(docker ps -q) (and variants with $(docker ps ...)) | Force-removes ALL running containers | | docker stop $(docker ps -q) | Stops ALL running containers | | docker volume rm outside slug | Could destroy operator's data | | docker network rm outside slug | Could break other projects' networking | | docker image rm ` | Affects every project that uses the image |
The docker-scope-guard.cjs hook enforces these at runtime — DevOps will be refused before the command runs.
Conflict resolution — non-mutating recourse
When an out-of-scope container holds a port the kit project needs:
- Probe and report — DevOps notes "port 5432 occupied by
mysite-postgres-1(not in scope)" in the deploy report. - Pick a different port — port-probe algorithm finds a free one (see Step 4 above).
- Surface to the operator if no port is available — write the conflict into
docs/open-issues.mdwith categorylocal-port-exhaustion, halt the dispatch, return NEEDS_CONTEXT asking the operator to manually free a port (their decision; they own those containers).
NEVER auto-resolve by stopping the other container.
Inputs and outputs
- Inputs: the project's existing
docker-compose.yml(or equivalent); project env templates and composeenv_file:references;docs/architecture.mdC2 Containers section (to know what services to expect); the task ID; SRS §3.4 Technical Constraints if it pins ports. - Outputs: a running local environment (containers up + health-checked);
docs/deploy-reports/.mdwith the standard Test Environment block + a new## Human Trial URLssection both QA and the operator consume.
Procedure
Step 1 — Verify Docker
Run two checks:
docker info > /dev/null 2>&1 || { echo "docker daemon not running"; exit 1; }
docker compose version > /dev/null 2>&1 || { echo "docker compose v2 not available"; exit 1; }
If either fails, halt with NEEDS_CONTEXT:
Status: NEEDS_CONTEXT
Reason: Docker prerequisite missing.
Question: docker daemon not running OR docker compose v2 not installed. The local-deployment skill requires both.
Suggested resolution: Start Docker Desktop / colima / podman; verify with `docker info` then `docker compose version`.
Do NOT attempt to install Docker yourself — that's an operator decision.
Step 1.5 — Detect host architecture + compute target platform
Docker images are architecture-specific. The kit's most common failure mode here is silently shipping amd64 images on Apple Silicon (arm64 Darwin) — they technically run via Rosetta emulation but at 30–60% performance penalty, occasional binary incompatibilities (e.g., native node-gyp modules), slow startup, and confusing error messages. Detect host arch BEFORE bringing the env up; match images deliberately.
host_os=$(uname -s) # Darwin / Linux
host_arch=$(uname -m) # x86_64 / arm64 / aarch64
# Normalize to Docker platform notation
case "$host_arch" in
x86_64|amd64) target_platform="linux/amd64" ;;
arm64|aarch64) target_platform="linux/arm64" ;;
*) echo "Unknown arch: $host_arch"; exit 1 ;;
esac
echo "Host: $host_os $host_arch → target_platform=$target_platform"
Host → target-platform mapping:
| Host | uname -m | targetplatform | Notes | |---|---|---|---| | Apple Silicon Mac (M1/M2/M3/M4) | arm64 | linux/arm64 | Native; preferred | | Intel Mac | x86_64 | linux/amd64 | Native | | Linux x8664 (most servers / cloud VMs) | x86_64 | linux/amd64 | Native | | Linux ARM64 (AWS Graviton, Ampere, RPi) | aarch64 | linux/arm64 | Native | | Windows (WSL2) | per WSL distro | per arch | Same rules apply inside WSL |
Inspect compose file for explicit platform pins. Many projects pin services to linux/amd64 for historical reasons (image vendor only published amd64, team standardized on Intel laptops, etc.). Walk the compose file's services.*.platform field:
# Pseudo: extract platform pins
yq '.services[] | select(.platform != null) | .platform' docker-compose.yml
For each pinned service, classify against the host:
| Compose platform | Host target | Action | |---|---|---| | Not pinned | any | Pass --platform=$target_platform to docker compose; Docker selects native image if available, falls back to multi-arch manifest | | linux/amd64 | linux/amd64 host | Native; proceed | | linux/amd64 | linux/arm64 host (Apple Silicon!) | Emulation warning — image runs via Rosetta/QEMU at ~50% performance. Flag in deploy report; consider asking SA for a multi-arch base image migration. | | linux/arm64 | linux/arm64 host | Native; proceed | | linux/arm64 | linux/amd64 host | Emulation warning (rare; usually means compose was authored on Apple Silicon and never re-tested on Intel CI) | | Multi-arch image (no platform pin, image manifest lists both) | any | Docker picks native automatically; preferred |
Apply the platform to every relevant command. Pass --platform=$target_platform to:
docker compose -p up -d --wait→ if compose-fileplatform:fields aren't explicit, this overrides; if they ARE explicit, this gets ignored per-service (compose-file wins).docker build --platform=$target_platform -t .(andbuildxinvocations) — when building from local Dockerfiles.docker run --platform=$target_platform— when running standalone containers.
For projects shipping amd64-only images to mixed-arch teams: the right answer is a multi-arch base image (build with docker buildx build --platform=linux/amd64,linux/arm64). Surface this as an open-issue with category multi-arch-base-needed and let SA pick up the migration.
Step 2 — Discover the project's compose definition
Look for, in order:
docker-compose.ymlat project root (most common).compose.yml(Compose v2 convention).infra/docker-compose.yml,deploy/docker-compose.yml(per kit's "project-owned reusable infra" pattern).- A multi-file split:
docker-compose.yml+docker-compose.local.yml(project'slocaloverlay).
If NONE exists, halt:
Status: NEEDS_CONTEXT
Reason: No compose file found at project root or under infra/ / deploy/.
Question: The kit cannot author a compose file inline (per the DevOps template's "project-owned reusable infra" rule). Either:
[a] The project doesn't have local-deploy machinery yet — needs an architecture decision (escalate to SA / TL via open-issue with category `infra-decision-pending`).
[b] The compose file lives elsewhere — name the path.
Step 2.5 — Discover environment files and validate env readiness
Do this before port probing or docker compose up. Many local deploy failures are really env-loading failures: Compose was run from the wrong directory, the root .env was not loaded, a service-level env_file: was missing, or QA ran with defaults that differ from the operator's intended local setup.
Classify env files:
- Operator-owned secret env files:
.env,.env.local,.env.development,.env.test,.env., and any file named by composeenv_file:unless it is an allowlisted template. Detect existence and usage, but never read or print values. - Safe templates:
.env.example,.env.template,.env.sample, including service-specific variants. These may be read to collect required key names and comments.
Procedure:
- Inspect the selected compose files and project run docs for:
env_file:entries per service;${VAR}/${VAR:-default}/${VAR:?required}interpolation placeholders;- documented local env-file order, for example
.envthen.env.local.
- Read safe templates only. Extract key names, defaults, and comments; do not treat template placeholder values as deploy secrets.
- Detect whether operator-owned env files exist and whether every compose-referenced
env_file:path exists. Do not copy env files into the worktree and do not create missing files. - Build a
compose_env_argslist for every later Compose command:
- run from the project root or pass
--project-directoryso root.envparticipates in interpolation; - include explicit
--env-fileonly when the project docs/scripts/compose setup declare that file order; - preserve service-level
env_file:entries in compose instead of copying them into the generated override.
- Validate the selected compose/env files before port probing:
``bash # Use the same -p, -f, --project-directory, and --env-file arguments that the final deploy will use, # but do not include the generated override yet because Step 5 has not created it. docker compose --project-directory "$project_root" "${compose_env_args[@]}" -p "$slug" -f docker-compose.yml config --quiet ``
Use config --quiet, not full docker compose config, because full config output can print resolved secret values.
- If validation fails because a required env var or env file is missing, halt with
NEEDS_CONTEXT. Ask the operator to create/update the project-local env file manually. Do not write.envyourself. - If privacy hooks block key-only inspection of operator-owned env files, record
key_status: not inspected (privacy guard)in the deploy report. Do not bypass privacy just to count keys.
The deploy report must include an env summary with file names and statuses only:
### Env File Awareness
| Item | Status | Notes |
|---|---|---|
| `.env` | present | operator-owned; values not read or printed |
| `.env.local` | absent | not declared by project docs |
| `api/.env` via compose `env_file` | present | service-level env file |
| `.env.example` | read | 18 documented keys |
- compose_config_quiet: pass
- missing_required_env: none
- secret_values_redacted: true
Step 3 — Determine preferred port ranges
Consult, in priority order:
- SRS §3.4 Technical Constraints if it names port preferences for the project.
.claude/skills/solution-defaults/references/defaults-table.mdif it lists project port defaults.- The compose file's published ports (
ports:lists; treat as preferences, not mandates — they may conflict on the host). - Kit defaults (table below) when none of the above apply.
| Service type | Preferred range | Container-internal default | |---|---|---| | Web FE (Next.js / Vite / CRA) | 3000–3010 | 3000 | | Backend HTTP API | 4000–4010 OR 8080–8090 | varies | | Admin / internal | 4100–4110 | varies | | WebSocket gateway | 4200–4210 | varies | | Postgres | 5432–5442 | 5432 | | MySQL | 3306–3316 | 3306 | | Redis | 6379–6389 | 6379 | | Elasticsearch | 9200–9210 | 9200 | | Kafka broker | 9092–9102 | 9092 | | RabbitMQ | 5672–5682 | 5672 | | MinIO / S3-compat | 9000–9010 + 9100–9110 (console) | 9000 / 9001 |
The preferred range gives predictability for the operator (no random ports each run) while leaving headroom when the defaults are occupied.
Step 4 — Probe ports
For each service, walk its preferred range and find the first host port not in LISTEN state:
probe_port() {
local start=$1 end=$2
for port in $(seq "$start" "$end"); do
if ! lsof -iTCP:"$port" -sTCP:LISTEN -t > /dev/null 2>&1; then
echo "$port"; return 0
fi
done
retur
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [evolplus](https://github.com/evolplus)
- **Source:** [evolplus/talos](https://github.com/evolplus/talos)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.