@deployfoundation/foundation-deploy 0.2.38 → 0.2.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  DEFAULT_RELEASE_BUCKET
4
- } from "../chunk-8e2z4rhf.js";
5
- import"../chunk-2ra18dqx.js";
4
+ } from "../chunk-dz91avrb.js";
6
5
 
7
6
  // bin/release-account.ts
8
7
  import * as cdk2 from "aws-cdk-lib";
@@ -17,13 +17,14 @@ import {
17
17
  parseInstanceConfig,
18
18
  parseSchedule,
19
19
  provisionsIntegration,
20
+ releaseAccess,
20
21
  releaseImageRepositoryArn,
21
22
  releaseSource,
22
23
  requiresAuthenticatedQueue,
23
24
  scheduledJobBindings,
24
25
  slackCommandPrefixes,
25
26
  webhookJobBindings
26
- } from "./chunk-2ra18dqx.js";
27
+ } from "./chunk-7rd8c1th.js";
27
28
 
28
29
  // src/stacks/agent-stack.ts
29
30
  import * as cdk from "aws-cdk-lib";
@@ -1846,6 +1847,18 @@ function deployStatements(stack, names, targets, instance) {
1846
1847
  const runtimeSecret = secretArn(names.secretRuntime);
1847
1848
  const knockOauthClient = secretArn(names.secretKnockOauthClient);
1848
1849
  return [
1850
+ new iam3.PolicyStatement({
1851
+ sid: "ReadFoundationRelease",
1852
+ actions: ["s3:GetObject"],
1853
+ resources: [`arn:${stack.partition}:s3:::${targets.release.bucket}/releases/*`]
1854
+ }),
1855
+ ...targets.release.signingKeyArn === undefined ? [] : [
1856
+ new iam3.PolicyStatement({
1857
+ sid: "VerifyReleaseSignature",
1858
+ actions: ["kms:Verify"],
1859
+ resources: [targets.release.signingKeyArn]
1860
+ })
1861
+ ],
1849
1862
  new iam3.PolicyStatement({
1850
1863
  sid: "AssumeCdkBootstrapRoles",
1851
1864
  actions: ["sts:AssumeRole"],
@@ -1927,7 +1940,8 @@ function deployStatements(stack, names, targets, instance) {
1927
1940
  "bedrock-agentcore:GetAgentRuntime",
1928
1941
  "bedrock-agentcore:ListAgentRuntimeVersions",
1929
1942
  "bedrock-agentcore:GetAgentRuntimeEndpoint",
1930
- "bedrock-agentcore:UpdateAgentRuntimeEndpoint"
1943
+ "bedrock-agentcore:UpdateAgentRuntimeEndpoint",
1944
+ "bedrock-agentcore:CreateAgentRuntimeEndpoint"
1931
1945
  ],
1932
1946
  resources: [
1933
1947
  `arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:runtime/${names.runtimeName}*`,
@@ -1970,7 +1984,7 @@ class FoundationCi extends cdk3.Stack {
1970
1984
  }),
1971
1985
  inlinePolicies: {
1972
1986
  FoundationDeploy: new iam4.PolicyDocument({
1973
- statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey }, props.instance)
1987
+ statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) }, props.instance)
1974
1988
  })
1975
1989
  }
1976
1990
  });
@@ -3453,7 +3467,7 @@ class FoundationPipeline extends cdk8.Stack {
3453
3467
  assumedBy: new iam9.ServicePrincipal("codebuild.amazonaws.com"),
3454
3468
  inlinePolicies: {
3455
3469
  FoundationDeploy: new iam9.PolicyDocument({
3456
- statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey }, props.instance)
3470
+ statements: deployStatements(this, names, { bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) }, props.instance)
3457
3471
  })
3458
3472
  }
3459
3473
  });
@@ -1,3 +1,8 @@
1
+ import {
2
+ DEFAULT_RELEASE_BUCKET,
3
+ configuredKeyArn
4
+ } from "./chunk-dz91avrb.js";
5
+
1
6
  // ../core/src/instance.ts
2
7
  import { parse as parseYaml } from "yaml";
3
8
  import { z as z2 } from "zod";
@@ -1816,6 +1821,14 @@ function releaseSource(scope) {
1816
1821
  throw new Error(`-c releaseBucket=<name> is required when deploying a release (or set ${RELEASE_BUCKET_ENV}); it is the bucket holding ${manifest.lambda.gateway?.key ?? "the release artifacts"}`);
1817
1822
  return { version: manifest.version, bucket, manifest };
1818
1823
  }
1824
+ function releaseAccess(scope, env = process.env) {
1825
+ const context = scope.node.tryGetContext("releaseBucket");
1826
+ const fromContext = typeof context === "string" && context !== "" ? context : undefined;
1827
+ const fromEnv = env[RELEASE_BUCKET_ENV];
1828
+ const bucket = fromContext ?? (fromEnv !== undefined && fromEnv !== "" ? fromEnv : DEFAULT_RELEASE_BUCKET);
1829
+ const signingKeyArn = configuredKeyArn(env);
1830
+ return { bucket, ...signingKeyArn === undefined ? {} : { signingKeyArn } };
1831
+ }
1819
1832
  function releaseBucket(scope, source) {
1820
1833
  const stack = cdk.Stack.of(scope);
1821
1834
  const existing = stack.node.tryFindChild("FoundationReleaseBucket");
@@ -1899,4 +1912,4 @@ function ecrRepositoryArn(repositoryUri) {
1899
1912
  return `arn:aws:ecr:${region}:${account}:repository/${name}`;
1900
1913
  }
1901
1914
 
1902
- export { parseSchedule, BASE_SLACK_EVENTS, CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, encodeProactiveChannels, CUSTOMIZATION_ARTIFACT_NAME, newsletterManifestFor, FOUNDATION_VERSION_RE, versionedConfigKey, instanceNames, slackCommandPrefix, slackCommandPrefixes, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, capabilityScheduledJobs, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, loadInstanceFile, configPathFor, capabilityEnabled, requiresAuthenticatedQueue, enabledIntegrations, provisionsIntegration, adminsFor, WEB_SEARCH_TARGET, WEB_SEARCH_CONNECTOR_VERSION, LIVE_ENDPOINT_NAME, RELEASE_VERSION_RE, parseManifest, manifestKey, lambdaKey, skillsKey, buildManifest, manifestSigningPayload, verifyManifest, lambdaBundleContext, BUN_VERSION, LAMBDA_ENTRY_POINTS, skipBundle, RELEASE_BUCKET_ENV, RELEASE_DIR_ENV, releaseCacheDir, releaseSource, lambdaCode, agentImage, agentImageTagRequired, releaseImageRepositoryArn, ecrRepositoryArn };
1915
+ export { parseSchedule, BASE_SLACK_EVENTS, CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, encodeProactiveChannels, CUSTOMIZATION_ARTIFACT_NAME, newsletterManifestFor, FOUNDATION_VERSION_RE, versionedConfigKey, instanceNames, slackCommandPrefix, slackCommandPrefixes, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, capabilityScheduledJobs, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, loadInstanceFile, configPathFor, capabilityEnabled, requiresAuthenticatedQueue, enabledIntegrations, provisionsIntegration, adminsFor, WEB_SEARCH_TARGET, WEB_SEARCH_CONNECTOR_VERSION, LIVE_ENDPOINT_NAME, RELEASE_VERSION_RE, parseManifest, manifestKey, lambdaKey, skillsKey, buildManifest, manifestSigningPayload, verifyManifest, lambdaBundleContext, BUN_VERSION, LAMBDA_ENTRY_POINTS, skipBundle, RELEASE_BUCKET_ENV, RELEASE_DIR_ENV, releaseCacheDir, releaseSource, releaseAccess, lambdaCode, agentImage, agentImageTagRequired, releaseImageRepositoryArn, ecrRepositoryArn };
@@ -0,0 +1,16 @@
1
+ // src/release/trust.ts
2
+ var DEFAULT_RELEASE_BUCKET = "foundry41-foundation-releases";
3
+ var RELEASE_KMS_KEY_ARN_ENV = "FOUNDATION_RELEASE_KMS_KEY_ARN";
4
+ var RELEASE_KMS_KEY_ARN = "arn:aws:kms:us-east-1:793593623536:key/fba7d9f3-e9b6-49ae-b4a5-c8c5f1429231";
5
+ function configuredKeyArn(env = process.env) {
6
+ const configured = env[RELEASE_KMS_KEY_ARN_ENV] ?? RELEASE_KMS_KEY_ARN;
7
+ return configured === undefined || configured === "" ? undefined : configured;
8
+ }
9
+ function expectedKeyArn(env = process.env) {
10
+ const configured = configuredKeyArn(env);
11
+ if (configured === undefined)
12
+ throw new Error(`no Foundation release signing key is configured; set ${RELEASE_KMS_KEY_ARN_ENV} to the key ARN published with the release`);
13
+ return configured;
14
+ }
15
+
16
+ export { DEFAULT_RELEASE_BUCKET, configuredKeyArn, expectedKeyArn };
package/dist/src/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  FoundationPipeline,
11
11
  NewsletterStack,
12
12
  deployStatements
13
- } from "../chunk-j52g7bsc.js";
13
+ } from "../chunk-6v0ewr5b.js";
14
14
  import {
15
15
  BUN_VERSION,
16
16
  LAMBDA_ENTRY_POINTS,
@@ -36,6 +36,7 @@ import {
36
36
  manifestSigningPayload,
37
37
  parseManifest,
38
38
  provisionsIntegration,
39
+ releaseAccess,
39
40
  releaseCacheDir,
40
41
  releaseImageRepositoryArn,
41
42
  releaseSource,
@@ -43,7 +44,8 @@ import {
43
44
  skillsKey,
44
45
  skipBundle,
45
46
  verifyManifest
46
- } from "../chunk-2ra18dqx.js";
47
+ } from "../chunk-7rd8c1th.js";
48
+ import"../chunk-dz91avrb.js";
47
49
  export {
48
50
  BUILD_TIMEOUT,
49
51
  BUN_VERSION,
@@ -80,6 +82,7 @@ export {
80
82
  instanceNames as namesFor,
81
83
  parseManifest,
82
84
  provisionsIntegration,
85
+ releaseAccess,
83
86
  releaseCacheDir,
84
87
  releaseImageRepositoryArn,
85
88
  releaseSource,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deployfoundation/foundation-deploy",
3
- "version": "0.2.38",
3
+ "version": "0.2.39",
4
4
  "description": "Deploys one Foundation instance from a published, signed release: the CDK app and the deploy tool.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
package/src/artifacts.ts CHANGED
@@ -41,6 +41,7 @@ import * as s3 from "aws-cdk-lib/aws-s3";
41
41
  import type { Construct } from "constructs";
42
42
  import { lambdaBundleContext } from "./lambda-bundle-context.ts";
43
43
  import { type ReleaseManifest, parseManifest } from "./release/manifest.ts";
44
+ import { DEFAULT_RELEASE_BUCKET, configuredKeyArn } from "./release/trust.ts";
44
45
 
45
46
  /** Pinned Bun used to bundle every Lambda (and the docker fallback image tag). */
46
47
  export const BUN_VERSION = "1.4.0";
@@ -174,6 +175,38 @@ export function releaseSource(scope: Construct): ReleaseSource | undefined {
174
175
  return { version: manifest.version, bucket, manifest };
175
176
  }
176
177
 
178
+ /**
179
+ * What a deployer of this app reads from Foundation's release account: the
180
+ * bucket the tool fetches `releases/*` from and the key whose signature it
181
+ * checks. The deployer roles (`deploy-permissions.ts`) are granted exactly
182
+ * these, cross-account, so a CodePipeline or GitHub Actions deploy can verify
183
+ * a release the way an admin's laptop can.
184
+ *
185
+ * Resolved the way `foundation-deploy` resolves them — `-c releaseBucket`
186
+ * (which the tool passes on a release deploy) or `FOUNDATION_RELEASE_BUCKET`,
187
+ * else the default bucket; `FOUNDATION_RELEASE_KMS_KEY_ARN`, else the pinned
188
+ * key — and resolved on EVERY synth, release or local build, so the grant does
189
+ * not come and go with how the last deploy was run.
190
+ */
191
+ export interface ReleaseAccess {
192
+ bucket: string;
193
+ /** Absent only when no signing key is configured at all; nothing is verified then. */
194
+ signingKeyArn?: string;
195
+ }
196
+
197
+ export function releaseAccess(
198
+ scope: Construct,
199
+ env: Record<string, string | undefined> = process.env,
200
+ ): ReleaseAccess {
201
+ const context: unknown = scope.node.tryGetContext("releaseBucket");
202
+ const fromContext = typeof context === "string" && context !== "" ? context : undefined;
203
+ const fromEnv = env[RELEASE_BUCKET_ENV];
204
+ const bucket =
205
+ fromContext ?? (fromEnv !== undefined && fromEnv !== "" ? fromEnv : DEFAULT_RELEASE_BUCKET);
206
+ const signingKeyArn = configuredKeyArn(env);
207
+ return { bucket, ...(signingKeyArn === undefined ? {} : { signingKeyArn }) };
208
+ }
209
+
177
210
  /**
178
211
  * The release bucket as one imported construct per stack. Every Lambda in a
179
212
  * stack references the same bucket, and importing it once keeps the template's
@@ -29,34 +29,10 @@ import {
29
29
  manifestKey,
30
30
  verifyManifest,
31
31
  } from "../release/manifest.ts";
32
+ import { DEFAULT_RELEASE_BUCKET, expectedKeyArn } from "../release/trust.ts";
32
33
  import { type AwsCallContext, aws } from "./aws.ts";
33
34
  import { FOUNDATION_ROOT, INFRA_ROOT } from "./paths.ts";
34
35
 
35
- /**
36
- * The bucket a release is fetched from when nothing says otherwise.
37
- *
38
- * One name, owned by Foundry 41, created by `release-account-stack.ts`. A
39
- * mirror is `--release-bucket` or `FOUNDATION_RELEASE_BUCKET`, and the
40
- * signature is what makes a mirror safe to use.
41
- */
42
- export const DEFAULT_RELEASE_BUCKET = "foundry41-foundation-releases";
43
-
44
- /** The env var that names the key a deployer will accept a manifest from. */
45
- export const RELEASE_KMS_KEY_ARN_ENV = "FOUNDATION_RELEASE_KMS_KEY_ARN";
46
-
47
- /**
48
- * The Foundation release signing key, in the Foundry 41 release account
49
- * (793593623536), created by `ReleaseAccountStack` on 2026-09-20.
50
- *
51
- * This constant is what makes signature verification mean anything: a
52
- * deployer refuses a manifest signed by any other key. Changing it rotates
53
- * the root of trust for every customer, so it changes only with a key
54
- * rotation and a release that says so. {@link RELEASE_KMS_KEY_ARN_ENV}
55
- * overrides it, for a private mirror or a test key.
56
- */
57
- export const RELEASE_KMS_KEY_ARN: string | undefined =
58
- "arn:aws:kms:us-east-1:793593623536:key/fba7d9f3-e9b6-49ae-b4a5-c8c5f1429231";
59
-
60
36
  /** What the caller asked for, before anything has been fetched. */
61
37
  export interface ReleaseRequest {
62
38
  /** `vX.Y.Z`, when the caller named a version or one was implied. */
@@ -149,16 +125,6 @@ export function releaseRequest(
149
125
  return { version, bucket };
150
126
  }
151
127
 
152
- /** The key ARN a deployer will accept a manifest from. */
153
- export function expectedKeyArn(env: Record<string, string | undefined> = process.env): string {
154
- const configured = env[RELEASE_KMS_KEY_ARN_ENV] ?? RELEASE_KMS_KEY_ARN;
155
- if (configured === undefined || configured === "")
156
- throw new Error(
157
- `no Foundation release signing key is configured; set ${RELEASE_KMS_KEY_ARN_ENV} to the key ARN published with the release`,
158
- );
159
- return configured;
160
- }
161
-
162
128
  export interface ResolveReleaseDeps {
163
129
  /** Defaults to the SDK-backed verifier for the expected key. */
164
130
  kms?: KmsVerifier;
@@ -17,6 +17,7 @@ import type * as cdk from "aws-cdk-lib";
17
17
  import * as iam from "aws-cdk-lib/aws-iam";
18
18
  import type * as kms from "aws-cdk-lib/aws-kms";
19
19
  import type * as s3 from "aws-cdk-lib/aws-s3";
20
+ import type { ReleaseAccess } from "./artifacts.ts";
20
21
  import { type Instance, type InstanceNames, provisionsIntegration } from "./names.ts";
21
22
 
22
23
  export interface DeployTargets {
@@ -24,6 +25,13 @@ export interface DeployTargets {
24
25
  bucket: s3.IBucket;
25
26
  /** The CMK that bucket is encrypted with. */
26
27
  dataKey: kms.IKey;
28
+ /**
29
+ * Foundation's release bucket and signing key, in the release account
30
+ * (`releaseAccess()` in `artifacts.ts`). Cross-account access needs BOTH
31
+ * sides to allow it: the release account's bucket and key policies name the
32
+ * customer account, and the statements below are this account's half.
33
+ */
34
+ release: ReleaseAccess;
27
35
  }
28
36
 
29
37
  /**
@@ -49,6 +57,30 @@ export function deployStatements(
49
57
  const knockOauthClient = secretArn(names.secretKnockOauthClient);
50
58
 
51
59
  return [
60
+ // A release deploy, before anything else: `aws s3 cp` of
61
+ // `releases/<v>/manifest.json`, then of every Lambda zip and
62
+ // `skills.tar.gz` the manifest names (the digest check in `verifyRelease`),
63
+ // and of `skills.tar.gz` again for the config sync (`unpackReleaseSkills`).
64
+ // HeadObject is authorized by s3:GetObject. The bucket is SSE-S3, so there
65
+ // is no key to grant for it.
66
+ new iam.PolicyStatement({
67
+ sid: "ReadFoundationRelease",
68
+ actions: ["s3:GetObject"],
69
+ resources: [`arn:${stack.partition}:s3:::${targets.release.bucket}/releases/*`],
70
+ }),
71
+ // ...and the manifest's signature, checked with one KMS `Verify` against
72
+ // the pinned key (`src/release/kms.ts`); nothing calls GetPublicKey or
73
+ // DescribeKey. By key ARN, not alias: an identity policy cannot name
74
+ // another account's key by its alias.
75
+ ...(targets.release.signingKeyArn === undefined
76
+ ? []
77
+ : [
78
+ new iam.PolicyStatement({
79
+ sid: "VerifyReleaseSignature",
80
+ actions: ["kms:Verify"],
81
+ resources: [targets.release.signingKeyArn],
82
+ }),
83
+ ]),
52
84
  // How `cdk deploy` does everything else: it assumes the bootstrap roles for
53
85
  // lookup, asset publishing and the CloudFormation call.
54
86
  new iam.PolicyStatement({
@@ -140,8 +172,10 @@ export function deployStatements(
140
172
  actions: ["kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey*"],
141
173
  resources: [targets.dataKey.keyArn],
142
174
  }),
143
- // `foundation-deploy post-deploy`: the smoke test (`_smoke_test`,
144
- // `_fs_probe`) against DEFAULT, then promotion of the new version onto `live`.
175
+ // `foundation-deploy post-deploy` (and `deploy`'s inline promotion): the
176
+ // smoke test (`_smoke_test`, `_fs_probe`) against DEFAULT, then promotion
177
+ // of the new version onto `live`, which CREATES `live` when an instance
178
+ // brought up by `deploy` alone has none yet (`promoteEndpoint`).
145
179
  new iam.PolicyStatement({
146
180
  sid: "SmokeTestRuntime",
147
181
  actions: [
@@ -150,6 +184,7 @@ export function deployStatements(
150
184
  "bedrock-agentcore:ListAgentRuntimeVersions",
151
185
  "bedrock-agentcore:GetAgentRuntimeEndpoint",
152
186
  "bedrock-agentcore:UpdateAgentRuntimeEndpoint",
187
+ "bedrock-agentcore:CreateAgentRuntimeEndpoint",
153
188
  ],
154
189
  resources: [
155
190
  `arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:runtime/${names.runtimeName}*`,
package/src/index.ts CHANGED
@@ -4,11 +4,13 @@ export {
4
4
  RELEASE_BUCKET_ENV,
5
5
  RELEASE_DIR_ENV,
6
6
  type LambdaArtifactId,
7
+ type ReleaseAccess,
7
8
  type ReleaseSource,
8
9
  agentImage,
9
10
  agentImageTagRequired,
10
11
  ecrRepositoryArn,
11
12
  lambdaCode,
13
+ releaseAccess,
12
14
  releaseCacheDir,
13
15
  releaseImageRepositoryArn,
14
16
  releaseSource,
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Where a deployer gets a release from, and whose signature it accepts.
3
+ *
4
+ * Pure, like `manifest.ts`: the deploy tool reads these to fetch and verify a
5
+ * release, and the CDK app reads them to give the deployer roles
6
+ * (`deploy-permissions.ts`) the release-account permissions that fetch needs.
7
+ * Both must resolve to the same bucket and the same key, so the resolution
8
+ * lives here once rather than in each.
9
+ */
10
+
11
+ /**
12
+ * The bucket a release is fetched from when nothing says otherwise.
13
+ *
14
+ * One name, owned by Foundry 41, created by `release-account-stack.ts`. A
15
+ * mirror is `--release-bucket` or `FOUNDATION_RELEASE_BUCKET`, and the
16
+ * signature is what makes a mirror safe to use.
17
+ */
18
+ export const DEFAULT_RELEASE_BUCKET = "foundry41-foundation-releases";
19
+
20
+ /** The env var that names the key a deployer will accept a manifest from. */
21
+ export const RELEASE_KMS_KEY_ARN_ENV = "FOUNDATION_RELEASE_KMS_KEY_ARN";
22
+
23
+ /**
24
+ * The Foundation release signing key, in the Foundry 41 release account
25
+ * (793593623536), created by `ReleaseAccountStack` on 2026-09-20.
26
+ *
27
+ * This constant is what makes signature verification mean anything: a
28
+ * deployer refuses a manifest signed by any other key. Changing it rotates
29
+ * the root of trust for every customer, so it changes only with a key
30
+ * rotation and a release that says so. {@link RELEASE_KMS_KEY_ARN_ENV}
31
+ * overrides it, for a private mirror or a test key.
32
+ */
33
+ export const RELEASE_KMS_KEY_ARN: string | undefined =
34
+ "arn:aws:kms:us-east-1:793593623536:key/fba7d9f3-e9b6-49ae-b4a5-c8c5f1429231";
35
+
36
+ /** The key ARN a deployer will accept a manifest from, or `undefined` when none is configured. */
37
+ export function configuredKeyArn(
38
+ env: Record<string, string | undefined> = process.env,
39
+ ): string | undefined {
40
+ const configured = env[RELEASE_KMS_KEY_ARN_ENV] ?? RELEASE_KMS_KEY_ARN;
41
+ return configured === undefined || configured === "" ? undefined : configured;
42
+ }
43
+
44
+ /** The key ARN a deployer will accept a manifest from. Throws when none is configured. */
45
+ export function expectedKeyArn(env: Record<string, string | undefined> = process.env): string {
46
+ const configured = configuredKeyArn(env);
47
+ if (configured === undefined)
48
+ throw new Error(
49
+ `no Foundation release signing key is configured; set ${RELEASE_KMS_KEY_ARN_ENV} to the key ARN published with the release`,
50
+ );
51
+ return configured;
52
+ }
@@ -3,6 +3,7 @@ import * as iam from "aws-cdk-lib/aws-iam";
3
3
  import type * as kms from "aws-cdk-lib/aws-kms";
4
4
  import type * as s3 from "aws-cdk-lib/aws-s3";
5
5
  import type { Construct } from "constructs";
6
+ import { releaseAccess } from "../artifacts.ts";
6
7
  import { deployStatements } from "../deploy-permissions.ts";
7
8
  import { type Instance, namesFor } from "../names.ts";
8
9
 
@@ -84,7 +85,7 @@ export class FoundationCi extends cdk.Stack {
84
85
  statements: deployStatements(
85
86
  this,
86
87
  names,
87
- { bucket: props.bucket, dataKey: props.dataKey },
88
+ { bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) },
88
89
  props.instance,
89
90
  ),
90
91
  }),
@@ -40,6 +40,7 @@ import type * as s3 from "aws-cdk-lib/aws-s3";
40
40
  import * as sns from "aws-cdk-lib/aws-sns";
41
41
  import * as subscriptions from "aws-cdk-lib/aws-sns-subscriptions";
42
42
  import type { Construct } from "constructs";
43
+ import { releaseAccess } from "../artifacts.ts";
43
44
  import { deployStatements } from "../deploy-permissions.ts";
44
45
  import { type Instance, namesFor } from "../names.ts";
45
46
 
@@ -101,7 +102,7 @@ export class FoundationPipeline extends cdk.Stack {
101
102
  statements: deployStatements(
102
103
  this,
103
104
  names,
104
- { bucket: props.bucket, dataKey: props.dataKey },
105
+ { bucket: props.bucket, dataKey: props.dataKey, release: releaseAccess(this) },
105
106
  props.instance,
106
107
  ),
107
108
  }),