--- name: openvpn3-on-demand-setup description: "Configure openvpn3-on-demand for this project — pick BYO vs ephemeral mode, write .claude/openvpn3-on-demand.local.md, and add it to .gitignore. Read-only against the host; runs nothing privileged." --- Resolve `../..` from this SKILL.md's directory to the absolute plugin root. Substitute that path for `` in the instructions and commands below; do not assume a plugin-root shell variable is set. Keep the working directory at the consuming project root. In Codex, select `$openvpn3-on-demand:openvpn3-on-demand-setup` or `$openvpn3-on-demand:openvpn3-on-demand-doctor`. In Claude Code, the equivalent commands are `/openvpn3-on-demand:setup` and `/openvpn3-on-demand:doctor`. You are running `openvpn3-on-demand-setup`, an interactive configurator. Write **only** `.claude/openvpn3-on-demand.local.md` and, if needed, a `.gitignore` line. **Never** run anything privileged, import an openvpn3 config, or call a `vpn_*` MCP tool. First read `/setup-checklist.md` (checks 1–9 and their remediation text); use that text verbatim on failures. ## Flow ### 1. Host prerequisites Run checks 1–4. If any fails, print its remediation and **stop**; tell the user to re-run `openvpn3-on-demand-setup` after fixing. Check 4 (netcfg) is instruct-only: show the `sudo` lines, don't run them. Then run check 9 without a hostname — exactly `python3 "/servers/openvpn3/src/openvpn3_mcp/dnscheck.py"`. On WARN or FAIL print its output but **don't stop**; its root-only fix goes in the summary. ### 2. Existing settings file? Run check 5. If the file exists, Read it, show its frontmatter, and ask the user: *Keep as-is* / *Reconfigure (overwrite)* / *Abort*. Stop on Keep or Abort. ### 3. Pick the mode Ask the user — "Which mode should this project use?": - **BYO** — "You import and maintain an openvpn3 config; the plugin starts/stops its sessions and writes its connect overrides (`dns-scope=tunnel` plus any `config_overrides`) into it, where they persist." - **Ephemeral** — "A command prints the `.ovpn` body on stdout; the plugin makes a fresh single-use config from it each VPN-gated turn. Nothing to import." ### 4. Mode-specific question **BYO** → Ask for `profile_name` (the `--name` for `openvpn3 config-import`). Run check 7; if the name isn't listed, print check 7's remediation verbatim, noting it applies *after* this command writes the settings file. Don't import it yourself. **Ephemeral** → Ask for `ovpn_provision_cmd`, showing these shapes: - `vault read -field=config secret/vpn/my-prod` - `aws s3 cp s3://my-bucket/vpn/my-prod.ovpn -` - `cat ~/.config/openvpn3/my-prod-vpn.ovpn` - `make get_vpn_client_config OUTPUT=/dev/stdout` Stress: stdout must be the `.ovpn` body — not a path or status line. Keep it task-agnostic; hard-coded per-task values lock the file to one task. The assistant adds them at call time from the project's AGENTS.md / CLAUDE.md — as arguments where the command takes them (`make … ENV=dev`), else as a leading assignment (`AWS_PROFILE=… vault read …`), never a leading `ENV=` (Claude Code refuses it in worktree-isolated sessions). ### 5. Optional fields Ask which optional fields to configure (default none): - `post_connect_cmd` — shell command run after a fresh `vpn_connect`. Non-fatal. - `post_disconnect_cmd` — shell command run after a fresh `vpn_disconnect` (not on `not_connected`). Non-fatal. - `config_overrides` — openvpn3 `config-manage` overrides set before each tunnel start (hyphenated: `dns-scope`, `persist-tun`, `log-level`, …) on top of the `dns-scope=tunnel` baseline; override only for fully tunnel-routed DNS or other tweaks. In BYO mode, tell the user the baseline and these values persist in their profile (also for their own `openvpn3 session-start`) and that removing a key needs `openvpn3 config-manage --config --unset-override `. If chosen, ask for the map. ### 6. Write `.claude/openvpn3-on-demand.local.md` Create the directory if needed. Use this template: the chosen mode's field uncommented, the other's commented out, each chosen optional uncommented with its value: ```markdown --- # Generated by openvpn3-on-demand-setup. Edits take effect immediately — no restart. # Exactly one of profile_name / ovpn_provision_cmd. profile_name: # ovpn_provision_cmd: # Optional: # post_connect_cmd: # post_disconnect_cmd: # config_overrides: # log-level: 4 --- # Project VPN notes (for humans — not read by the plugin) Which account this profile is for, how to rotate its credentials, who to ping when it breaks. ``` ### 7. `.gitignore` Run check 8. If not covered, append `.claude/*.local.md` (create `.gitignore` if absent); if covered, say so and change nothing. ### 8. Summary Print the path written, the mode, and what the user still owes: - BYO + profile not imported → the `config-import` line again. - Check 4 borderline / skipped → the netcfg note again. - Check 9 WARN / FAIL → its fix lines again, and say that until they're applied programs may not resolve private names even with the tunnel up. End with: "Done. No restart needed — the skill re-reads this file every turn. Run `openvpn3-on-demand-doctor` any time to re-check." Add that asking the assistant for something VPN-gated triggers the skill, that the assistant decides per operation, and that a line in the project's `AGENTS.md` / `CLAUDE.md` marking resources VPN-only covers what it can't recognise from the command (a host in a config file, another plugin's calls).