docs: document secret variable handling in cli skills

Adds a Variables section to the resources skill covering file format,
plaintext vs. ciphertext semantics, safe creation recipes, and recovery
steps for corrupt secrets. Annotates the cli-commands skill with a
warning on `variable push` and clarifies `--plain-secrets` /
`--include-secrets` flag descriptions on `sync push`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guilhem Lemouel
2026-04-24 09:36:08 +02:00
parent 1d279e7a1e
commit ff915089d2
8 changed files with 157 additions and 13 deletions
+8 -2
View File
@@ -4045,11 +4045,17 @@ const command = new Command()
"--dry-run",
"Show changes that would be pushed without actually pushing",
)
.option("--plain-secrets", "Push secrets as plain text")
.option(
"--plain-secrets",
"Push secrets as plaintext (server encrypts on receipt). Required when pushing hand-written secret variables that weren't obtained via `sync pull`.",
)
.option("--json", "Use JSON instead of YAML")
.option("--skip-variables", "Skip syncing variables (including secrets)")
.option("--skip-secrets", "Skip syncing only secrets variables")
.option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)")
.option(
"--include-secrets",
"Include secrets in sync (overrides `skipSecrets` in wmill.yaml). Typically paired with `--plain-secrets` for hand-written secrets.",
)
.option("--skip-resources", "Skip syncing resources")
.option("--skip-resource-types", "Skip syncing resource types")
.option("--skip-scripts", "Skip syncing scripts")
+45 -3
View File
@@ -5662,9 +5662,49 @@ Example: \`f/databases/postgres_prod.resource.json\`
- \`value\` - Object containing the resource configuration
- \`resource_type\` - Name of the resource type (e.g., "postgresql", "slack")
## Variables
Variables store configuration values and secrets. Secrets are encrypted at rest on the server.
### File Format
Variable files use the pattern: \`{path}.variable.yaml\`
\`\`\`yaml
value: "plaintext-or-ciphertext-see-below"
is_secret: false
description: "Optional description"
\`\`\`
### Secrets: plaintext vs. ciphertext (IMPORTANT)
The \`value\` field for a secret variable is expected to be **already encrypted** by default. This is because the canonical way to get a local variable file is to \`wmill sync pull\` from the server, which writes encrypted ciphertext.
If you write the YAML by hand with a plaintext value and \`is_secret: true\`, you **must** push with \`--plain-secrets\`, or the server will store your plaintext as if it were ciphertext and every later decryption will fail with errors like \`Encoded text cannot have a 6-bit remainder\`.
Safe ways to create a plaintext secret:
\`\`\`bash
# Option 1: push the YAML with --plain-secrets so the server encrypts it
wmill variable push path.variable.yaml f/folder/name --plain-secrets
wmill sync push --plain-secrets --include-secrets
# Option 2: send the value directly via the API (always encrypts server-side)
wmill variable add "<plaintext>" f/folder/name
\`\`\`
### Recovering from a corrupt secret variable
If a secret was pushed without \`--plain-secrets\`, it cannot be decrypted. Symptoms:
- Resources that reference it fail at resolve time.
- \`wmill sync push\` fails during the pre-push export with a 500 and the same decrypt error (the export tries to read every variable).
- \`wmill variable push\` / \`variable add\` may fail with "already exists" due to a broken update path in some CLI versions.
Recovery: delete the variable via the UI (Variables page), then re-create it using one of the safe methods above.
## Variable References
Reference variables in resource values:
See Variables above for how to create them. Reference variables in resource values:
\`\`\`json
{
@@ -6336,11 +6376,11 @@ sync local with a remote workspaces or the opposite (push or pull)
- \`sync push\` - Push any local changes and apply them remotely.
- \`--yes\` - Push without needing confirmation
- \`--dry-run\` - Show changes that would be pushed without actually pushing
- \`--plain-secrets\` - Push secrets as plain text
- \`--plain-secrets\` - Push secrets as plaintext (server encrypts on receipt). Required when pushing hand-written secret variables that weren't obtained via \`sync pull\`.
- \`--json\` - Use JSON instead of YAML
- \`--skip-variables\` - Skip syncing variables (including secrets)
- \`--skip-secrets\` - Skip syncing only secrets variables
- \`--include-secrets\` - Include secrets in sync (overrides skipSecrets in wmill.yaml)
- \`--include-secrets\` - Include secrets in sync (overrides \`skipSecrets\` in wmill.yaml). Typically paired with \`--plain-secrets\` for hand-written secrets.
- \`--skip-resources\` - Skip syncing resources
- \`--skip-resource-types\` - Skip syncing resource types
- \`--skip-scripts\` - Skip syncing scripts
@@ -6440,6 +6480,8 @@ variable related commands
- \`--plain-secrets\` - Push secrets as plain text
- \`--public\` - Legacy option, use --plain-secrets instead
** Secret variables:** \`variable push\` assumes the \`value\` field in the YAML is already encrypted ciphertext. To push a plaintext secret, pass \`--plain-secrets\` (available on \`variable push\` and \`sync push\`) so the server encrypts it on receipt. \`variable add <value> <path>\` always encrypts server-side and is the safest way to create a plaintext secret from the CLI.
### version
Show version information
@@ -455,11 +455,11 @@ sync local with a remote workspaces or the opposite (push or pull)
- `sync push` - Push any local changes and apply them remotely.
- `--yes` - Push without needing confirmation
- `--dry-run` - Show changes that would be pushed without actually pushing
- `--plain-secrets` - Push secrets as plain text
- `--plain-secrets` - Push secrets as plaintext (server encrypts on receipt). Required when pushing hand-written secret variables that weren't obtained via `sync pull`.
- `--json` - Use JSON instead of YAML
- `--skip-variables` - Skip syncing variables (including secrets)
- `--skip-secrets` - Skip syncing only secrets variables
- `--include-secrets` - Include secrets in sync (overrides skipSecrets in wmill.yaml)
- `--include-secrets` - Include secrets in sync (overrides `skipSecrets` in wmill.yaml). Typically paired with `--plain-secrets` for hand-written secrets.
- `--skip-resources` - Skip syncing resources
- `--skip-resource-types` - Skip syncing resource types
- `--skip-scripts` - Skip syncing scripts
@@ -559,6 +559,8 @@ variable related commands
- `--plain-secrets` - Push secrets as plain text
- `--public` - Legacy option, use --plain-secrets instead
**⚠ Secret variables:** `variable push` assumes the `value` field in the YAML is already encrypted ciphertext. To push a plaintext secret, pass `--plain-secrets` (available on `variable push` and `sync push`) so the server encrypts it on receipt. `variable add <value> <path>` always encrypts server-side and is the safest way to create a plaintext secret from the CLI.
### version
Show version information
+4 -2
View File
@@ -2276,11 +2276,11 @@ sync local with a remote workspaces or the opposite (push or pull)
- \`sync push\` - Push any local changes and apply them remotely.
- \`--yes\` - Push without needing confirmation
- \`--dry-run\` - Show changes that would be pushed without actually pushing
- \`--plain-secrets\` - Push secrets as plain text
- \`--plain-secrets\` - Push secrets as plaintext (server encrypts on receipt). Required when pushing hand-written secret variables that weren't obtained via \`sync pull\`.
- \`--json\` - Use JSON instead of YAML
- \`--skip-variables\` - Skip syncing variables (including secrets)
- \`--skip-secrets\` - Skip syncing only secrets variables
- \`--include-secrets\` - Include secrets in sync (overrides skipSecrets in wmill.yaml)
- \`--include-secrets\` - Include secrets in sync (overrides \`skipSecrets\` in wmill.yaml). Typically paired with \`--plain-secrets\` for hand-written secrets.
- \`--skip-resources\` - Skip syncing resources
- \`--skip-resource-types\` - Skip syncing resource types
- \`--skip-scripts\` - Skip syncing scripts
@@ -2380,6 +2380,8 @@ variable related commands
- \`--plain-secrets\` - Push secrets as plain text
- \`--public\` - Legacy option, use --plain-secrets instead
**⚠ Secret variables:** \`variable push\` assumes the \`value\` field in the YAML is already encrypted ciphertext. To push a plaintext secret, pass \`--plain-secrets\` (available on \`variable push\` and \`sync push\`) so the server encrypts it on receipt. \`variable add <value> <path>\` always encrypts server-side and is the safest way to create a plaintext secret from the CLI.
### version
Show version information
@@ -460,11 +460,11 @@ sync local with a remote workspaces or the opposite (push or pull)
- `sync push` - Push any local changes and apply them remotely.
- `--yes` - Push without needing confirmation
- `--dry-run` - Show changes that would be pushed without actually pushing
- `--plain-secrets` - Push secrets as plain text
- `--plain-secrets` - Push secrets as plaintext (server encrypts on receipt). Required when pushing hand-written secret variables that weren't obtained via `sync pull`.
- `--json` - Use JSON instead of YAML
- `--skip-variables` - Skip syncing variables (including secrets)
- `--skip-secrets` - Skip syncing only secrets variables
- `--include-secrets` - Include secrets in sync (overrides skipSecrets in wmill.yaml)
- `--include-secrets` - Include secrets in sync (overrides `skipSecrets` in wmill.yaml). Typically paired with `--plain-secrets` for hand-written secrets.
- `--skip-resources` - Skip syncing resources
- `--skip-resource-types` - Skip syncing resource types
- `--skip-scripts` - Skip syncing scripts
@@ -564,6 +564,8 @@ variable related commands
- `--plain-secrets` - Push secrets as plain text
- `--public` - Legacy option, use --plain-secrets instead
**⚠ Secret variables:** `variable push` assumes the `value` field in the YAML is already encrypted ciphertext. To push a plaintext secret, pass `--plain-secrets` (available on `variable push` and `sync push`) so the server encrypts it on receipt. `variable add <value> <path>` always encrypts server-side and is the safest way to create a plaintext secret from the CLI.
### version
Show version information
@@ -34,9 +34,49 @@ Example: `f/databases/postgres_prod.resource.json`
- `value` - Object containing the resource configuration
- `resource_type` - Name of the resource type (e.g., "postgresql", "slack")
## Variables
Variables store configuration values and secrets. Secrets are encrypted at rest on the server.
### File Format
Variable files use the pattern: `{path}.variable.yaml`
```yaml
value: "plaintext-or-ciphertext-see-below"
is_secret: false
description: "Optional description"
```
### Secrets: plaintext vs. ciphertext (IMPORTANT)
The `value` field for a secret variable is expected to be **already encrypted** by default. This is because the canonical way to get a local variable file is to `wmill sync pull` from the server, which writes encrypted ciphertext.
If you write the YAML by hand with a plaintext value and `is_secret: true`, you **must** push with `--plain-secrets`, or the server will store your plaintext as if it were ciphertext and every later decryption will fail with errors like `Encoded text cannot have a 6-bit remainder`.
Safe ways to create a plaintext secret:
```bash
# Option 1: push the YAML with --plain-secrets so the server encrypts it
wmill variable push path.variable.yaml f/folder/name --plain-secrets
wmill sync push --plain-secrets --include-secrets
# Option 2: send the value directly via the API (always encrypts server-side)
wmill variable add "<plaintext>" f/folder/name
```
### Recovering from a corrupt secret variable
If a secret was pushed without `--plain-secrets`, it cannot be decrypted. Symptoms:
- Resources that reference it fail at resolve time.
- `wmill sync push` fails during the pre-push export with a 500 and the same decrypt error (the export tries to read every variable).
- `wmill variable push` / `variable add` may fail with "already exists" due to a broken update path in some CLI versions.
Recovery: delete the variable via the UI (Variables page), then re-create it using one of the safe methods above.
## Variable References
Reference variables in resource values:
See Variables above for how to create them. Reference variables in resource values:
```json
{
+41 -1
View File
@@ -29,9 +29,49 @@ Example: `f/databases/postgres_prod.resource.json`
- `value` - Object containing the resource configuration
- `resource_type` - Name of the resource type (e.g., "postgresql", "slack")
## Variables
Variables store configuration values and secrets. Secrets are encrypted at rest on the server.
### File Format
Variable files use the pattern: `{path}.variable.yaml`
```yaml
value: "plaintext-or-ciphertext-see-below"
is_secret: false
description: "Optional description"
```
### Secrets: plaintext vs. ciphertext (IMPORTANT)
The `value` field for a secret variable is expected to be **already encrypted** by default. This is because the canonical way to get a local variable file is to `wmill sync pull` from the server, which writes encrypted ciphertext.
If you write the YAML by hand with a plaintext value and `is_secret: true`, you **must** push with `--plain-secrets`, or the server will store your plaintext as if it were ciphertext and every later decryption will fail with errors like `Encoded text cannot have a 6-bit remainder`.
Safe ways to create a plaintext secret:
```bash
# Option 1: push the YAML with --plain-secrets so the server encrypts it
wmill variable push path.variable.yaml f/folder/name --plain-secrets
wmill sync push --plain-secrets --include-secrets
# Option 2: send the value directly via the API (always encrypts server-side)
wmill variable add "<plaintext>" f/folder/name
```
### Recovering from a corrupt secret variable
If a secret was pushed without `--plain-secrets`, it cannot be decrypted. Symptoms:
- Resources that reference it fail at resolve time.
- `wmill sync push` fails during the pre-push export with a 500 and the same decrypt error (the export tries to read every variable).
- `wmill variable push` / `variable add` may fail with "already exists" due to a broken update path in some CLI versions.
Recovery: delete the variable via the UI (Variables page), then re-create it using one of the safe methods above.
## Variable References
Reference variables in resource values:
See Variables above for how to create them. Reference variables in resource values:
```json
{
+10
View File
@@ -559,6 +559,16 @@ def generate_cli_commands_markdown(cli_data: dict) -> str:
md += "\n"
# Per-command annotations after the subcommand list
if cmd['name'] == 'variable':
md += (
"**⚠ Secret variables:** `variable push` assumes the `value` field in the "
"YAML is already encrypted ciphertext. To push a plaintext secret, pass "
"`--plain-secrets` (available on `variable push` and `sync push`) so the server "
"encrypts it on receipt. `variable add <value> <path>` always encrypts "
"server-side and is the safest way to create a plaintext secret from the CLI.\n\n"
)
return md