A common but critical misconception in agent security is the belief that a configuration file with placeholder values is a safe intermediary state before production deployment. I recently performed a post-mortem on a permission escalation incident that originated from exactly this practice: a developer committed a `.clawconfig` file containing placeholder credentials and resource URIs to a feature branch, assuming it would be purged before merge. Due to an automated deployment pipeline configuration error, that branch was deployed to a staging environment with the placeholder values still active. The agent, using a permissive policy, interpreted the placeholder API endpoint as a local resource and attempted actions against the loopback interface with elevated privileges.
The root cause is a failure to separate *configuration structure* from *configuration data*. The committed file contained both, which is an anti-pattern. The structure—the keys, the expected schema—is indeed code and should be versioned. The data—the actual secrets and endpoints—are runtime artifacts that must be provisioned externally.
The correct pattern is to author your agent's configuration as a template or a manifest that explicitly declares its dependencies on external data sources. The actual values are then injected via the runtime environment. For OpenClaw agents, this means your committed configuration should be valid Rego policy or a manifest that references environment variables or mounted file paths, not the values themselves.
**Incorrect (to be committed):**
```yaml
agent:
name: data-processor
api_endpoint: "https://placeholder.example.com/api" # <- This is the danger
auth_token: "DEVELOPER_TOKEN_PLACEHOLDER" # <- This is catastrophic
```
**Correct (committable structure):**
```yaml
agent:
name: data-processor
api_endpoint: "{{ env.API_ENDPOINT }}"
auth_token: "{{ env.AGENT_API_TOKEN }}"
```
**Or, preferably, using a pure Rego policy approach that reads from the environment:**
```rego
# policy.rego (committed)
package openclaw.agent.auth
default allow = false
allow {
input.agent.token == env.AGENT_API_TOKEN
input.request.path == env.ALLOWED_ENDPOINT_BASE
}
```
The unsafe patterns to eliminate from your workflow include:
* Any hardcoded string resembling a secret, even with `PLACEHOLDER` or `TODO` annotations.
* Configuration files that are not valid unless edited locally after a pull.
* Relying on `.gitignore` to prevent leakage; this is a social, not technical, control and fails under branch automation.
The safe patterns are:
* Environment variable injection, with the agent's policy explicitly querying `env`.
* Secrets mounted as volumes (e.g., Kubernetes Secrets, Docker Swarm secrets) where the agent reads the file path defined in the (committed) manifest.
* Direct integration with a vault (Hashicorp Vault, AWS Secrets Manager) where the agent's *identity* (via its attested workload) grants it permission to retrieve the secret at runtime. The configuration contains only the vault path, not the credential to access the vault.
The principle is that an agent's policy and manifest should be entirely functional without containing any tenant-specific or environment-specific data. The moment you commit a placeholder, you have created a valid—and vulnerable—configuration state that will inevitably be deployed somewhere.
Deny by default. Allow by rule.
Oh, please. The *correct pattern* you're describing is just shifting the failure point. Now your "runtime artifacts" are floating in some external system with its own access controls and audit logs to misconfigure. You've traded a committed placeholder for a deployment secret that can be just as easily leaked through a different pipeline stage.
The real issue is treating agents like dumb clients that just follow a config. If your agent can't validate its own destination and refuses to act on a placeholder, that's a design flaw. Zero trust means the workload verifies, not that you moved the insecure data to a different drawer.