Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .agents/project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"claude": "dispatch: claude",
"codex": "dispatch: codex",
"hermes": "dispatch: hermes",
"jcode": "dispatch: jcode",
"kimi": "dispatch: kimi",
"zcode": "dispatch: zcode"
}
},
Expand Down
139 changes: 128 additions & 11 deletions .github/workflows/agent-policy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ permissions:
contents: read
issues: read
pull-requests: read
statuses: read
packages: read

jobs:
Expand All @@ -26,19 +27,101 @@ jobs:
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1
- name: Load pinned policy runtime
# cosign-installer v4 shells out to envsubst to expand its install path.
# The compatibility workflow must also run on the contract's ci-linux-x64
# pool, where envsubst is intentionally not required. Download the same
# publisher-compatible cosign v3 binary directly and verify its pinned
# SHA-256 so lifecycle enforcement does not depend on an undeclared tool.
- name: Install signature verifier
env:
POLICY_ARTIFACT: ${{ vars.AGENT_POLICY_ARTIFACT }}
COSIGN_VERSION: v3.0.6
COSIGN_SHA256_AMD64: c956e5dfcac53d52bcf058360d579472f0c1d2d9b69f55209e256fe7783f4c74
COSIGN_SHA256_ARM64: bedac92e8c3729864e13d4a17048007cfafa79d5deca993a43a90ffe018ef2b8
run: |
set -euo pipefail
case "$(uname -m)" in
x86_64) asset=cosign-linux-amd64; expected="$COSIGN_SHA256_AMD64" ;;
aarch64 | arm64) asset=cosign-linux-arm64; expected="$COSIGN_SHA256_ARM64" ;;
*) echo "unsupported runner architecture: $(uname -m)" >&2; exit 1 ;;
esac
install_dir="$RUNNER_TEMP/hv-cosign"
mkdir -p "$install_dir"
curl -fsSL -o "$install_dir/cosign" \
"https://github.com/sigstore/cosign/releases/download/$COSIGN_VERSION/$asset"
actual=$(python3 - "$install_dir/cosign" <<'PY'
import hashlib
import pathlib
import sys

print(hashlib.sha256(pathlib.Path(sys.argv[1]).read_bytes()).hexdigest())
PY
)
test "$actual" = "$expected" || {
echo "cosign checksum mismatch: expected $expected, got $actual" >&2
exit 1
}
chmod +x "$install_dir/cosign"
echo "$install_dir" >> "$GITHUB_PATH"
"$install_dir/cosign" version
- name: Select source-owned policy channel
shell: bash
env:
GH_TOKEN: ${{ github.token }}
TARGET_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
source_repository=$(gh api repos/happyvertical/.github --jq '.id')
test "$source_repository" = 1129270614 || {
echo "trusted policy source repository id is $source_repository, expected 1129270614" >&2
exit 1
}
channel_state="$RUNNER_TEMP/hv-agent-policy-channels.json"
gh api 'repos/happyvertical/.github/contents/.github/agent-policy-channels.json?ref=main' \
--jq '.content' | base64 --decode > "$channel_state"
protected_selector="$RUNNER_TEMP/hv-agent-policy-channel-select"
gh api 'repos/happyvertical/.github/contents/scripts/hv-agent-policy-channel-select?ref=main' \
--jq '.content' | base64 --decode > "$protected_selector"
chmod +x "$protected_selector"
selected_lock="$RUNNER_TEMP/hv-agent-policy-selected-lock.json"
target_repository_id=$(gh api "repos/$TARGET_REPOSITORY" --jq '.node_id')
channel=$(python3 "$protected_selector" \
"$channel_state" "$selected_lock" "$target_repository_id")
artifact=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["artifact"])' "$selected_lock")
{
echo "HV_POLICY_ARTIFACT=$artifact"
echo "HV_POLICY_CHANNEL=$channel"
echo "HV_POLICY_LOCK=$selected_lock"
} >> "$GITHUB_ENV"
- name: Load selected policy runtime
env:
POLICY_ARTIFACT: ${{ env.HV_POLICY_ARTIFACT }}
POLICY_PUBLISHER_IDENTITY: ${{ vars.AGENT_POLICY_PUBLISHER_IDENTITY }}
run: |
case "$POLICY_ARTIFACT" in
ghcr.io/*@sha256:*) ;;
*) echo 'AGENT_POLICY_ARTIFACT must be an immutable GHCR digest' >&2; exit 1 ;;
ghcr.io/happyvertical/agent-policy@sha256:*) ;;
*) echo 'source-owned policy channel must select the trusted HappyVertical policy image by digest' >&2; exit 1 ;;
esac
expected_publisher_identity_sha256=86a917ce537a9e923ce02103068c76eb72629247ab584f7d368ac238a0209857
publisher_identity_sha256=$(python3 -c 'import hashlib, os; print(hashlib.sha256(os.environ["POLICY_PUBLISHER_IDENTITY"].encode()).hexdigest())')
test "$publisher_identity_sha256" = "$expected_publisher_identity_sha256" || {
echo 'AGENT_POLICY_PUBLISHER_IDENTITY must match the exact protected-main publisher identity' >&2
exit 1
}
publisher_workflow="${POLICY_PUBLISHER_IDENTITY%@refs/heads/main}"
test "$publisher_workflow" != "$POLICY_PUBLISHER_IDENTITY" || {
echo 'AGENT_POLICY_PUBLISHER_IDENTITY must name the protected-main publisher workflow' >&2
exit 1
}
publisher_workflow_regexp=$(printf '%s' "$publisher_workflow" | sed 's/[][\.*^()+?{}|$]/\\&/g')
cosign verify "$POLICY_ARTIFACT" \
--certificate-identity-regexp "^${publisher_workflow_regexp}@refs/(heads/main|tags/agent-policy-v[0-9]+\.[0-9]+\.[0-9]+)$" \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com'
policy_download_dir="$RUNNER_TEMP/hv-policy-download"
policy_dir="$RUNNER_TEMP/hv-policy"
rm -rf "$policy_dir"
mkdir -p "$policy_dir"
oras pull "$POLICY_ARTIFACT" -o "$policy_dir"
archive="$policy_dir/agent-policy.tar.gz"
rm -rf "$policy_download_dir" "$policy_dir"
mkdir -p "$policy_download_dir" "$policy_dir"
oras pull "$POLICY_ARTIFACT" -o "$policy_download_dir"
archive="$policy_download_dir/agent-policy.tar.gz"
test -f "$archive" || { echo 'policy artifact is missing agent-policy.tar.gz' >&2; exit 1; }
python3 - "$archive" <<'PY'
import pathlib
Expand All @@ -52,16 +135,50 @@ jobs:
raise SystemExit(f"unsafe policy archive member: {member.name}")
PY
tar -xzf "$archive" -C "$policy_dir"
identity=$(python3 "$policy_dir/scripts/verify-policy-artifact" "$policy_dir")
python3 - "$HV_POLICY_LOCK" "$policy_dir/policy-artifact.json" "$POLICY_ARTIFACT" <<'PY'
import json
import sys

lock = json.load(open(sys.argv[1], encoding="utf-8"))
manifest = json.load(open(sys.argv[2], encoding="utf-8"))
expected = {
"artifact": sys.argv[3],
"generation": manifest.get("generation"),
"policy_revision": manifest.get("policy_revision"),
"source_commit": manifest.get("source_commit"),
"source_tree_sha256": manifest.get("source_tree_sha256"),
}
for field, value in expected.items():
if lock.get(field) != value:
raise SystemExit(
f"selected policy lock {field} does not match signed artifact manifest"
)
PY
echo "Loaded $identity from $HV_POLICY_CHANNEL channel $POLICY_ARTIFACT"
echo "HV_POLICY_DIR=$policy_dir" >> "$GITHUB_ENV"
echo "HV_POLICY_IDENTITY=$identity" >> "$GITHUB_ENV"
- name: Validate lifecycle
shell: bash
env:
GH_TOKEN: ${{ github.token }}
POLICY_ARTIFACT: ${{ env.HV_POLICY_ARTIFACT }}
EVENT_NAME: ${{ github.event_name }}
EVENT_ACTION: ${{ github.event.action }}
PR_NUMBER: ${{ inputs.pr_number || github.event.pull_request.number }}
MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }}
MERGE_GROUP_HEAD_REF: ${{ github.event.merge_group.head_ref }}
run: |
"$HV_POLICY_DIR/scripts/hv-agent-lifecycle-prs" | while IFS= read -r number; do
python3 "$HV_POLICY_DIR/scripts/hv-agent" check-pr "$number"
done
set -euo pipefail
if test "$EVENT_NAME" = workflow_dispatch && test "${PR_NUMBER:-0}" = 0; then
python3 "$HV_POLICY_DIR/scripts/hv-agent" audit .
exit
fi
numbers_file="$RUNNER_TEMP/hv-agent-lifecycle-prs"
"$HV_POLICY_DIR/scripts/hv-agent-lifecycle-prs" > "$numbers_file"
while IFS= read -r number; do
if ! python3 "$HV_POLICY_DIR/scripts/hv-agent" check-pr "$number"; then
echo "ERROR $HV_POLICY_IDENTITY from $POLICY_ARTIFACT rejected PR #$number; correct canonical claim/PR state, then rerun the organization-required workflow execution; a repository-local lifecycle-only dispatch is not protected merge authority and draft toggles are invalid" >&2
exit 1
fi
done < "$numbers_file"
Loading