Skip to content

Instantly share code, notes, and snippets.

@sallyom
Last active May 23, 2026 21:23
Show Gist options
  • Select an option

  • Save sallyom/50f4678b053eaa93c4311a5509e52aeb to your computer and use it in GitHub Desktop.

Select an option

Save sallyom/50f4678b053eaa93c4311a5509e52aeb to your computer and use it in GitHub Desktop.
OpenClaw SecretRef Vault smoke test

Vault/OpenClaw live smoke reproduction

Updated May 23, 2026 for the plugin-managed SecretRef provider contract.

This verifies an external Vault plugin installed from git:github.com/sallyom/claw-vault using canonical OpenClaw plugin id vault. The repo/package name remains claw-vault; the OpenClaw plugin id and SecretRef integration id are both vault.

Contract shape

OpenClaw model credentials still use a normal SecretRef:

{
  "models": {
    "providers": {
      "openrouter": {
        "baseUrl": "https://openrouter.ai/api/v1",
        "apiKey": {
          "source": "exec",
          "provider": "vault",
          "id": "providers/openrouter/apiKey"
        },
        "models": [
          {
            "id": "google/gemini-3.5-flash",
            "name": "google/gemini-3.5-flash"
          }
        ]
      }
    }
  }
}

The provider config points at the owning plugin integration instead of copying command and args into OpenClaw config:

{
  "secrets": {
    "providers": {
      "vault": {
        "source": "exec",
        "pluginIntegration": {
          "pluginId": "vault",
          "integrationId": "vault"
        }
      }
    }
  }
}

At runtime OpenClaw loads the active plugin manifest, verifies the plugin is installed/enabled/trusted, and materializes the exec resolver from secretProviderIntegrations.vault.

OpenShift proof

Environment tested:

  • OpenShift namespace: openclaw-bob
  • Pod/container: openclaw-8468444d5c-g62bp, container gateway
  • Vault address: http://vault.vault.svc:8200
  • Plugin install source: git:github.com/sallyom/claw-vault
  • Installed plugin commit: 1aa40622d046c638d92eeb781415ccd8c1362595
  • No Vault token value was printed.

Plugin CLI command is registered

Command:

openclaw vault -h

Output:

OpenClaw 2026.5.22 (unknown) — All your chats, one OpenClaw.

Usage: openclaw vault [options] [command]

Manage Vault SecretRefs

Options:
  -h, --help  Display help for command

Commands:
  help        Display help for command
  setup       Create a Vault SecretRef setup plan
  status      Show Vault SecretRef provider status

Vault provider status uses pluginIntegration

Command:

openclaw vault status --json

Output:

{
  "providerAlias": "vault",
  "provider": {
    "configured": true,
    "source": "exec",
    "pluginIntegration": {
      "pluginId": "vault",
      "integrationId": "vault"
    }
  },
  "resolverScript": "/home/node/.openclaw/git/git-c317c86c2672b3ca/repo/dist/vault-secret-ref-resolver.js",
  "vaultAddr": "http://vault.vault.svc:8200",
  "kvMount": "secret",
  "kvVersion": "2",
  "hasVaultToken": true
}

Installed plugin manifest owns the resolver

Sanitized installed manifest/runtime proof:

{
  "manifest": {
    "id": "vault",
    "activation": {
      "onStartup": false,
      "onCommands": ["vault"]
    },
    "commandAliases": [
      {
        "name": "vault",
        "kind": "cli"
      }
    ],
    "secretProviderIntegrations": {
      "vault": {
        "providerAlias": "vault",
        "displayName": "HashiCorp Vault",
        "source": "exec",
        "command": "${node}",
        "args": ["./vault-secret-ref-resolver.js"],
        "timeoutMs": 5000,
        "noOutputTimeoutMs": 5000,
        "maxOutputBytes": 1048576,
        "allowInsecurePath": true,
        "passEnv": [
          "VAULT_ADDR",
          "VAULT_TOKEN",
          "VAULT_NAMESPACE",
          "CLAW_VAULT_KV_MOUNT",
          "CLAW_VAULT_KV_VERSION",
          "CLAW_VAULT_VALUES_JSON"
        ]
      }
    }
  },
  "distIndexHead": "definePluginEntry({ id: \"vault\", ... registerCli(...) })"
}

Sanitized OpenClaw config shape

{
  "plugins": {
    "allow": ["vault"],
    "entries": {
      "vault": {
        "enabled": true
      }
    }
  },
  "secrets": {
    "providers": {
      "vault": {
        "source": "exec",
        "pluginIntegration": {
          "pluginId": "vault",
          "integrationId": "vault"
        }
      }
    }
  },
  "models": {
    "providers": {
      "openrouter": {
        "baseUrl": "https://openrouter.ai/api/v1",
        "apiKey": {
          "source": "exec",
          "provider": "vault",
          "id": "providers/openrouter/apiKey"
        },
        "models": [
          {
            "id": "google/gemini-3.5-flash",
            "name": "google/gemini-3.5-flash"
          }
        ]
      }
    }
  }
}

SecretRef audit executes the Vault resolver

Command:

openclaw secrets audit --allow-exec --json

Relevant output:

{
  "version": 1,
  "status": "findings",
  "resolution": {
    "refsChecked": 2,
    "skippedExecRefs": 0,
    "resolvabilityComplete": true
  },
  "summary": {
    "plaintextCount": 1,
    "unresolvedRefCount": 0,
    "shadowedRefCount": 2,
    "legacyResidueCount": 0
  }
}

unresolvedRefCount: 0 proves the configured exec SecretRefs resolved through the Vault provider. The findings in this deployment were non-blocking audit warnings: a plaintext gateway auth token and provider precedence shadowing.

Model availability

openclaw models status --json showed openrouter/google/gemini-3.5-flash as allowed and provider auth profiles for openai and openrouter as SecretRef-backed (ref(exec:providers/.../apiKey)). A live model call was also confirmed separately in this deployment.

Note: in this specific pod, openclaw secrets reload --json from the CLI context was blocked by a Gateway scope-upgrade approval. That is an auth/scope issue for the command path, not a Vault resolver failure; secrets audit --allow-exec --json did execute the resolver and reported zero unresolved refs.

User workflow

The plugin helper can generate the plan that writes the provider config and model SecretRefs:

openclaw vault setup \
  --openrouter-id providers/openrouter/apiKey \
  --plan-out ./vault-secrets-plan.json

openclaw secrets apply --from ./vault-secrets-plan.json --dry-run --allow-exec
openclaw secrets apply --from ./vault-secrets-plan.json --allow-exec
openclaw secrets audit --allow-exec --json

openclaw vault setup maps existing Vault secret ids into OpenClaw SecretRefs and provider config. It does not write secret values into Vault.

What this replaced

Older smoke tests copied this into OpenClaw config:

{
  "source": "exec",
  "command": "/usr/local/bin/node",
  "args": ["/home/node/.openclaw/plugins/claw-vault/vault-secret-ref-resolver.js"],
  "passEnv": ["VAULT_ADDR", "VAULT_TOKEN"]
}

That is still possible as manual exec config, but it is no longer the plugin-managed contract. The plugin-managed contract stores plugin ownership (pluginId + integrationId) and lets OpenClaw materialize command details from the installed plugin manifest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment