feat: add oneclaw module for 1Claw MCP integration - #857
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new community namespace (kmjones1979) and a new Terraform module (oneclaw) to configure 1Claw vault-backed secrets access and write/merge MCP server configuration for AI agents in Coder workspaces, including an optional “bootstrap” flow that provisions 1Claw resources from inside the workspace.
Changes:
- Introduces
registry/kmjones1979/contributor namespace metadata (README + avatar reference). - Adds
registry/kmjones1979/modules/oneclaw/with Terraform module wiring (coder_env+coder_script) and a unifiedscripts/run.sh. - Adds Terraform (
main.tftest.hcl) and Bun (main.test.ts) tests covering manual vs bootstrap behavior and ensuring the human key is not embedded in the rendered script.
Reviewed changes
Copilot reviewed 6 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| registry/kmjones1979/README.md | Adds namespace/frontmatter metadata for the new community contributor. |
| registry/kmjones1979/modules/oneclaw/README.md | Documents module usage (bootstrap/manual) and the security model/cleanup steps. |
| registry/kmjones1979/modules/oneclaw/main.tf | Implements the module’s inputs/outputs and Coder resources for env injection and startup script execution. |
| registry/kmjones1979/modules/oneclaw/main.tftest.hcl | Adds Terraform plan-based assertions for the module’s two operating modes and env/script wiring. |
| registry/kmjones1979/modules/oneclaw/main.test.ts | Adds integration-style tests validating rendered script behavior (including no human key literal). |
| registry/kmjones1979/modules/oneclaw/scripts/run.sh | Implements the bootstrap provisioning flow plus MCP config write/merge logic. |
main.tf: - Remove unused `order` variable. coder_script does not support an order field, so the variable was dead code. - Remove unused `data "coder_workspace_owner" "me"` source. scripts/run.sh: - Replace `eval echo` (used to expand `$HOME` / `~` in user-overridable config paths) with a dedicated expand_path() helper that uses bash string substitution and a case statement. Adds unit-test coverage confirming that injected `$(...)` and backticks are not executed. - Rewrite the existing-vault lookup so the vault name is passed to Python via argv instead of being interpolated into the inline Python source. The JSON payload is now fed to Python on stdin via a here-string instead of via a pipe + here-doc combination, which shellcheck flagged (SC2259) as broken (the heredoc would have overridden the piped stdin and the program would have failed to parse the response). - `unset _ONECLAW_HUMAN_API_KEY` immediately after reading it into HUMAN_KEY at the top of the script, so the privileged bootstrap key is no longer visible in /proc/<pid>/environ for the lifetime of the script run. - Update the "no API token or vault ID" warning to drop the mention of `master_api_key`, which was removed earlier in this PR. Made-with: Cursor
|
Thanks for the careful review — addressed all seven items in 4d5b414.
All checks still green: |
4d5b414 to
69ea479
Compare
main.tf: - Remove unused `order` variable. coder_script does not support an order field, so the variable was dead code. - Remove unused `data "coder_workspace_owner" "me"` source. scripts/run.sh: - Replace `eval echo` (used to expand `$HOME` / `~` in user-overridable config paths) with a dedicated expand_path() helper that uses bash string substitution and a case statement. Adds unit-test coverage confirming that injected `$(...)` and backticks are not executed. - Rewrite the existing-vault lookup so the vault name is passed to Python via argv instead of being interpolated into the inline Python source. The JSON payload is now fed to Python on stdin via a here-string instead of via a pipe + here-doc combination, which shellcheck flagged (SC2259) as broken (the heredoc would have overridden the piped stdin and the program would have failed to parse the response). - `unset _ONECLAW_HUMAN_API_KEY` immediately after reading it into HUMAN_KEY at the top of the script, so the privileged bootstrap key is no longer visible in /proc/<pid>/environ for the lifetime of the script run. - Update the "no API token or vault ID" warning to drop the mention of `master_api_key`, which was removed earlier in this PR. Made-with: Cursor
|
I think all issues were fixed, tested this full flow locally and all works well. Let me know if you find anything else. Thanks for all the support on this PR. |
Add kmjones1979 namespace and oneclaw module, ported from 1clawAI/1claw-coder-workspace-module. Provides vault-backed secrets and MCP server config for AI coding agents in Coder workspaces. - Namespace: kmjones1979 (avatar from GitHub) - Module: oneclaw with three provisioning modes (terraform-native, shell bootstrap, manual) - Tests: main.tftest.hcl (5 runs) and main.test.ts (5 tests) - Scripts: provision.sh, bootstrap.sh, setup.sh Made-with: Cursor
Made-with: Cursor
…dling Addresses reviewer feedback on closed PR coder#845 that the module was "split up way more than usual" and did not follow the registry module schema. Structure (matches the coder/ namespace conventions): - Collapse variables.tf + outputs.tf into main.tf - Merge scripts/bootstrap.sh + scripts/setup.sh into a single scripts/run.sh executed by a single coder_script - Remove Terraform-native provisioning mode (scripts/provision.sh, null_resource.provision, master_api_key): it relied on local-exec writing a state file to the provisioner's cwd, which is ephemeral inside Coder template provisioners and therefore cannot round-trip credentials into coder_env - Keep two supported modes: bootstrap (human 1ck_ key, recommended) and manual (pre-provisioned scoped ocv_ key) Security hardening for the 1ck_ human bootstrap key: - Deliver the key via a sensitive coder_env (_ONECLAW_HUMAN_API_KEY) instead of templatefile() substitution, so the literal key never appears in the rendered script body stored in Terraform state or logged to the workspace's /tmp/coder-agent.log - Send the key to the 1Claw auth endpoint via curl --data-binary @- from stdin so it does not appear in process argv (ps/proc/cmdline) - Unset HUMAN_KEY and _ONECLAW_HUMAN_API_KEY as soon as auth completes so downstream processes do not inherit the key - Only the scoped ocv_ agent key and vault id are persisted to ~/.1claw/bootstrap.json and the MCP config files - README documents post-bootstrap cleanup (set human_api_key = "" once the state file exists) and the full security guarantees Tested end-to-end against a local Coder server with real 1Claw credentials: first boot, idempotent restart, and post-bootstrap cleanup all succeed and leave no copy of the 1ck_ value anywhere on the workspace filesystem or in its process environments. Made-with: Cursor
main.tf: - Remove unused `order` variable. coder_script does not support an order field, so the variable was dead code. - Remove unused `data "coder_workspace_owner" "me"` source. scripts/run.sh: - Replace `eval echo` (used to expand `$HOME` / `~` in user-overridable config paths) with a dedicated expand_path() helper that uses bash string substitution and a case statement. Adds unit-test coverage confirming that injected `$(...)` and backticks are not executed. - Rewrite the existing-vault lookup so the vault name is passed to Python via argv instead of being interpolated into the inline Python source. The JSON payload is now fed to Python on stdin via a here-string instead of via a pipe + here-doc combination, which shellcheck flagged (SC2259) as broken (the heredoc would have overridden the piped stdin and the program would have failed to parse the response). - `unset _ONECLAW_HUMAN_API_KEY` immediately after reading it into HUMAN_KEY at the top of the script, so the privileged bootstrap key is no longer visible in /proc/<pid>/environ for the lifetime of the script run. - Update the "no API token or vault ID" warning to drop the mention of `master_api_key`, which was removed earlier in this PR. Made-with: Cursor
The repo's README validator (cmd/readmevalidation) requires every module README to contain exactly one fenced ```tf code block (with a `version` field) between the H1 and the next heading. The previous structure had the H1 followed directly by `## Usage`, with the tf example nested inside `### Bootstrap mode`, so the validator failed with: "registry/kmjones1979/modules/oneclaw/README.md": did not find Terraform code block within h1 section Move the canonical bootstrap-mode example up to sit directly under the H1 paragraph (matching the convention used by registry/coder/modules/*), and drop the now-duplicate block from the Bootstrap mode subsection. Made-with: Cursor
69ea479 to
aac4cd3
Compare
Merge latest registry main (including lowercase namespace validation) and update the module icon plus README links to match current 1Claw MCP docs and llms.txt guidance. Co-authored-by: Cursor <cursoragent@cursor.com>
Swap the oversized embedded SVG for the current round dark PNG and update module icon references accordingly. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (8)
registry/kmjones1979/modules/oneclaw/scripts/run.sh:150
- This API call constructs JSON via string interpolation. If
bootstrap_agent_namecontains quotes/newlines, it can break the JSON payload. Building the request body via JSON serialization avoids escaping bugs.
log "Creating agent '$AGENT_NAME_IN'..."
local agent_response agent_id agent_key
agent_response=$(api_call POST "/v1/agents" "$jwt" \
"{\"name\": \"$AGENT_NAME_IN\", \"vault_ids\": [\"$vault\"]}") || die "Failed to create agent"
registry/kmjones1979/modules/oneclaw/scripts/run.sh:162
- This API call constructs JSON via string interpolation. If
bootstrap_policy_pathcontains quotes/newlines, it can break the JSON payload. Building the policy body via JSON serialization avoids escaping bugs.
log "Creating access policy (path: $POLICY_PATH_IN)..."
api_call POST "/v1/vaults/$vault/policies" "$jwt" \
"{\"secret_path_pattern\": \"$POLICY_PATH_IN\", \"principal_type\": \"agent\", \"principal_id\": \"$agent_id\", \"permissions\": [\"read\", \"write\"]}" \
> /dev/null || die "Failed to create policy"
registry/kmjones1979/modules/oneclaw/main.tf:170
- This module introduces a new startup script via a raw
coder_scriptresource. Per registry guidance for new/rewritten script-based modules, prefer using thecoder-utilsmodule to orchestrate scripts (ordering, sync, logs) and to standardize the runtime directory layout; see e.g.registry/matifali/modules/omnigent/main.tf:145-157for the expected pattern.
resource "coder_script" "run" {
agent_id = var.agent_id
display_name = "1Claw"
icon = var.icon
run_on_start = true
registry/kmjones1979/modules/oneclaw/main.tf:181
STATE_DIRis set to$HOME/.1claw, which means the module writes its own runtime state directly under$HOME. The registry’s module data layout guidance expects module-controlled runtime data to live under$HOME/.coder-modules/<namespace>/<module-name>/...to keep workspace state discoverable and avoid ad-hoc dot-directories.
POLICY_PATH = var.bootstrap_policy_path
STATE_DIR = "$HOME/.1claw"
MCP_HOST = var.mcp_host
registry/kmjones1979/modules/oneclaw/scripts/run.sh:3
- This script writes secrets (bootstrap state and MCP config containing bearer tokens) and only applies
chmod 600after writing. Addingumask 077up-front prevents a brief window where newly created files may be readable under a permissive default umask.
#!/bin/bash
set -euo pipefail
registry/kmjones1979/modules/oneclaw/scripts/run.sh:75
api_call()tries to capture curl’s exit code, but withset -e+pipefailenabled, a failingcurlin the command substitution can terminate the script beforerc=$?is reached. This makes the function’s error handling unreliable (and can prevent intended fallbacks like “create vault, else list and find existing”).
if [ -n "$body" ]; then
response=$(printf '%s' "$body" | curl -s -w "\n%%{http_code}" \
-K "$curl_cfg" \
-H "Content-Type: application/json" \
--data-binary @- \
-X "$method" "$API_URL$path" 2>&1)
registry/kmjones1979/modules/oneclaw/scripts/run.sh:124
- This API call constructs JSON via string interpolation. If
bootstrap_vault_namecontains quotes/newlines, it will produce invalid JSON (and is hard to debug). Consider generating the JSON body via a serializer (Python is already a dependency in this script) so values are properly escaped.
This issue also appears in the following locations of the same file:
- line 147
- line 159
log "Creating vault '$VAULT_NAME_IN'..."
local vault_response
vault_response=$(api_call POST "/v1/vaults" "$jwt" \
"{\"name\": \"$VAULT_NAME_IN\"}") || {
registry/kmjones1979/modules/oneclaw/main.tf:64
agent_id_1claw/ONECLAW_AGENT_IDis defined and exported, but it’s not referenced anywhere inscripts/run.shnor included in the generated MCP config (which only sets Authorization + X-Vault-ID). Unless something else consumes this env var, it’s dead configuration and the variable description may mislead users.
variable "agent_id_1claw" {
type = string
description = "Optional 1Claw agent UUID. When omitted, the MCP server resolves the agent from the API key prefix."
default = ""
}
Migrate script orchestration to coder-utils with the standard module data directory, harden API request bodies and curl error handling, and update tests and docs for the new install pipeline. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Addressed all 8 suppressed Copilot findings from the Aug 12 review in
Tests run locally:
Note: Bootstrap no longer sets |
DevelopmentCats
left a comment
There was a problem hiding this comment.
LGTM I tested everything in my own workspaces as well and it works like a charm!
Thank you very much for the contribution!!
Summary
Resubmission of #845 (closed for structural reasons) with the reviewer feedback addressed and additional security hardening of the bootstrap flow.
Adds the
kmjones1979namespace and theoneclawmodule, which provides vault-backed secrets and MCP server wiring for AI coding agents (Cursor, Claude Code) in Coder workspaces.Changes since #845
Structural (addresses @DevelopmentCats review)
The reviewer asked that the module follow the standard schema used in the
coder/namespace:Done:
variables.tfandoutputs.tfare consolidated intomain.tf.scripts/bootstrap.shandscripts/setup.share merged into a singlescripts/run.shexecuted by onecoder_script.scripts/provision.sh,null_resource.provision,master_api_key) is removed. That mode relied on alocal-execprovisioner writing a state file to the provisioner's cwd, which is ephemeral inside Coder template provisioners and cannot round-trip credentials intocoder_env. Two modes remain: bootstrap (recommended) and manual.Final tree:
Security hardening for the
1ck_human bootstrap keyThe
1ck_human API key is privileged (can create and destroy vaults in the caller's 1Claw account), so the module goes out of its way to make sure it cannot be recovered from the workspace after bootstrap:coder_envvariable (_ONECLAW_HUMAN_API_KEY) rather than viatemplatefile()substitution. As a result, the literal key never appears in the rendered script body that lives in Terraform state or in the Coder agent's/tmp/coder-agent.log. The rendered script only showsHUMAN_KEY="\${_ONECLAW_HUMAN_API_KEY:-}".curl --data-binary @-from stdin, so it never appears inps aux//proc/<pid>/cmdline.HUMAN_KEYand_ONECLAW_HUMAN_API_KEYare unset immediately after authentication, so downstream subprocesses spawned by the script do not inherit the key.ocv_agent key and the vault id are persisted to~/.1claw/bootstrap.jsonand the MCP config files.README.mddocuments a post-bootstrap cleanup flow: once the state file exists, the user is instructed to sethuman_api_key = ""in their Terraform so subsequent restarts do not reference the human key at all.Test plan
Verified against a local Coder server (v2.31.9) running the module with real 1Claw credentials.
terraform testpasses (4 runs, Terraform 1.14 via Docker)bun test main.test.tspasses (5 tests, including an explicit assertion that the human key value is not embedded in the rendered script)shellcheckis clean onscripts/run.shbun run fmtleaves the tree unchangedbootstrap.json+ Cursor/Claude MCP configshuman_api_key = "",coder update, restart) continues to work using cached credentials1ck_key value does not appear in any file on the workspace (state file, MCP configs, agent log, script log, shell init files,/proc/<pid>/environof any Coder process)Made with Cursor