Skip to content
Back to skills

Configure

ASecurity

Configure the voice channel connection — dispatcher URL, pairing string (bundles agent ID, token, and dispatcher certificate), and optional permission-relay opt-in.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 27, 2026
developmentbashdocker

Works with

  • terminal

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add gtapps/voice-channel --skill configure --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Configure?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Configure
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gtapps-configure/badge)](https://www.skillsdirectory.com/skills/gtapps-configure)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: voice:configure
description: Configure the voice channel connection — dispatcher URL, pairing string (bundles agent ID, token, and dispatcher certificate), and optional permission-relay opt-in.
allowed-tools:
  - AskUserQuestion
  - Read
  - Write
  - Bash(echo *)
  - Bash(mkdir *)
  - Bash(bun -e *)
  - Bash(chmod *)
---

# /voice:configure

Configure the voice channel connection inside this agent container.

## Resolve the state dir

Run:

```bash
echo "${VOICE_STATE_DIR:-$HOME/.claude/channels/voice}"
```

Use the output as `<STATE_DIR>` for every file path below.

## Detect existing config

Check if `<STATE_DIR>/config.json` exists. If it does, read it and record the
current values for `dispatcher_url`, `agent_id`, `dispatcher_cert_sha256`, `dispatcher_cert_pem`, and
`enable_permission_relay`. Tell the user:
_"Found existing config — showing current values as defaults."_

## Collect settings

### Call 1 — Dispatcher URL + permission relay

Ask both in a single `AskUserQuestion` call:

```
questions: [
  {
    header: "Dispatcher URL",
    question: "WebSocket URL of the voice-dispatcher on your laptop?",
    options: [
      // Always show the secure default first.
      // If existing config differs from the default, replace option 2 with the current value.
      // If existing config matches or there is no config, use the LAN IP fallback.
      { label: "wss://127.0.0.1:7355", description: "localhost — default (dispatcher on the same machine)" },
      { label: "<current value OR 'wss://laptop.local:7355'>", description: "<'Current value' OR 'Remote / Docker — mDNS hostname'>" }
    ]
  },
  {
    header: "Permission relay",
    question: "Relay Claude's tool-permission prompts through the voice channel?",
    options: [
      { label: "No — keep off", description: "Terminal approval only. Safest — anyone the mic can hear could otherwise say 'yes <id>' and approve a tool call. (default)" },
      { label: "Yes — enable", description: "⚠ Voice approval is unauthenticated. Only enable if you accept that risk and understand terminal approval is always the fallback." }
    ]
  }
]
```

### Call 2 — Pairing string

Ask alone so the user can focus on pasting it:

```
questions: [
  {
    header: "Pairing string",
    question: "Paste the pairing string printed by the dispatcher:",
    options: [
      // Include ONLY if existing config has agent_id, dispatcher_cert_sha256, dispatcher_cert_pem
      // AND the .env file has a token:
      { label: "Keep existing pairing", description: "Leave the current agent ID, token, and dispatcher certificate unchanged" },
      { label: "I don't have the pairing string yet", description: "Run voice-dispatcher config add-agent <id> --triggers '...' --voice <voice.onnx> on the dispatcher first, then re-run /voice:configure." }
    ]
    // User pastes the actual voicepair_... string via Other
  }
]
```

Handle the result:

- **"Keep existing pairing"** → keep `agent_id`, `dispatcher_cert_sha256`, `dispatcher_cert_pem`, and the token in `.env` unchanged
- **"I don't have the pairing string yet"** → stop here; remind the user to run
  `voice-dispatcher config add-agent <agent_id> --triggers "..."` on their laptop, then re-run `/voice:configure`
- **Other (typed value starting with `voicepair_`)** → decode it (see below)

## Decode the pairing string

Use Bun (the plugin's guaranteed runtime — not coreutils `base64`, which may be
absent in minimal containers and cannot decode url-safe base64):

```bash
bun -e 'process.stdout.write(Buffer.from(process.argv[1].replace(/^voicepair_/,""),"base64url").toString())' "<pairing-string>"
```

Parse the JSON output. It must contain:

- `pairing_v` — must be `2`; if missing or not `2`, stop and ask the user to upgrade/re-run `voice-dispatcher config add-agent` or `rotate-token`
- `agent_id` — the agent's ID on the dispatcher
- `token` — the bearer token (a credential — write to `.env`, not `config.json`)
- `cert_sha256` — the dispatcher's TLS cert fingerprint (public — write to `config.json`)
- `cert_pem` — the dispatcher's public TLS certificate PEM (public — write to `config.json` as `dispatcher_cert_pem`)

Before writing, verify `sha256(DER(cert_pem))` equals `cert_sha256`. Use Bun's `X509Certificate` and `createHash`; if the values disagree, stop and ask for a fresh pairing string.

## Write `.env` and `config.json`

Create `<STATE_DIR>` if it does not exist, then:

**`<STATE_DIR>/.env`** — the token is a credential:

```
VOICE_DISPATCHER_TOKEN=<token>
```

```bash
chmod 600 "<STATE_DIR>/.env"
```

**`<STATE_DIR>/config.json`**:

```json
{
  "dispatcher_url": "<dispatcher_url>",
  "agent_id": "<agent_id>",
  "dispatcher_cert_sha256": "<cert_sha256>",
  "dispatcher_cert_pem": "<cert_pem>",
  "enable_permission_relay": <enable_permission_relay>
}
```

## After writing

Tell the user:

- To activate the channel, close this session and start a new one with:
  ```
  claude --dangerously-load-development-channels plugin:voice@voice-channel
  ```
- This skill only configures the plugin inside this container — it does NOT modify
  the dispatcher's config on the laptop.

## Notes

- This skill writes the bearer token to `<STATE_DIR>/.env` (chmod 600) and the
  rest of the config to `<STATE_DIR>/config.json`. The token never appears in
  `config.json`.
- The v2 pairing string bundles `agent_id` + `token` + `cert_sha256` + `cert_pem` in one
  paste-safe string. It is printed by `voice-dispatcher config add-agent` on the
  laptop. The PEM is public certificate material; the token remains the only secret.
- To add this agent to the dispatcher, the user must run on their laptop if they
  haven't yet:
  `voice-dispatcher config add-agent <agent_id> --triggers "hey jarvis,jarvis" --voice <voice.onnx>`
- To re-pair a single agent (e.g. after a token rotation), run on the laptop:
  `voice-dispatcher config rotate-token <agent_id>`
  then re-run `/voice:configure` with the new pairing string. Other agents are
  unaffected (shared cert is unchanged).
- To re-pair all agents (after a cert rotation on the laptop), run:
  `voice-dispatcher tls rotate`
  then re-run `/voice:configure` on every agent container.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.