-
Notifications
You must be signed in to change notification settings - Fork 9
feat(kirocrew): opt-in Telegram channel wiring during install (do not merge yet) #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
497dd01
4c9d1fd
818c0f8
79c93b6
9bce967
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,8 @@ Metadata: | |
| - KiroFromSecret | ||
| - TelegramBotTokenSecret | ||
| - TelegramUser | ||
| - KirocrewTgBotTokenSecret | ||
| - KirocrewTgUserId | ||
| - Primary | ||
| - DailyDriver | ||
| - CodexModel | ||
|
|
@@ -272,6 +274,17 @@ Parameters: | |
| Default: '' | ||
| Description: "Telegram username for bot pairing (roundhouse pack only, without @ prefix)." | ||
|
|
||
| KirocrewTgBotTokenSecret: | ||
| Type: String | ||
| Default: '' | ||
| Description: "AWS Secrets Manager secret id/arn holding the KiroCrew Telegram bot token (kirocrew pack only). The instance resolves the secret at bootstrap time via its IAM role — the token itself is never embedded in UserData." | ||
|
|
||
| KirocrewTgUserId: | ||
| Type: String | ||
| Default: '' | ||
| Description: "KiroCrew Telegram numeric user ID (kirocrew pack only). Opt-in wiring. Digits only. Not sensitive (same class as TelegramUser)." | ||
|
|
||
|
|
||
| Primary: | ||
| Type: String | ||
| Default: openclaw | ||
|
|
@@ -811,6 +824,32 @@ Resources: | |
| - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore | ||
| - !If [IsBuilder, 'arn:aws:iam::aws:policy/AdministratorAccess', !Ref 'AWS::NoValue'] | ||
| - !If [IsAccountAssistant, 'arn:aws:iam::aws:policy/ReadOnlyAccess', !Ref 'AWS::NoValue'] | ||
| Policies: | ||
| # KiroCrew Telegram bot-token secret read access. | ||
| # Inline on the role (not a sibling AWS::IAM::Policy) so it is | ||
| # attached at role creation — the Instance's DependsOn InstanceProfile | ||
| # is then sufficient and there is no race between UserData starting | ||
| # and the permission propagating (Codex P1 on 818c0f8). | ||
| # | ||
| # Installer always passes a plain secret name in the form | ||
| # /lowkey/<env>/kirocrew-telegram-bot-token, so the Resource is a | ||
| # single well-formed ARN built via !Sub — never mix a plain name | ||
| # with a pre-built ARN (Codex P1 on 818c0f8: doing both produced | ||
| # arn:...:secret:arn:aws:... which is malformed). | ||
| # | ||
| # Empty parameter → empty policy statement action; harmless because | ||
| # the resource pattern won't match anything, but the statement is | ||
| # present on the role so no follow-up IAM propagation is required. | ||
| - PolicyName: !Sub '${EnvironmentName}-kirocrew-tg-secret-read' | ||
| PolicyDocument: | ||
| Version: '2012-10-17' | ||
| Statement: | ||
| - Sid: ReadKirocrewTelegramBotToken | ||
| Effect: Allow | ||
| Action: | ||
| - secretsmanager:GetSecretValue | ||
| - secretsmanager:DescribeSecret | ||
| Resource: !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${KirocrewTgBotTokenSecret}*' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| Tags: | ||
| - Key: Name | ||
| Value: !Sub '${EnvironmentName}-role' | ||
|
|
@@ -881,7 +920,13 @@ Resources: | |
| Action: | ||
| - secretsmanager:GetSecretValue | ||
| - secretsmanager:GetResourcePolicy | ||
| Resource: '*' | ||
| NotResource: | ||
| # Bootstrap needs to read the KiroCrew Telegram bot-token secret | ||
| # (installer-managed, plain name /lowkey/<env>/kirocrew-telegram-bot-token) | ||
| # even under account_assistant. Explicit Deny always wins over the | ||
| # matching Allow on InstanceRole, so exempt this one ARN pattern | ||
| # via NotResource (Codex P1 on 818c0f8). | ||
| - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${KirocrewTgBotTokenSecret}*' | ||
| - Sid: DenyS3ObjectAccess | ||
| Effect: Deny | ||
| Action: | ||
|
|
@@ -1849,6 +1894,13 @@ Resources: | |
| # Export all params as env vars for the bootstrap script | ||
| export ACCT_ID="${AWS::AccountId}" | ||
| export REGION="${AWS::Region}" | ||
| # STACK_REGION is the CFN stack / deployment region — preserved | ||
| # separately from BEDROCK_REGION and from bootstrap.sh's --region | ||
| # (which is repurposed for the Bedrock region on Bedrock-model deploys). | ||
| # Used by resolvers that must hit AWS APIs in the same region where | ||
| # the installer created resources (e.g. Secrets Manager for the | ||
| # KiroCrew Telegram bot token). Codex P1 on 79c93b6. | ||
| export STACK_REGION="${AWS::Region}" | ||
| export STACK_NAME="${AWS::StackName}" | ||
| export DEFAULT_MODEL="${DefaultModel}" | ||
| export BEDROCK_REGION="${BedrockRegion}" | ||
|
|
@@ -1861,6 +1913,8 @@ Resources: | |
| export KIRO_FROM_SECRET="${KiroFromSecret}" | ||
| export TELEGRAM_BOT_TOKEN_SECRET="${TelegramBotTokenSecret}" | ||
| export TELEGRAM_USER="${TelegramUser}" | ||
| export KIROCREW_TG_BOT_TOKEN_SECRET="${KirocrewTgBotTokenSecret}" | ||
| export KIROCREW_TG_USER_ID="${KirocrewTgUserId}" | ||
| export PACK_NAME="${PackName}" | ||
| export PROFILE_NAME="${ProfileName}" | ||
| # Publish failure to SSM and signal CFN on any error | ||
|
|
@@ -1905,6 +1959,8 @@ Resources: | |
| --kiro-from-secret "$KIRO_FROM_SECRET" \ | ||
| --telegram-bot-token-secret "$TELEGRAM_BOT_TOKEN_SECRET" \ | ||
| --telegram-user "$TELEGRAM_USER" \ | ||
| --kirocrew-tg-bot-token-secret "$KIROCREW_TG_BOT_TOKEN_SECRET" \ | ||
| --kirocrew-tg-user-id "$KIROCREW_TG_USER_ID" \ | ||
| --primary "${Primary}" \ | ||
| --daily-driver "${DailyDriver}" \ | ||
| --codex-model "${CodexModel}" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2390,7 +2390,7 @@ collect_security_config() { | |
| # Parameter source-of-truth: single mapping for CFN Console and CFN CLI | ||
| # ============================================================================ | ||
| # ⚠ KEEP THESE TWO ARRAYS IN SYNC — same order, same count | ||
| PARAM_CFN_NAMES=(EnvironmentName PackName ProfileName InstanceType DefaultModel ModelMode BedrockRegion LokiWatermark EnableBedrockForm EnableSecurityHub EnableGuardDuty EnableInspector EnableAccessAnalyzer EnableConfigRecorder ExistingVpcId ExistingSubnetId ExistingSubnetId2 RepoBranch KiroFromSecret TelegramBotTokenSecret TelegramUser Primary DailyDriver CodexModel EnableWebUIAuth WebUIAdminEmail EdgeLambdaVersionArn EdgeConfigSecretName EdgeConfigSecretArn SigningKeySecretName SigningKeySecretArn) | ||
| PARAM_CFN_NAMES=(EnvironmentName PackName ProfileName InstanceType DefaultModel ModelMode BedrockRegion LokiWatermark EnableBedrockForm EnableSecurityHub EnableGuardDuty EnableInspector EnableAccessAnalyzer EnableConfigRecorder ExistingVpcId ExistingSubnetId ExistingSubnetId2 RepoBranch KiroFromSecret TelegramBotTokenSecret TelegramUser Primary DailyDriver CodexModel EnableWebUIAuth WebUIAdminEmail EdgeLambdaVersionArn EdgeConfigSecretName EdgeConfigSecretArn SigningKeySecretName SigningKeySecretArn KirocrewTgBotTokenSecret KirocrewTgUserId) | ||
| PARAM_VALUES=() # populated by build_deploy_params() | ||
|
|
||
| # Per-pack default model (passed to CFN DefaultModel / bootstrap.sh --model). | ||
|
|
@@ -2457,6 +2457,8 @@ build_deploy_params() { | |
| "${EDGE_CONFIG_SECRET_ARN:-}" | ||
| "${SIGNING_KEY_SECRET_NAME:-}" | ||
| "${SIGNING_KEY_SECRET_ARN:-}" | ||
| "${KIROCREW_TG_BOT_TOKEN_SECRET:-}" | ||
| "${KIROCREW_TG_USER_ID:-}" | ||
| ) | ||
| # Validate parallel arrays are in sync | ||
| [[ ${#PARAM_CFN_NAMES[@]} -eq ${#PARAM_VALUES[@]} ]] \ | ||
|
|
@@ -3364,6 +3366,109 @@ run_config_and_review() { | |
| build_deploy_params | ||
| fi | ||
|
|
||
| # Pack-specific: kirocrew Telegram channel setup | ||
| # Prompts the operator for a bot token + numeric user ID, both with retry | ||
| # loops that accept 'skip' or empty input to bail. Values flow to the pack | ||
| # via PACK_CONFIG (bootstrap.sh -> pack_config_get), which then writes: | ||
| # - bot token -> ~/.kiro/crew/.env (as TELEGRAM_BOT_TOKEN=...) | ||
| # - user ID -> ~/.kiro/crew/config.json ('telegram.allowed_user_ids') | ||
| # - enabled -> ~/.kiro/crew/config.json ('telegram.enabled' = true) | ||
| # Both writes are gated on both values being present. | ||
| KIROCREW_TG_BOT_TOKEN="" | ||
| KIROCREW_TG_BOT_TOKEN_SECRET="" | ||
| KIROCREW_TG_USER_ID="" | ||
|
Comment on lines
+3377
to
+3379
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When an interactive KiroCrew install opts into Telegram, the normal CloudFormation Console flow and the default CLI flow still deploy empty values because Useful? React with 👍 / 👎. |
||
| if [[ "${PACK_NAME:-}" == "kirocrew" && "$AUTO_YES" != true ]]; then | ||
| if confirm "Connect KiroCrew to Telegram? (chat with your agent from your phone)" "default_no"; then | ||
| echo "" | ||
| echo -e " ${BOLD}Two things to get from Telegram before continuing:${NC}" | ||
| echo "" | ||
| echo -e " 1. ${BOLD}Create a bot${NC} — message ${CYAN}@BotFather${NC}, send ${BOLD}/newbot${NC}," | ||
| echo -e " and follow the prompts. You'll get a token like ${DIM}123456789:AA…${NC}" | ||
| echo "" | ||
| echo -e " 2. ${BOLD}Find your user ID${NC} — message ${CYAN}@userinfobot${NC}; it replies with" | ||
| echo -e " your number (e.g. ${DIM}123456789${NC}). That's the only account your" | ||
| echo -e " bot will answer." | ||
| echo "" | ||
| echo -e " ${DIM}Press Enter (empty) or type 'skip' at either prompt to skip Telegram setup.${NC}" | ||
| echo "" | ||
|
|
||
| # --- Bot token: retry loop, format ^[0-9]+:[A-Za-z0-9_-]+$ --- | ||
| _KC_TG_ATTEMPTS=0 | ||
| _KC_TG_MAX=5 | ||
| while (( _KC_TG_ATTEMPTS < _KC_TG_MAX )); do | ||
| _KC_TG_ATTEMPTS=$((_KC_TG_ATTEMPTS + 1)) | ||
| _KC_TG_INPUT="" | ||
| prompt_secret "Telegram bot token" _KC_TG_INPUT "" | ||
| _KC_TG_INPUT_LC="$(printf '%s' "$_KC_TG_INPUT" | tr '[:upper:]' '[:lower:]')" | ||
| if [[ -z "$_KC_TG_INPUT" ]] || [[ "$_KC_TG_INPUT_LC" == "skip" ]]; then | ||
| KIROCREW_TG_BOT_TOKEN="" | ||
| break | ||
| fi | ||
| if [[ "$_KC_TG_INPUT" =~ ^[0-9]+:[A-Za-z0-9_-]+$ ]]; then | ||
| KIROCREW_TG_BOT_TOKEN="$_KC_TG_INPUT" | ||
| break | ||
| fi | ||
| _KC_TG_REMAINING=$((_KC_TG_MAX - _KC_TG_ATTEMPTS)) | ||
| if (( _KC_TG_REMAINING > 0 )); then | ||
| warn "Bot token doesn't match expected format (digits:alphanumerics, e.g. 123456789:AA-...). ${_KC_TG_REMAINING} attempt(s) left. Press Enter or type 'skip' to skip." | ||
| else | ||
| warn "Bot token invalid after ${_KC_TG_MAX} attempts. Skipping Telegram setup." | ||
| KIROCREW_TG_BOT_TOKEN="" | ||
| fi | ||
| done | ||
|
|
||
| # --- User ID: retry loop, all-digit (Telegram user IDs are 32-bit+ ints) --- | ||
| if [[ -n "$KIROCREW_TG_BOT_TOKEN" ]]; then | ||
| _KC_TG_ATTEMPTS=0 | ||
| while (( _KC_TG_ATTEMPTS < _KC_TG_MAX )); do | ||
| _KC_TG_ATTEMPTS=$((_KC_TG_ATTEMPTS + 1)) | ||
| _KC_TG_INPUT="" | ||
| prompt "Your Telegram user ID (numeric)" _KC_TG_INPUT "" | ||
| _KC_TG_INPUT_LC="$(printf '%s' "$_KC_TG_INPUT" | tr '[:upper:]' '[:lower:]')" | ||
| if [[ -z "$_KC_TG_INPUT" ]] || [[ "$_KC_TG_INPUT_LC" == "skip" ]]; then | ||
| KIROCREW_TG_USER_ID="" | ||
| KIROCREW_TG_BOT_TOKEN="" # neither goes without both | ||
| break | ||
| fi | ||
| if [[ "$_KC_TG_INPUT" =~ ^[0-9]{5,15}$ ]]; then | ||
| KIROCREW_TG_USER_ID="$_KC_TG_INPUT" | ||
| break | ||
| fi | ||
| _KC_TG_REMAINING=$((_KC_TG_MAX - _KC_TG_ATTEMPTS)) | ||
| if (( _KC_TG_REMAINING > 0 )); then | ||
| warn "User ID must be all digits (5-15 chars). ${_KC_TG_REMAINING} attempt(s) left. Press Enter or type 'skip' to skip." | ||
| else | ||
| warn "User ID invalid after ${_KC_TG_MAX} attempts. Skipping Telegram setup." | ||
| KIROCREW_TG_USER_ID="" | ||
| KIROCREW_TG_BOT_TOKEN="" | ||
| fi | ||
| done | ||
| fi | ||
| unset _KC_TG_ATTEMPTS _KC_TG_MAX _KC_TG_INPUT _KC_TG_INPUT_LC _KC_TG_REMAINING | ||
|
|
||
| if [[ -n "$KIROCREW_TG_BOT_TOKEN" && -n "$KIROCREW_TG_USER_ID" ]]; then | ||
| # Codex P2 (818c0f8): defer the Secrets Manager write until AFTER | ||
| # show_summary confirms the deploy — same pattern Roundhouse and | ||
| # kiro-cli already use. Here we only stash the token in a shell var | ||
| # and pre-compute the secret name so build_deploy_params can emit | ||
| # the correct CFN parameter value; the actual create-secret / | ||
| # put-secret-value call happens in the post-confirmation block | ||
| # (search for _KC_TG_SECRET_NAME below). | ||
| _KC_TG_SECRET_NAME="/lowkey/${ENV_NAME}/kirocrew-telegram-bot-token" | ||
| KIROCREW_TG_BOT_TOKEN_SECRET="$_KC_TG_SECRET_NAME" | ||
| # NOTE: KIROCREW_TG_BOT_TOKEN stays populated in-memory; it is | ||
| # written to Secrets Manager and cleared only after the operator | ||
| # confirms deployment. | ||
| ok "Telegram setup captured (secret ${KIROCREW_TG_BOT_TOKEN_SECRET} + user ID ${KIROCREW_TG_USER_ID}); token will be stored after you confirm deployment." | ||
| # Rebuild PARAM_VALUES so the KirocrewTg* fields are no longer stale | ||
| # (Codex P1 on 4c9d1fd: build_deploy_params ran before this wizard). | ||
| build_deploy_params | ||
| else | ||
| info "Skipping Telegram setup — you can enable it later by editing ~/.kiro/crew/config.json on the instance." | ||
| fi | ||
| fi | ||
| fi | ||
|
|
||
| # Pack-specific: kiro-cli/kirocrew interactive API key for headless mode | ||
| if [[ "${PACK_NAME:-}" == "kiro-cli" || "${PACK_NAME:-}" == "kirocrew" ]]; then | ||
| if [[ -z "${KIRO_FROM_SECRET:-}" && "$AUTO_YES" != true ]]; then | ||
|
|
@@ -3505,6 +3610,41 @@ main() { | |
| _telem_pack_selected 2>/dev/null || true | ||
| _telem_method_selected 2>/dev/null || true | ||
|
|
||
| # KiroCrew Telegram: save bot token to Secrets Manager (deferred until after user confirmation) | ||
| # Codex P2 on 818c0f8: the wizard used to write this before show_summary, | ||
| # which orphaned a secret if the operator cancelled at the summary or | ||
| # chose "Change settings". Matches the Roundhouse + Kiro API-key pattern. | ||
| if [[ -n "${KIROCREW_TG_BOT_TOKEN:-}" && -n "${_KC_TG_SECRET_NAME:-}" ]]; then | ||
| info "Storing KiroCrew Telegram bot token in Secrets Manager: ${_KC_TG_SECRET_NAME}" | ||
| local kc_tg_token_file | ||
| kc_tg_token_file=$(mktemp /tmp/lowkey-kc-tg-token.XXXXXX) | ||
| chmod 600 "$kc_tg_token_file" | ||
| printf '%s' "$KIROCREW_TG_BOT_TOKEN" > "$kc_tg_token_file" | ||
| # Restore if in pending-deletion state (same guard as Roundhouse). | ||
| aws secretsmanager restore-secret --secret-id "$_KC_TG_SECRET_NAME" --region "$DEPLOY_REGION" >/dev/null 2>&1 || true | ||
| local kc_tg_sm_err="" | ||
| if kc_tg_sm_err=$(aws secretsmanager create-secret \ | ||
| --name "$_KC_TG_SECRET_NAME" \ | ||
| --secret-string "file://${kc_tg_token_file}" \ | ||
| --description "KiroCrew Telegram bot token for ${ENV_NAME} (managed by lowkey install.sh)" \ | ||
| --tags "Key=loki:managed,Value=true" "Key=loki:pack,Value=kirocrew" "Key=loki:env,Value=${ENV_NAME}" \ | ||
| --region "$DEPLOY_REGION" 2>&1); then | ||
| ok "KiroCrew Telegram token saved to Secrets Manager" | ||
| elif kc_tg_sm_err=$(aws secretsmanager put-secret-value \ | ||
| --secret-id "$_KC_TG_SECRET_NAME" \ | ||
| --secret-string "file://${kc_tg_token_file}" \ | ||
| --region "$DEPLOY_REGION" 2>&1); then | ||
| ok "KiroCrew Telegram token updated in Secrets Manager" | ||
| else | ||
| rm -f "$kc_tg_token_file" | ||
| fail "Failed to save KiroCrew Telegram bot token to Secrets Manager: ${kc_tg_sm_err}" | ||
| fi | ||
| rm -f "$kc_tg_token_file" | ||
| # Scrub the plaintext token from installer state — CFN only needs the arn/id. | ||
| KIROCREW_TG_BOT_TOKEN="" | ||
| unset KIROCREW_TG_BOT_TOKEN _KC_TG_SECRET_NAME | ||
| fi | ||
|
|
||
| # Roundhouse: save bot token to Secrets Manager (deferred until after user confirmation) | ||
| if [[ -n "${_RH_BOT_TOKEN:-}" && -n "${_RH_SECRET_NAME:-}" ]]; then | ||
| info "Storing bot token in Secrets Manager: ${_RH_SECRET_NAME}" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For both inspected CloudFormation CLI and Console deployment flows, these variables exist only in the local installer process:
build_deploy_paramshas no corresponding parameters, anddeploy/cloudformation/template.yamlneither exports them in UserData nor passes them tobootstrap.sh. Consequently, the remote bootstrap expands both values to empty strings here, sopacks/kirocrew/install.shalways skips Telegram wiring even after the wizard reports that the credentials were captured.Useful? React with 👍 / 👎.