diff --git a/.env.example b/.env.example index e559567..33bc0f6 100644 --- a/.env.example +++ b/.env.example @@ -17,14 +17,23 @@ FORGE_URL=http://localhost:3000 # [CONFIG] local Forgejo instance # ── Auth tokens ─────────────────────────────────────────────────────────── -FORGE_TOKEN= # [SECRET] dev-bot API token +# Each agent has its own Forgejo account and API token (#747). +# Per-agent tokens fall back to FORGE_TOKEN if not set. +FORGE_TOKEN= # [SECRET] dev-bot API token (default for all agents) FORGE_REVIEW_TOKEN= # [SECRET] review-bot API token -FORGE_BOT_USERNAMES= # [CONFIG] comma-separated bot usernames +FORGE_PLANNER_TOKEN= # [SECRET] planner-bot API token +FORGE_GARDENER_TOKEN= # [SECRET] gardener-bot API token +FORGE_VAULT_TOKEN= # [SECRET] vault-bot API token +FORGE_SUPERVISOR_TOKEN= # [SECRET] supervisor-bot API token +FORGE_PREDICTOR_TOKEN= # [SECRET] predictor-bot API token +FORGE_ACTION_TOKEN= # [SECRET] action-bot API token +FORGE_BOT_USERNAMES=dev-bot,review-bot,planner-bot,gardener-bot,vault-bot,supervisor-bot,predictor-bot,action-bot # ── Backwards compatibility ─────────────────────────────────────────────── # If CODEBERG_TOKEN is set but FORGE_TOKEN is not, env.sh falls back to # CODEBERG_TOKEN automatically (same for REVIEW_BOT_TOKEN, CODEBERG_REPO, # CODEBERG_BOT_USERNAMES). No action needed for existing deployments. +# Per-agent tokens default to FORGE_TOKEN when unset (single-token setups). # ── Woodpecker CI ───────────────────────────────────────────────────────── WOODPECKER_TOKEN= # [SECRET] Woodpecker API token diff --git a/action/action-agent.sh b/action/action-agent.sh index 1afbfe1..e6e55ff 100755 --- a/action/action-agent.sh +++ b/action/action-agent.sh @@ -27,6 +27,8 @@ ISSUE="${1:?Usage: action-agent.sh [project.toml]}" export PROJECT_TOML="${2:-${PROJECT_TOML:-}}" source "$(dirname "$0")/../lib/env.sh" +# Use action-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_ACTION_TOKEN:-${FORGE_TOKEN}}" source "$(dirname "$0")/../lib/ci-helpers.sh" source "$(dirname "$0")/../lib/agent-session.sh" source "$(dirname "$0")/../lib/formula-session.sh" diff --git a/action/action-poll.sh b/action/action-poll.sh index 100d854..ba0c4ec 100755 --- a/action/action-poll.sh +++ b/action/action-poll.sh @@ -13,6 +13,8 @@ set -euo pipefail export PROJECT_TOML="${1:-}" source "$(dirname "$0")/../lib/env.sh" +# Use action-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_ACTION_TOKEN:-${FORGE_TOKEN}}" # shellcheck source=../lib/guard.sh source "$(dirname "$0")/../lib/guard.sh" check_active action diff --git a/bin/disinto b/bin/disinto index 4ec6293..aa4b50c 100755 --- a/bin/disinto +++ b/bin/disinto @@ -439,10 +439,25 @@ setup_forge() { fi # Create bot users and tokens - local dev_token="" review_token="" - for bot_user in dev-bot review-bot; do - local bot_pass + # Each agent gets its own Forgejo account for identity and audit trail (#747). + # Map: bot-username -> env-var-name for the token + local -A bot_token_vars=( + [dev-bot]="FORGE_TOKEN" + [review-bot]="FORGE_REVIEW_TOKEN" + [planner-bot]="FORGE_PLANNER_TOKEN" + [gardener-bot]="FORGE_GARDENER_TOKEN" + [vault-bot]="FORGE_VAULT_TOKEN" + [supervisor-bot]="FORGE_SUPERVISOR_TOKEN" + [predictor-bot]="FORGE_PREDICTOR_TOKEN" + [action-bot]="FORGE_ACTION_TOKEN" + ) + + local env_file="${FACTORY_ROOT}/.env" + local bot_user bot_pass token token_var + + for bot_user in dev-bot review-bot planner-bot gardener-bot vault-bot supervisor-bot predictor-bot action-bot; do bot_pass="bot-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)" + token_var="${bot_token_vars[$bot_user]}" if ! curl -sf --max-time 5 \ -H "Authorization: token ${admin_token}" \ @@ -476,7 +491,6 @@ setup_forge() { # Generate token via API (basic auth as the bot user — Forgejo requires # basic auth on POST /users/{username}/tokens, token auth is rejected) - local token token=$(curl -sf -X POST \ -u "${bot_user}:${bot_pass}" \ -H "Content-Type: application/json" \ @@ -499,41 +513,23 @@ setup_forge() { exit 1 fi - if [ "$bot_user" = "dev-bot" ]; then - dev_token="$token" + # Store token in .env under the per-agent variable name + if grep -q "^${token_var}=" "$env_file" 2>/dev/null; then + sed -i "s|^${token_var}=.*|${token_var}=${token}|" "$env_file" else - review_token="$token" + printf '%s=%s\n' "$token_var" "$token" >> "$env_file" + fi + export "${token_var}=${token}" + echo " ${bot_user} token saved (${token_var})" + + # Backwards-compat aliases for dev-bot and review-bot + if [ "$bot_user" = "dev-bot" ]; then + export CODEBERG_TOKEN="$token" + elif [ "$bot_user" = "review-bot" ]; then + export REVIEW_BOT_TOKEN="$token" fi done - # Store tokens in .env - local env_file="${FACTORY_ROOT}/.env" - if [ -n "$dev_token" ]; then - if grep -q '^FORGE_TOKEN=' "$env_file" 2>/dev/null; then - sed -i "s|^FORGE_TOKEN=.*|FORGE_TOKEN=${dev_token}|" "$env_file" - elif grep -q '^CODEBERG_TOKEN=' "$env_file" 2>/dev/null; then - sed -i "s|^CODEBERG_TOKEN=.*|FORGE_TOKEN=${dev_token}|" "$env_file" - else - printf '\nFORGE_TOKEN=%s\n' "$dev_token" >> "$env_file" - fi - export FORGE_TOKEN="$dev_token" - export CODEBERG_TOKEN="$dev_token" - echo " dev-bot token saved" - fi - - if [ -n "$review_token" ]; then - if grep -q '^FORGE_REVIEW_TOKEN=' "$env_file" 2>/dev/null; then - sed -i "s|^FORGE_REVIEW_TOKEN=.*|FORGE_REVIEW_TOKEN=${review_token}|" "$env_file" - elif grep -q '^REVIEW_BOT_TOKEN=' "$env_file" 2>/dev/null; then - sed -i "s|^REVIEW_BOT_TOKEN=.*|FORGE_REVIEW_TOKEN=${review_token}|" "$env_file" - else - printf 'FORGE_REVIEW_TOKEN=%s\n' "$review_token" >> "$env_file" - fi - export FORGE_REVIEW_TOKEN="$review_token" - export REVIEW_BOT_TOKEN="$review_token" - echo " review-bot token saved" - fi - # Store FORGE_URL in .env if not already present if ! grep -q '^FORGE_URL=' "$env_file" 2>/dev/null; then printf 'FORGE_URL=%s\n' "$forge_url" >> "$env_file" @@ -569,8 +565,8 @@ setup_forge() { -d "{\"name\":\"${repo_name}\",\"auto_init\":false,\"default_branch\":\"main\"}" >/dev/null 2>&1 || true fi - # Add bot users as collaborators - for bot_user in dev-bot review-bot; do + # Add all bot users as collaborators + for bot_user in dev-bot review-bot planner-bot gardener-bot vault-bot supervisor-bot predictor-bot action-bot; do curl -sf -X PUT \ -H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \ -H "Content-Type: application/json" \ diff --git a/gardener/gardener-run.sh b/gardener/gardener-run.sh index 58b9b1f..9b730b4 100755 --- a/gardener/gardener-run.sh +++ b/gardener/gardener-run.sh @@ -20,6 +20,8 @@ FACTORY_ROOT="$(dirname "$SCRIPT_DIR")" export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" # shellcheck source=../lib/env.sh source "$FACTORY_ROOT/lib/env.sh" +# Use gardener-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_GARDENER_TOKEN:-${FORGE_TOKEN}}" # shellcheck source=../lib/agent-session.sh source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh diff --git a/lib/env.sh b/lib/env.sh index 0d0aeb6..947c1af 100755 --- a/lib/env.sh +++ b/lib/env.sh @@ -53,8 +53,17 @@ export CODEBERG_TOKEN="${FORGE_TOKEN}" # backwards compat export FORGE_REVIEW_TOKEN="${FORGE_REVIEW_TOKEN:-${REVIEW_BOT_TOKEN:-}}" export REVIEW_BOT_TOKEN="${FORGE_REVIEW_TOKEN}" # backwards compat +# Per-agent tokens (#747): each agent gets its own Forgejo identity. +# Falls back to FORGE_TOKEN for backwards compat with single-token setups. +export FORGE_PLANNER_TOKEN="${FORGE_PLANNER_TOKEN:-${FORGE_TOKEN}}" +export FORGE_GARDENER_TOKEN="${FORGE_GARDENER_TOKEN:-${FORGE_TOKEN}}" +export FORGE_VAULT_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}" +export FORGE_SUPERVISOR_TOKEN="${FORGE_SUPERVISOR_TOKEN:-${FORGE_TOKEN}}" +export FORGE_PREDICTOR_TOKEN="${FORGE_PREDICTOR_TOKEN:-${FORGE_TOKEN}}" +export FORGE_ACTION_TOKEN="${FORGE_ACTION_TOKEN:-${FORGE_TOKEN}}" + # Bot usernames filter: FORGE_BOT_USERNAMES > legacy CODEBERG_BOT_USERNAMES -export FORGE_BOT_USERNAMES="${FORGE_BOT_USERNAMES:-${CODEBERG_BOT_USERNAMES:-}}" +export FORGE_BOT_USERNAMES="${FORGE_BOT_USERNAMES:-${CODEBERG_BOT_USERNAMES:-dev-bot,review-bot,planner-bot,gardener-bot,vault-bot,supervisor-bot,predictor-bot,action-bot}}" export CODEBERG_BOT_USERNAMES="${FORGE_BOT_USERNAMES}" # backwards compat # Project config (FORGE_* preferred, CODEBERG_* fallback) diff --git a/planner/planner-run.sh b/planner/planner-run.sh index c529aa9..ab7d987 100755 --- a/planner/planner-run.sh +++ b/planner/planner-run.sh @@ -18,6 +18,8 @@ FACTORY_ROOT="$(dirname "$SCRIPT_DIR")" export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" # shellcheck source=../lib/env.sh source "$FACTORY_ROOT/lib/env.sh" +# Use planner-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_PLANNER_TOKEN:-${FORGE_TOKEN}}" # shellcheck source=../lib/agent-session.sh source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh diff --git a/predictor/predictor-run.sh b/predictor/predictor-run.sh index 989576d..504e377 100755 --- a/predictor/predictor-run.sh +++ b/predictor/predictor-run.sh @@ -20,6 +20,8 @@ FACTORY_ROOT="$(dirname "$SCRIPT_DIR")" export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" # shellcheck source=../lib/env.sh source "$FACTORY_ROOT/lib/env.sh" +# Use predictor-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_PREDICTOR_TOKEN:-${FORGE_TOKEN}}" # shellcheck source=../lib/agent-session.sh source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh diff --git a/supervisor/supervisor-run.sh b/supervisor/supervisor-run.sh index b4ac052..0bdcd5d 100755 --- a/supervisor/supervisor-run.sh +++ b/supervisor/supervisor-run.sh @@ -24,6 +24,8 @@ FACTORY_ROOT="$(dirname "$SCRIPT_DIR")" export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}" # shellcheck source=../lib/env.sh source "$FACTORY_ROOT/lib/env.sh" +# Use supervisor-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_SUPERVISOR_TOKEN:-${FORGE_TOKEN}}" # shellcheck source=../lib/agent-session.sh source "$FACTORY_ROOT/lib/agent-session.sh" # shellcheck source=../lib/formula-session.sh diff --git a/vault/vault-agent.sh b/vault/vault-agent.sh index 202474d..eab384f 100755 --- a/vault/vault-agent.sh +++ b/vault/vault-agent.sh @@ -12,6 +12,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "${SCRIPT_DIR}/../lib/env.sh" +# Use vault-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}" VAULT_DIR="${FACTORY_ROOT}/vault" PROMPT_FILE="${VAULT_DIR}/PROMPT.md" diff --git a/vault/vault-fire.sh b/vault/vault-fire.sh index 18d3d90..515fba9 100755 --- a/vault/vault-fire.sh +++ b/vault/vault-fire.sh @@ -14,6 +14,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "${SCRIPT_DIR}/../lib/env.sh" +# Use vault-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}" VAULT_DIR="${FACTORY_ROOT}/vault" LOCKS_DIR="${VAULT_DIR}/.locks" diff --git a/vault/vault-poll.sh b/vault/vault-poll.sh index 5dbf06c..736e897 100755 --- a/vault/vault-poll.sh +++ b/vault/vault-poll.sh @@ -20,6 +20,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "${SCRIPT_DIR}/../lib/env.sh" +# Use vault-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}" LOGFILE="${FACTORY_ROOT}/vault/vault.log" STATUSFILE="/tmp/vault-status" diff --git a/vault/vault-reject.sh b/vault/vault-reject.sh index 95598bc..821094c 100755 --- a/vault/vault-reject.sh +++ b/vault/vault-reject.sh @@ -7,6 +7,8 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "${SCRIPT_DIR}/../lib/env.sh" +# Use vault-bot's own Forgejo identity (#747) +FORGE_TOKEN="${FORGE_VAULT_TOKEN:-${FORGE_TOKEN}}" VAULT_DIR="${FACTORY_ROOT}/vault" LOGFILE="${VAULT_DIR}/vault.log"