local/skills/local-deploy/SKILL.md
MUST be invoked before any work involving: `target: local` deployments, the Ansible-style `host:` destination field (literal `local` for direct shell, anything else routes through ssh(1) reading `~/.ssh/config` + ssh-agent), the `local:` template reference, the `user:` and `ssh_args:` Ansible-shaped overrides, the managed `~/.config/charly/ssh_config` fragment, the install ledger at `~/.config/opencharly/installed/`, ReverseOp teardown, or the `--with-services`/`--allow-repo-changes`/`--allow-root-tasks` gates.
npx skillsauth add overthinkos/overthink-plugins local-deployInstall 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.
target: local deployments apply a box or candy's install recipe directly to a Linux filesystem instead of baking it into a container image. The destination is named by the deployment's host: field (Ansible-style):
host: local (literal) or absent → ShellExecutor (run on this machine).SSHExecutor (ssh(1) reads ~/.ssh/config + ssh-agent for keys, host-key checking, options).The same InstallPlan IR that drives charly box build (via OCITarget) and container deploys (via PodDeployTarget) is consumed by LocalDeployTarget, which translates each IR step into shell commands, podman run <builder> invocations for compile-needing work, and systemd unit writes.
The deploy applies host packages + configs ONLY. Container images required for charly eval run / charly eval live are ensured by the eval preflight (see /charly-eval:eval "Image preflight"), not by the deploy. Deploys (any target) emit zero image-pull / image-build steps — that's the CLAUDE.md "Deploy fetches NOTHING speculative" Key Rule, codified at the type level. Migration of legacy image: blocks: charly migrate (idempotent).
Use cases:
charly contains zero custom SSH-key resolution. We do not read ~/.ssh/config, we do not detect ssh-agent, we do not prompt for keys. ssh(1) does it all. Configure your destinations via ~/.ssh/config Host stanzas, load keys into ssh-agent, and charly shells out to ssh with no -i / -o StrictHostKeyChecking= / -o UserKnownHostsFile= overrides.
For VM destinations, charly vm create <name> writes a managed Host stanza into ~/.config/charly/ssh_config (one per VM, fenced with # opencharly:begin markers) and ensures your ~/.ssh/config has Include ~/.config/charly/ssh_config (also managed). After that, ssh charly-<vmname> works from any terminal — and LocalDeployTarget constructs &SSHExecutor{Host: "charly-<vmname>"} with no User/Port/Key.
| Action | Command |
|---|---|
| Direct local | charly deploy add my-laptop (host: local is the default) |
| SSH to remote | charly deploy add ci-3 with host: [email protected] in charly.yml |
| Reference a template | local: dev-workstation on the deployment |
| Tear down | charly deploy del <name> |
| Tear down, keep repo changes | charly deploy del <name> --keep-repo-changes |
host: destination semanticsReserved literal: local. Anything else (including localhost, 127.0.0.1) goes through SSH.
deploy:
# Direct local — host: omitted == "local".
my-laptop:
target: local
local: dev-workstation
add_candy: [sshkeys]
# Explicit local sentinel.
my-laptop-explicit:
target: local
local: dev-workstation
host: local
# SSH to remote machine (ssh-config + agent supply credentials).
ci-runner-3:
target: local
local: ci-runner
host: [email protected]
# SSH with explicit port.
bastion:
target: local
local: dev-workstation
host: [email protected]:2222
# SSH to loopback for testing the SSH path.
ssh-self-test:
target: local
local: dev-workstation
host: localhost
user: and ssh_args: — Ansible-style overridesTwo pass-through fields mirror Ansible's per-host overrides:
# Explicit user override (Ansible's ansible_user). Used when host: has
# no "@" prefix. Cleaner than embedding the user in host: when the
# destination is an ssh-config alias.
workshop-laptop:
target: local
local: dev-workstation
host: workshop-laptop.lan
user: alice
# ssh_args: passes options through to ssh(1) (Ansible's
# ansible_ssh_extra_args). Use sparingly — ssh-config Host stanzas are
# the right home for persistent options.
via-bastion:
target: local
local: dev-workstation
host: target.internal
user: ops
ssh_args:
- "-o"
- "[email protected]"
Precedence rule for user: vs the inline host: <user>@<machine> form: the inline user wins (more-specific beats more-general). When both are set with different values, the validator emits an error — pick one. When both are absent, ssh(1) reads the User directive from ~/.ssh/config or falls back to $USER.
Include lineThe first charly vm create writes:
~/.config/charly/ssh_config (managed block, fenced).Include ~/.config/charly/ssh_config line into your ~/.ssh/config (also fenced).If you prefer to manage your ~/.ssh/config manually, add the Include line yourself before creating any VMs. charly vm destroy removes the matching stanza and, when the fragment is empty, removes the Include line too.
Remote target: local deploys assume passwordless sudo on the destination. We do not bridge interactive sudo prompts through the SSH session. Either: (a) configure NOPASSWD for your user on the remote, or (b) restrict the deploy to layers that don't require root (omit --with-services, omit packages that need sudo dnf).
| Flag | Guards |
|---|---|
| --with-services | systemd unit writes; systemctl enable --now |
| --allow-repo-changes | /etc/yum.repos.d/, /etc/apt/, /etc/pacman.conf mutations |
| --allow-root-tasks | arbitrary cmd: user: root task bodies (opaque shell) |
| --skip-incompatible | skip candies without a destination-matching format section |
| --builder-image <ref> | override the compile builder image |
| --yes / -y | implies all three gates + skips sudo preflight |
charly box validate checks every target: local deployment:
local: <name> references a kind: local template that exists.host: field, when non-local, parses via ParseSSHTarget.user: and ssh_args: only meaningful when host: is non-local (otherwise error).user: redundancy: when host: <inline>@<machine> and user: are both set with different values, error./charly-local:local-spec — author-facing reference for kind: local templates./charly-internals:local-infra — Go file map for the executor/ledger surface./charly-core:deploy — parent command family./charly-image:layer — service: schema rendered as systemd units on local-target deploys./charly-vm:vm — managed ssh-config fragment writen on charly vm create./charly-eval:eval — --verify re-runs candy eval: against the deploy post-install./charly-internals:install-plan — shared IR consumed by LocalDeployTarget.MUST be invoked when the task involves target: local, the Ansible-style host: field, the user:/ssh_args: overrides, the managed ssh-config fragment, the install ledger, or ReverseOp teardown. Invoke this skill BEFORE reading Go source or launching Explore agents.
tools
OpenCharly CLI (charly) binary installed into container/VM images for in-container use. Use when working with charly binary deployment inside containers, native D-Bus support, or the full charly toolchain (charly binary + virtualization + gocryptfs + socat).
development
Operator CachyOS workstation profile — a kind:local template + target:local deploy that installs the full dev stack (30 candies) onto a CachyOS host via ShellExecutor. Lives in the overthinkos/cachyos submodule. MUST be invoked before editing or applying the charly-cachyos workstation profile.
tools
Fedora box with the full charly toolchain using shared candies. Rootless-first — runs as uid=1000 with passwordless sudo (no root, no cap_add: ALL). Same candy list as charly-arch. Includes NVIDIA GPU runtime. MUST be invoked before building, deploying, configuring, or troubleshooting the charly-fedora box.
tools
Arch Linux box with the full charly toolchain. Rootless-first — runs as uid=1000 with passwordless sudo (no root, no cap_add: ALL). Composes /charly-coder:charly-mcp so the box is reachable as an MCP gateway on port 18765. NVIDIA GPU runtime composed in. MUST be invoked before building, deploying, configuring, or troubleshooting the charly-arch box.