#standard approach

1 messages · Page 1 of 1 (latest)

buoyant furnace
#

Claude responded: Send this:Send this:

"I have a skill that runs Node.js scripts via exec. My current setup is:

API keys stored as plaintext in skills.entries.icp-hunter.env in openclaw.json:

json"icp-hunter": {
"enabled": true,
"env": {
"NOTION_API_KEY": "ntn_actual_key_here",
"NOTION_DATABASE_ID": "34f0487c...",
"CLAY_WEBHOOK_URL": "https://api.clay.com/...",
"CLAY_WEBHOOK_TOKEN": "actual_token_here"
}
}

Scripts in ~/.openclaw/workspace/scripts/ that read keys via process.env.NOTION_API_KEY
SKILL.md tells the agent to run node fetch-leads.js, node update-status.js, node push-to-clay.js
The agent reads SKILL.md, runs scripts via exec (scripts handle API calls), and the agent handles reasoning/scoring

My concerns:

API keys are in plaintext in openclaw.json — is this safe enough or should I use SecretRef?
Is storing keys in skills.entries.env the right place or should they be in ~/.openclaw/.env or secrets.json?
Is the overall pattern (SKILL.md + scripts + LLM reasoning) the recommended way to build skills that combine external API calls with LLM intelligence?
Any security or structural improvements I should make before running this in production?"

fiery flaxBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

zenith vigilBOT
#

Bobby — I checked the current docs/source shape. Short version: your architecture is basically reasonable, but I’d tighten the secret handling before calling it production.

  1. Plaintext in openclaw.json works, but I wouldn’t call it “safe enough” for production secrets. Treat it like any local secrets file: private machine/user, locked-down perms, backups excluded, never shared in logs/screenshots. Prefer SecretRef where the field supports it.

  2. For skills specifically, the documented SecretRef surface is skills.entries.<skill>.apiKey, not arbitrary skills.entries.<skill>.env.*. skills.entries.env is documented as plain string env injection for host runs. So the clean pattern is:

  • Put the skill’s main token in apiKey using a SecretRef.
  • Declare metadata.openclaw.primaryEnv in SKILL.md so OpenClaw maps apiKey into that env var.
  • For additional secrets, use process env / ~/.openclaw/.env, or a SecretRef-backed provider/file where the supported config field exists. ~/.openclaw/secrets.json is useful as a file SecretRef payload, but you still need to reference it from a SecretRef-supported field.

Example for the primary key:

{
  skills: {
    entries: {
      "icp-hunter": {
        enabled: true,
#
        apiKey: { source: "env", provider: "default", id: "NOTION_API_KEY" },
        env: {
          NOTION_DATABASE_ID: "34f0487c...",
          CLAY_WEBHOOK_URL: "https://api.clay.com/..."
        }
      }
    }
  }
}

Then keep NOTION_API_KEY in the Gateway process env or ~/.openclaw/.env. Don’t put secret values in SKILL.md.

  1. SKILL.md + deterministic scripts + LLM reasoning/scoring is a good pattern. I’d make the scripts own the API calls and return clean JSON; let the model decide/scoring based on that JSON. Keep writes explicit/idempotent, and have dry-run modes for update-status / push-to-clay.

  2. Structural/security improvements I’d make:

  • Put scripts next to the skill folder and reference them via {baseDir} instead of loose ~/.openclaw/workspace/scripts/ if this is a reusable skill.
#
  • Don’t log env vars, request headers, webhook tokens, or full Notion payloads if they contain sensitive data.
  • Use least-privilege API tokens and rotate them.
  • Validate all inputs to scripts; avoid shell interpolation.
  • Add rate limiting/retry handling for Notion/Clay.
  • For production writes, require a confirmation step or write a “pending review” status first.
  • Remember: skills.entries.*.env/apiKey injection applies to host runs, not sandboxed sessions.

Docs: https://docs.openclaw.ai/tools/skills, https://docs.openclaw.ai/tools/skills-config, https://docs.openclaw.ai/gateway/secrets, https://docs.openclaw.ai/reference/secretref-credential-surface

buoyant furnace
#

"I have my secrets in ~/.openclaw/.env (symlinked to /opt/aws/open_claw/openclaw.env). My Slack channel config already uses SecretRef properly:
json"botToken": {
"source": "env",
"provider": "default",
"id": "SLACK_BOT_TOKEN"
}
Now I want to do the same for my skill env vars. Currently my skill has plaintext keys:
json"icp-hunter": {
"enabled": true,
"env": {
"NOTION_API_KEY": "ntn_actual_key_here",
"NOTION_DATABASE_ID": "34f0487c...",
"CLAY_WEBHOOK_URL": "https://api.clay.com/...",
"CLAY_WEBHOOK_TOKEN": "actual_token_here"
}
}
I want to move the sensitive keys (NOTION_API_KEY, CLAY_WEBHOOK_TOKEN) to my .env file and reference them via SecretRef. How exactly should the skills.entries config look? Is it like this:
json"icp-hunter": {
"enabled": true,
"apiKey": { "source": "env", "provider": "default", "id": "NOTION_API_KEY" },
"env": {
"NOTION_DATABASE_ID": "34f0487c...",
"CLAY_WEBHOOK_URL": "https://api.clay.com/...",
"CLAY_WEBHOOK_TOKEN": { "source": "env", "provider": "default", "id": "CLAY_WEBHOOK_TOKEN" }
}
}
Or does SecretRef not work inside skills.entries.*.env? What's the correct way to mix SecretRef and plain values in skill config?"

zenith vigilBOT
#

You’ve got it mostly right, with one important correction: SecretRef works for skills.entries.<skill>.apiKey, but not for arbitrary values inside skills.entries.<skill>.env.

env is a Record<string, string> — plain strings only. So this part is not the supported shape:

CLAY_WEBHOOK_TOKEN: { source: "env", provider: "default", id: "CLAY_WEBHOOK_TOKEN" }

The supported config looks like this for the primary skill secret:

{
  skills: {
    entries: {
      "icp-hunter": {
        enabled: true,
#
        apiKey: { source: "env", provider: "default", id: "NOTION_API_KEY" },
        env: {
          NOTION_DATABASE_ID: "34f0487c...",
          CLAY_WEBHOOK_URL: "https://api.clay.com/..."
        }
      }
    }
  }
}

For that to inject NOTION_API_KEY, your SKILL.md should declare it as the primary env, e.g. metadata contains:

{"openclaw":{"primaryEnv":"NOTION_API_KEY","requires":{"env":["NOTION_API_KEY"]}}}