# Nova AWS key rotation — platform-managed scheduled pipeline (SPEC §5.9) # # Rotates the NOVA_AWS_* static key daily (no long-lived keys in the steady # state). v0.2 scope: the mechanism must exist (SPEC §5.9); the v0.2 deploy # uses the currently-active key. The rotation is best-effort + idempotent # (scripts/rotate_spike_key.sh deactivates the old key only after the new # key propagates to the consumer's Actions secret store). # # Auth: the rotation uses the CURRENT NOVA_AWS_* key to authenticate to IAM # (the root account 581513795199 can rotate its own keys — confirmed by the # bootstrap). The aws-actions/configure-aws-credentials@v4 step uses the # static-key path (no OIDC role-to-assume); the long-lived key rotates # itself, which is the bootstrap-exception documented in §5.9. # # Forge coords (base URL / owner / consumer repo) are sourced from # repository secrets — NOVA_FORGE_BASE_URL, NOVA_FORGE_OWNER, # NOVA_CONSUMER_REPO — so the synced workflow file stays forge-agnostic # (REQ-230). The rotation script uploads the new key to the consumer's # Actions secret store (the consumer whose deploy.yml consumes NOVA_AWS_* # via secrets: inherit). name: nova-rotate-aws-key on: schedule: - cron: "0 0 * * *" # daily at 00:00 UTC workflow_dispatch: permissions: id-token: write contents: read jobs: rotate: name: Rotate NOVA_AWS_* static key runs-on: ubuntu-latest steps: - name: Check out Nova platform repo uses: actions/checkout@v4 - name: Configure AWS credentials (bootstrap root creds for IAM key rotation) uses: aws-actions/configure-aws-credentials@v4 with: aws-region: ${{ secrets.AWS_DEFAULT_REGION || 'us-east-1' }} access-key-id: ${{ secrets.NOVA_AWS_ACCESS_KEY_ID }} secret-access-key: ${{ secrets.NOVA_AWS_SECRET_ACCESS_KEY }} - name: Install Python deps (boto3 for the rotation script) run: | python3 -m pip install --break-system-packages --quiet boto3 - name: Run the key rotation script env: # aws-actions/configure-aws-credentials exports AWS_ACCESS_KEY_ID / # AWS_SECRET_ACCESS_KEY; the rotation script reads the bootstrap # creds via NOVA_BOOTSTRAP_AWS_* (its dual-read contract, D-034). # Map the standard AWS_* exports onto the script's expected vars. NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }} NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }} # Forge + consumer coords come from repository secrets (REQ-230 — # no forge hostnames/orgs hardcoded in the synced workflow file). # NOVA_FORGE_TOKEN holds the forge API token (set equal to the # existing forge token as a one-time secret setup). NOVA_FORGE_TOKEN: ${{ secrets.NOVA_FORGE_TOKEN }} NOVA_FORGE_BASE_URL: ${{ secrets.NOVA_FORGE_BASE_URL }} NOVA_FORGE_OWNER: ${{ secrets.NOVA_FORGE_OWNER }} NOVA_CONSUMER_REPO: ${{ secrets.NOVA_CONSUMER_REPO }} AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION || 'us-east-1' }} run: | bash scripts/rotate_spike_key.sh