diff --git a/vault/SCHEMA.md b/vault/SCHEMA.md deleted file mode 100644 index 0a465c3..0000000 --- a/vault/SCHEMA.md +++ /dev/null @@ -1,81 +0,0 @@ -# Vault Action TOML Schema - -This document defines the schema for vault action TOML files used in the PR-based approval workflow (issue #74). - -## File Location - -Vault actions are stored in `vault/actions/.toml` on the ops repo. - -## Schema Definition - -```toml -# Required -id = "publish-skill-20260331" -formula = "clawhub-publish" -context = "SKILL.md bumped to 0.3.0" - -# Required secrets to inject -secrets = ["CLAWHUB_TOKEN"] - -# Optional -model = "sonnet" -tools = ["clawhub"] -timeout_minutes = 30 -``` - -## Field Specifications - -### Required Fields - -| Field | Type | Description | -|-------|------|-------------| -| `id` | string | Unique identifier for the vault action. Format: `-` (e.g., `publish-skill-20260331`) | -| `formula` | string | Formula name from `formulas/` directory that defines the operational task to execute | -| `context` | string | Human-readable explanation of why this action is needed. Used in PR description | -| `secrets` | array of strings | List of secret names to inject into the execution environment. Only these secrets are passed to the container | - -### Optional Fields - -| Field | Type | Default | Description | -|-------|------|---------|-------------| -| `model` | string | `sonnet` | Override the default Claude model for this action | -| `tools` | array of strings | `[]` | MCP tools to enable during execution | -| `timeout_minutes` | integer | `60` | Maximum execution time in minutes | - -## Secret Names - -Secret names must be defined in `.env.vault.enc` on the ops repo. The vault validates that requested secrets exist in the allowlist before execution. - -Common secret names: -- `CLAWHUB_TOKEN` - Token for ClawHub skill publishing -- `GITHUB_TOKEN` - GitHub API token for repository operations -- `DEPLOY_KEY` - Infrastructure deployment key - -## Validation Rules - -1. **Required fields**: `id`, `formula`, `context`, and `secrets` must be present -2. **Formula validation**: The formula must exist in the `formulas/` directory -3. **Secret validation**: All secrets in the `secrets` array must be in the allowlist -4. **No unknown fields**: The TOML must not contain fields outside the schema -5. **ID uniqueness**: The `id` must be unique across all vault actions - -## Example Files - -See `vault/examples/` for complete examples: -- `webhook-call.toml` - Example of calling an external webhook -- `promote.toml` - Example of promoting a build/artifact -- `publish.toml` - Example of publishing a skill to ClawHub - -## Usage - -Validate a vault action file: - -```bash -./vault/validate.sh vault/actions/.toml -``` - -The validator will check: -- All required fields are present -- Secret names are in the allowlist -- No unknown fields are present -- Formula exists in the formulas directory diff --git a/vault/examples/promote.toml b/vault/examples/promote.toml deleted file mode 100644 index b956c9f..0000000 --- a/vault/examples/promote.toml +++ /dev/null @@ -1,21 +0,0 @@ -# vault/examples/promote.toml -# Example: Promote a build/artifact to production -# -# This vault action demonstrates promoting a built artifact to a -# production environment with proper authentication. - -id = "promote-20260331" -formula = "run-supervisor" -context = "Promote build v1.2.3 to production environment" - -# Secrets to inject for deployment authentication -secrets = ["DEPLOY_KEY", "DOCKER_HUB_TOKEN"] - -# Optional: use larger model for complex deployment logic -model = "sonnet" - -# Optional: enable MCP tools for container operations -tools = ["docker"] - -# Optional: deployments may take longer -timeout_minutes = 45 diff --git a/vault/examples/publish.toml b/vault/examples/publish.toml deleted file mode 100644 index 2373b00..0000000 --- a/vault/examples/publish.toml +++ /dev/null @@ -1,21 +0,0 @@ -# vault/examples/publish.toml -# Example: Publish a skill to ClawHub -# -# This vault action demonstrates publishing a skill to ClawHub -# using the clawhub-publish formula. - -id = "publish-site-20260331" -formula = "run-publish-site" -context = "Publish updated site to production" - -# Secrets to inject (only these get passed to the container) -secrets = ["DEPLOY_KEY"] - -# Optional: use sonnet model -model = "sonnet" - -# Optional: enable MCP tools -tools = [] - -# Optional: 30 minute timeout -timeout_minutes = 30 diff --git a/vault/examples/webhook-call.toml b/vault/examples/webhook-call.toml deleted file mode 100644 index 27b3f25..0000000 --- a/vault/examples/webhook-call.toml +++ /dev/null @@ -1,21 +0,0 @@ -# vault/examples/webhook-call.toml -# Example: Call an external webhook with authentication -# -# This vault action demonstrates calling an external webhook endpoint -# with proper authentication via injected secrets. - -id = "webhook-call-20260331" -formula = "run-rent-a-human" -context = "Notify Slack channel about deployment completion" - -# Secrets to inject (only these get passed to the container) -secrets = ["DEPLOY_KEY"] - -# Optional: use sonnet model for this action -model = "sonnet" - -# Optional: enable MCP tools -tools = [] - -# Optional: 30 minute timeout -timeout_minutes = 30 diff --git a/vault/validate.sh b/vault/validate.sh deleted file mode 100755 index f01ea63..0000000 --- a/vault/validate.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# vault/validate.sh — Validate vault action TOML files -# -# Usage: ./vault/validate.sh -# -# Validates a vault action TOML file according to the schema defined in -# vault/SCHEMA.md. Checks: -# - Required fields are present -# - Secret names are in the allowlist -# - No unknown fields are present -# - Formula exists in formulas/ - -set -euo pipefail - -# Get script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -# Source vault environment -source "$SCRIPT_DIR/vault-env.sh" - -# Get the TOML file to validate -TOML_FILE="${1:-}" - -if [ -z "$TOML_FILE" ]; then - echo "Usage: $0 " >&2 - echo "Example: $0 vault/examples/publish.toml" >&2 - exit 1 -fi - -# Resolve relative paths -if [[ "$TOML_FILE" != /* ]]; then - TOML_FILE="$(cd "$(dirname "$TOML_FILE")" && pwd)/$(basename "$TOML_FILE")" -fi - -# Run validation -if validate_vault_action "$TOML_FILE"; then - echo "VALID: $TOML_FILE" - echo " ID: $VAULT_ACTION_ID" - echo " Formula: $VAULT_ACTION_FORMULA" - echo " Context: $VAULT_ACTION_CONTEXT" - echo " Secrets: $VAULT_ACTION_SECRETS" - exit 0 -else - echo "INVALID: $TOML_FILE" >&2 - exit 1 -fi diff --git a/vault/vault-env.sh b/vault/vault-env.sh index 8e7f7c6..459d214 100644 --- a/vault/vault-env.sh +++ b/vault/vault-env.sh @@ -10,145 +10,3 @@ FORGE_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}" # Vault redesign in progress (PR-based approval workflow) # This file is kept for shared env setup; scripts being replaced by #73 - -# ============================================================================= -# VAULT ACTION VALIDATION -# ============================================================================= - -# Allowed secret names - must match keys in .env.vault.enc -VAULT_ALLOWED_SECRETS="CLAWHUB_TOKEN GITHUB_TOKEN DEPLOY_KEY NPM_TOKEN DOCKER_HUB_TOKEN" - -# Validate a vault action TOML file -# Usage: validate_vault_action -# Returns: 0 if valid, 1 if invalid -# Sets: VAULT_ACTION_ID, VAULT_ACTION_FORMULA, VAULT_ACTION_CONTEXT on success -validate_vault_action() { - local toml_file="$1" - - if [ -z "$toml_file" ]; then - echo "ERROR: No TOML file specified" >&2 - return 1 - fi - - if [ ! -f "$toml_file" ]; then - echo "ERROR: File not found: $toml_file" >&2 - return 1 - fi - - log "Validating vault action: $toml_file" - - # Get script directory for relative path resolution - # FACTORY_ROOT is set by lib/env.sh which is sourced above - local formulas_dir="${FACTORY_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}/formulas" - - # Extract TOML values using grep/sed (basic TOML parsing) - local toml_content - toml_content=$(cat "$toml_file") - - # Extract string values (id, formula, context) - local id formula context - id=$(echo "$toml_content" | grep -E '^id\s*=' | sed -E 's/^id\s*=\s*"(.*)"/\1/' | tr -d '\r') - formula=$(echo "$toml_content" | grep -E '^formula\s*=' | sed -E 's/^formula\s*=\s*"(.*)"/\1/' | tr -d '\r') - context=$(echo "$toml_content" | grep -E '^context\s*=' | sed -E 's/^context\s*=\s*"(.*)"/\1/' | tr -d '\r') - - # Extract secrets array - local secrets_line secrets_array - secrets_line=$(echo "$toml_content" | grep -E '^secrets\s*=' | tr -d '\r') - secrets_array=$(echo "$secrets_line" | sed -E 's/^secrets\s*=\s*\[(.*)\]/\1/' | tr -d '[]"' | tr ',' ' ' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - - # Check for unknown fields (any top-level key not in allowed list) - local unknown_fields - unknown_fields=$(echo "$toml_content" | grep -E '^[a-zA-Z_][a-zA-Z0-9_]*\s*=' | sed -E 's/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=.*/\1/' | sort -u | while read -r field; do - case "$field" in - id|formula|context|secrets|model|tools|timeout_minutes) ;; - *) echo "$field" ;; - esac - done) - - if [ -n "$unknown_fields" ]; then - echo "ERROR: Unknown fields in TOML: $(echo "$unknown_fields" | tr '\n' ', ' | sed 's/,$//')" >&2 - return 1 - fi - - # Validate required fields - if [ -z "$id" ]; then - echo "ERROR: Missing required field: id" >&2 - return 1 - fi - - if [ -z "$formula" ]; then - echo "ERROR: Missing required field: formula" >&2 - return 1 - fi - - if [ -z "$context" ]; then - echo "ERROR: Missing required field: context" >&2 - return 1 - fi - - # Validate formula exists in formulas/ - if [ ! -f "$formulas_dir/${formula}.toml" ]; then - echo "ERROR: Formula not found: $formula" >&2 - return 1 - fi - - # Validate secrets field exists and is not empty - if [ -z "$secrets_line" ]; then - echo "ERROR: Missing required field: secrets" >&2 - return 1 - fi - - # Validate each secret is in the allowlist - for secret in $secrets_array; do - secret=$(echo "$secret" | tr -d '"' | xargs) # trim whitespace and quotes - if [ -n "$secret" ]; then - if ! echo " $VAULT_ALLOWED_SECRETS " | grep -q " $secret "; then - echo "ERROR: Unknown secret (not in allowlist): $secret" >&2 - return 1 - fi - fi - done - - # Validate optional fields if present - # model - if echo "$toml_content" | grep -qE '^model\s*='; then - local model_value - model_value=$(echo "$toml_content" | grep -E '^model\s*=' | sed -E 's/^model\s*=\s*"(.*)"/\1/' | tr -d '\r') - if [ -z "$model_value" ]; then - echo "ERROR: 'model' must be a non-empty string" >&2 - return 1 - fi - fi - - # tools - if echo "$toml_content" | grep -qE '^tools\s*='; then - local tools_line - tools_line=$(echo "$toml_content" | grep -E '^tools\s*=' | tr -d '\r') - if ! echo "$tools_line" | grep -q '\['; then - echo "ERROR: 'tools' must be an array" >&2 - return 1 - fi - fi - - # timeout_minutes - if echo "$toml_content" | grep -qE '^timeout_minutes\s*='; then - local timeout_value - timeout_value=$(echo "$toml_content" | grep -E '^timeout_minutes\s*=' | sed -E 's/^timeout_minutes\s*=\s*([0-9]+)/\1/' | tr -d '\r') - if [ -z "$timeout_value" ] || [ "$timeout_value" -le 0 ] 2>/dev/null; then - echo "ERROR: 'timeout_minutes' must be a positive integer" >&2 - return 1 - fi - fi - - # Export validated values (for use by caller script) - export VAULT_ACTION_ID="$id" - export VAULT_ACTION_FORMULA="$formula" - export VAULT_ACTION_CONTEXT="$context" - export VAULT_ACTION_SECRETS="$secrets_array" - - log "VAULT_ACTION_ID=$VAULT_ACTION_ID" - log "VAULT_ACTION_FORMULA=$VAULT_ACTION_FORMULA" - log "VAULT_ACTION_SECRETS=$VAULT_ACTION_SECRETS" - - return 0 -}