Relay CI Stack Pattern
The relay-eval-ci IAM role is the source of truth for the Relay bot’s CI in
the genai accounts. It is the one stack in hungry-hub-iam that lives
outside the home accounts. This page is the single description of what it
grants, how its secrets are wired, and how the live (hand-built) role was
reconciled into Terraform.
- Stack:
hungry-hub-iam/stacks/relay-ci/ - Role name:
relay-eval-ci - Accounts:
genai-sandbox 965444437277/genai-prod 512438352490 - Region:
us-east-1(matches the genai Bedrock/AgentCore region) - Consumed by:
hh-relayGitHub Actions (RELAY_EVAL_ROLE_ARN/RELAY_EVAL_ROLE_ARN_DEV)
Why it exists
hh-relay runs three GitHub Actions pipelines that need AWS access:
- nightly eval (Strands agent) — Bedrock model inference + AgentCore eval.
- release-notes scheduler — DynamoDB queues, Bedrock model inference.
- kb-distill scheduler — DynamoDB queues, KB retrieve, S3 KB sync, Bedrock ingestion.
All three assume the single relay-eval-ci role, so its inline policy is the
superset of their needs.
Trust subject
Pinned to the main branch of hh-relay (see
OIDC Subject Pinning Patterns):
repo:hungryhub-team/hh-relay:ref:refs/heads/main
The live hand-built role trusted repo:.../hh-relay:* (any ref/tag/env). The
reconciled stack pins it.
What relay-eval-ci grants
The inline policy relay-eval-ci-policy (in stacks/relay-ci/main.tf):
| Sid | Actions | Resource scope |
|---|---|---|
BedrockInvoke | bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream | foundation-model/* + inference-profile/* in this account |
KnowledgeBaseRetrieve | bedrock:Retrieve | knowledge-base/* in this account |
AgentCore | bedrock-agentcore:InvokeAgentRuntime, bedrock-agentcore:GetAgentRuntime, bedrock-agentcore:Evaluate, bedrock-agentcore:GetEvaluation, bedrock-agentcore:ListEvaluations | * (these APIs do not support resource-level scoping) |
CloudWatchLogs | logs:GetQueryResults, logs:StartQuery, logs:StopQuery | /ecs/relay-* log groups |
SecretsManagerRelay | secretsmanager:GetSecretValue | secret:relay/* |
DynamoDBRelayQueues | dynamodb:GetItem, dynamodb:PutItem, dynamodb:UpdateItem, dynamodb:DeleteItem, dynamodb:Query, dynamodb:Scan, dynamodb:BatchWriteItem | relay-release-drafts-* and relay-kb-distill-* tables (+ indexes) |
S3KBSync | s3:PutObject, s3:DeleteObject, s3:GetObject, s3:ListBucket, s3:GetBucketLocation | relay-kb-docs-<env>-<account> bucket |
BedrockAgentIngestion | bedrock-agent:StartIngestionJob, bedrock-agent:GetIngestionJob, bedrock-agent:ListIngestionJobs | knowledge-base/* |
STSCallerIdentity | sts:GetCallerIdentity | * |
Two intentional corrections vs. the original hand-built role:
- KB retrieve uses
bedrock:Retrieve(the live role hadbedrock-agent-runtime:Retrieve, which is not a valid IAM action prefix). - AgentCore is narrowed from
bedrock-agentcore:*to the specific actions above.
Secret wiring
The relay-ci stack is dispatched through
hungry-hub-iam/.github/workflows/terraform-stacks.yml, which reads the
_RELAY_CI_* secret overrides (instead of the default home-account secrets)
because the role lives in the genai accounts:
| GitHub secret | Feeds |
|---|---|
OIDC_ROLE_ARN_RELAY_CI_{PROD,DEV} | The OIDC role the workflow assumes to manage the stack |
S3_BUCKET_RELAY_CI_{PROD,DEV} | Terraform state backend bucket |
DYNAMO_TABLE_RELAY_CI_{PROD,DEV} | Terraform state lock table |
AWS_ACCOUNT_ID_RELAY_CI_{PROD,DEV} | 512438352490 / 965444437277 |
OIDC_PROVIDER_ARN_RELAY_CI_{PROD,DEV} | The genai-account OIDC provider ARN |
On the consumer side, hh-relay’s workflows read the role ARN from the
RELAY_EVAL_ROLE_ARN (prod) / RELAY_EVAL_ROLE_ARN_DEV (dev) GitHub secrets.
Reconcile: from hand-built to Terraform-managed
The stack was historically never applied — the live role was hand-built in both
genai accounts, and its kb-distill/Bedrock permissions were added by hand
(aws iam put-role-policy) during the hh-relay#199 rollout. Until imported, the
live role is drift: anyone who applied the old un-reconciled stack would
strip those hand-added permissions.
The import + apply runbook (pin the trust subject, fix bedrock:Retrieve,
narrow AgentCore) lives in
hungry-hub-iam/docs/RELAY_CI_RECONCILE.md. Summary:
cd stacks/relay-ci
# terraform init with the RELAY_CI backend bucket/table
terraform import 'module.relay_eval_role.aws_iam_role.this' relay-eval-ci
terraform import 'aws_iam_role_policy.relay_eval' 'relay-eval-ci:relay-eval-ci-policy'
terraform plan # review the corrective diff
terraform apply # make the corrections + Terraform-manage the role
Related
- OIDC Subject Pinning Patterns
- Relay Bot — Architecture and Operations
- hungryhub-iam — Architecture and Operations
- Import runbook:
hungry-hub-iam/docs/RELAY_CI_RECONCILE.md